Browse Source

update server sync

Luke Pulverenti 10 years ago
parent
commit
d9518be3ed

+ 1 - 1
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -154,7 +154,7 @@ namespace MediaBrowser.Api.Playback.Hls
                             throw;
                         }
 
-                        await WaitForMinimumSegmentCount(playlistPath, 2, cancellationTokenSource.Token).ConfigureAwait(false);
+                        await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
                     }
                 }
             }

+ 34 - 0
MediaBrowser.Api/Sync/SyncService.cs

@@ -94,6 +94,9 @@ namespace MediaBrowser.Api.Sync
         [ApiMember(Name = "ParentId", Description = "ParentId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string ParentId { get; set; }
 
+        [ApiMember(Name = "TargetId", Description = "TargetId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string TargetId { get; set; }
+
         [ApiMember(Name = "Category", Description = "Category", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public SyncCategory? Category { get; set; }
     }
@@ -226,6 +229,13 @@ namespace MediaBrowser.Api.Sync
             result.Targets = _syncManager.GetSyncTargets(request.UserId)
                 .ToList();
 
+            if (!string.IsNullOrWhiteSpace(request.TargetId))
+            {
+                result.Targets = result.Targets
+                    .Where(i => string.Equals(i.Id, request.TargetId, StringComparison.OrdinalIgnoreCase))
+                    .ToList();
+            }
+
             if (request.Category.HasValue)
             {
                 result.Options = SyncHelper.GetSyncOptions(request.Category.Value);
@@ -254,6 +264,30 @@ namespace MediaBrowser.Api.Sync
                 result.Options = SyncHelper.GetSyncOptions(dtos);
             }
 
+            result.QualityOptions = new List<SyncQualityOption>
+            {
+                new SyncQualityOption
+                {
+                    Name = SyncQuality.Original.ToString(),
+                    Id = SyncQuality.Original.ToString()
+                },
+                new SyncQualityOption
+                {
+                    Name = SyncQuality.High.ToString(),
+                    Id = SyncQuality.High.ToString()
+                },
+                new SyncQualityOption
+                {
+                    Name = SyncQuality.Medium.ToString(),
+                    Id = SyncQuality.Medium.ToString()
+                },
+                new SyncQualityOption
+                {
+                    Name = SyncQuality.Low.ToString(),
+                    Id = SyncQuality.Low.ToString()
+                }
+            };
+
             return ToOptimizedResult(result);
         }
 

+ 3 - 9
MediaBrowser.Controller/Sync/IServerSyncProvider.cs

@@ -12,13 +12,13 @@ namespace MediaBrowser.Controller.Sync
         /// <summary>
         /// Transfers the file.
         /// </summary>
-        /// <param name="inputFile">The input file.</param>
-        /// <param name="path">The path.</param>
+        /// <param name="stream">The stream.</param>
+        /// <param name="remotePath">The remote path.</param>
         /// <param name="target">The target.</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task SendFile(string inputFile, string path, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
+        Task SendFile(Stream stream, string remotePath, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Deletes the file.
@@ -62,11 +62,5 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="target">The target.</param>
         /// <returns>Task&lt;List&lt;DeviceFileInfo&gt;&gt;.</returns>
         Task<List<DeviceFileInfo>> GetFileSystemEntries(string path, SyncTarget target);
-
-        /// <summary>
-        /// Gets the data provider.
-        /// </summary>
-        /// <returns>ISyncDataProvider.</returns>
-        ISyncDataProvider GetDataProvider();
     }
 }

+ 5 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -25,6 +25,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IUserManager _userManager;
         private readonly ILocalizationManager _localization;
         private readonly IChannelManager _channelManager;
+        private readonly IMediaSourceManager _mediaSourceManager;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -33,7 +34,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager)
+            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -44,6 +45,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _userManager = userManager;
             _localization = localization;
             _channelManager = channelManager;
+            _mediaSourceManager = mediaSourceManager;
         }
 
         private int SystemUpdateId
@@ -83,7 +85,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 SystemUpdateId,
                 _config,
                 _localization,
-                _channelManager)
+                _channelManager,
+                _mediaSourceManager)
                 .ProcessControlRequest(request);
         }
 

+ 2 - 3
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -46,9 +46,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly DidlBuilder _didlBuilder;
 
         private readonly DeviceProfile _profile;
-        private readonly IMediaSourceManager _mediaSourceManager;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
@@ -59,7 +58,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _profile = profile;
             _config = config;
 
-            _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, _mediaSourceManager);
+            _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager);
         }
 
         protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 {
     public class AudioEncoder : BaseEncoder
     {
-        public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder)
+        public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager)
         {
         }
 

+ 4 - 2
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -36,6 +36,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         protected readonly IChannelManager ChannelManager;
         protected readonly ISessionManager SessionManager;
         protected readonly ISubtitleEncoder SubtitleEncoder;
+        protected readonly IMediaSourceManager MediaSourceManager;
 
         protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
@@ -47,7 +48,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             IIsoManager isoManager,
             ILibraryManager libraryManager,
             IChannelManager channelManager,
-            ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder)
+            ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager)
         {
             MediaEncoder = mediaEncoder;
             Logger = logger;
@@ -59,13 +60,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
             ChannelManager = channelManager;
             SessionManager = sessionManager;
             SubtitleEncoder = subtitleEncoder;
+            MediaSourceManager = mediaSourceManager;
         }
 
         public async Task<EncodingJob> Start(EncodingJobOptions options,
             IProgress<double> progress,
             CancellationToken cancellationToken)
         {
-            var encodingJob = await new EncodingJobFactory(Logger, LiveTvManager, LibraryManager, ChannelManager)
+            var encodingJob = await new EncodingJobFactory(Logger, LiveTvManager, LibraryManager, ChannelManager, MediaSourceManager)
                 .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
 
             encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);

+ 3 - 3
MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs

@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.MediaInfo;
@@ -23,16 +22,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly ILiveTvManager _liveTvManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IChannelManager _channelManager;
-        private IMediaSourceManager _mediaSourceManager;
+        private readonly IMediaSourceManager _mediaSourceManager;
 
         protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         
-        public EncodingJobFactory(ILogger logger, ILiveTvManager liveTvManager, ILibraryManager libraryManager, IChannelManager channelManager)
+        public EncodingJobFactory(ILogger logger, ILiveTvManager liveTvManager, ILibraryManager libraryManager, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
         {
             _logger = logger;
             _liveTvManager = liveTvManager;
             _libraryManager = libraryManager;
             _channelManager = channelManager;
+            _mediaSourceManager = mediaSourceManager;
         }
 
         public async Task<EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken)

+ 7 - 3
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -70,8 +70,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
         protected readonly IChannelManager ChannelManager;
         protected readonly ISessionManager SessionManager;
         protected readonly Func<ISubtitleEncoder> SubtitleEncoder;
+        protected readonly Func<IMediaSourceManager> MediaSourceManager;
 
-        public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder)
+        public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, string version, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager)
         {
             _logger = logger;
             _jsonSerializer = jsonSerializer;
@@ -84,6 +85,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             ChannelManager = channelManager;
             SessionManager = sessionManager;
             SubtitleEncoder = subtitleEncoder;
+            MediaSourceManager = mediaSourceManager;
             FFProbePath = ffProbePath;
             FFMpegPath = ffMpegPath;
         }
@@ -580,7 +582,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 LibraryManager,
                 ChannelManager,
                 SessionManager,
-                SubtitleEncoder())
+                SubtitleEncoder(),
+                MediaSourceManager())
                 .Start(options, progress, cancellationToken).ConfigureAwait(false);
 
             await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -601,7 +604,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 LibraryManager,
                 ChannelManager,
                 SessionManager,
-                SubtitleEncoder())
+                SubtitleEncoder(),
+                MediaSourceManager())
                 .Start(options, progress, cancellationToken).ConfigureAwait(false);
 
             await job.TaskCompletionSource.Task.ConfigureAwait(false);

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 {
     public class VideoEncoder : BaseEncoder
     {
-        public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder)
+        public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager)
         {
         }
 

+ 8 - 1
MediaBrowser.Model/ApiClient/ApiClientExtensions.cs

@@ -35,7 +35,14 @@ namespace MediaBrowser.Model.ApiClient
 
         public static Task<SyncDialogOptions> GetSyncOptions(this IApiClient apiClient, SyncJob job)
         {
-            return apiClient.GetSyncOptions(job.RequestedItemIds, job.UserId, job.ParentId, job.Category);
+            return apiClient.GetSyncOptions(new SyncJobRequest
+            {
+                Category = job.Category,
+                ItemIds = job.RequestedItemIds,
+                ParentId = job.ParentId,
+                TargetId = job.TargetId,
+                UserId = job.UserId
+            });
         }
     }
 }

+ 2 - 5
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -1519,11 +1519,8 @@ namespace MediaBrowser.Model.ApiClient
         /// <summary>
         /// Gets the synchronize options.
         /// </summary>
-        /// <param name="userId">The user identifier.</param>
-        /// <param name="itemIds">The item ids.</param>
-        /// <param name="parentId">The parent identifier.</param>
-        /// <param name="category">The category.</param>
+        /// <param name="jobInfo">The job information.</param>
         /// <returns>Task&lt;SyncOptions&gt;.</returns>
-        Task<SyncDialogOptions> GetSyncOptions(IEnumerable<string> itemIds, string userId, string parentId = null, SyncCategory? category = null);
+        Task<SyncDialogOptions> GetSyncOptions(SyncJobRequest jobInfo);
     }
 }

+ 1 - 23
MediaBrowser.Model/Sync/SyncDialogOptions.cs

@@ -24,29 +24,7 @@ namespace MediaBrowser.Model.Sync
         {
             Targets = new List<SyncTarget>();
             Options = new List<SyncJobOption>();
-            QualityOptions = new List<SyncQualityOption>
-            {
-                new SyncQualityOption
-                {
-                    Name = SyncQuality.Original.ToString(),
-                    Id = SyncQuality.Original.ToString()
-                },
-                new SyncQualityOption
-                {
-                    Name = SyncQuality.High.ToString(),
-                    Id = SyncQuality.High.ToString()
-                },
-                new SyncQualityOption
-                {
-                    Name = SyncQuality.Medium.ToString(),
-                    Id = SyncQuality.Medium.ToString()
-                },
-                new SyncQualityOption
-                {
-                    Name = SyncQuality.Low.ToString(),
-                    Id = SyncQuality.Low.ToString()
-                }
-            };
+            QualityOptions = new List<SyncQualityOption>();
         }
     }
 }

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

@@ -316,6 +316,7 @@
     <Compile Include="Sync\SyncManager.cs" />
     <Compile Include="Sync\SyncRepository.cs" />
     <Compile Include="Sync\SyncConvertScheduledTask.cs" />
+    <Compile Include="Sync\TargetDataProvider.cs" />
     <Compile Include="Themes\AppThemeManager.cs" />
     <Compile Include="TV\TVSeriesManager.cs" />
     <Compile Include="Udp\UdpMessageReceivedEventArgs.cs" />

+ 5 - 2
MediaBrowser.Server.Implementations/Sync/MediaSync.cs

@@ -206,9 +206,12 @@ namespace MediaBrowser.Server.Implementations.Sync
             await dataProvider.Delete(target, localId).ConfigureAwait(false);
         }
 
-        private Task SendFile(IServerSyncProvider provider, string inputPath, LocalItem item, SyncTarget target, CancellationToken cancellationToken)
+        private async Task SendFile(IServerSyncProvider provider, string inputPath, LocalItem item, SyncTarget target, CancellationToken cancellationToken)
         {
-            return provider.SendFile(inputPath, item.LocalPath, target, new Progress<double>(), cancellationToken);
+            using (var stream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+            {
+                await provider.SendFile(stream, item.LocalPath, target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+            }
         }
 
         private string GetLocalId(string serverId, string itemId)

+ 5 - 3
MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs

@@ -14,12 +14,12 @@ namespace MediaBrowser.Server.Implementations.Sync
 {
     public class MultiProviderSync
     {
-        private readonly ISyncManager _syncManager;
+        private readonly SyncManager _syncManager;
         private readonly IServerApplicationHost _appHost;
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
 
-        public MultiProviderSync(ISyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem)
+        public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem)
         {
             _syncManager = syncManager;
             _appHost = appHost;
@@ -54,8 +54,10 @@ namespace MediaBrowser.Server.Implementations.Sync
                     progress.Report(totalProgress);
                 });
 
+                var dataProvider = _syncManager.GetDataProvider(target.Item1, target.Item2);
+
                 await new MediaSync(_logger, _syncManager, _appHost, _fileSystem)
-                    .Sync(target.Item1, target.Item1.GetDataProvider(), target.Item2, innerProgress, cancellationToken)
+                    .Sync(target.Item1, dataProvider, target.Item2, innerProgress, cancellationToken)
                     .ConfigureAwait(false);
 
                 numComplete++;

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

@@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
         public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            return new MultiProviderSync(_syncManager, _appHost, _logger, _fileSystem)
+            return new MultiProviderSync((SyncManager)_syncManager, _appHost, _logger, _fileSystem)
                 .Sync(ServerSyncProviders, progress, cancellationToken);
         }
 

+ 13 - 1
MediaBrowser.Server.Implementations/Sync/SyncManager.cs

@@ -21,10 +21,12 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Sync;
 using MediaBrowser.Model.Users;
 using MoreLinq;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -49,6 +51,7 @@ namespace MediaBrowser.Server.Implementations.Sync
         private readonly IConfigurationManager _config;
         private readonly IUserDataManager _userDataManager;
         private readonly Func<IMediaSourceManager> _mediaSourceManager;
+        private readonly IJsonSerializer _json;
 
         private ISyncProvider[] _providers = { };
 
@@ -58,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Sync
         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, IUserDataManager userDataManager, Func<IMediaSourceManager> mediaSourceManager)
+        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, IUserDataManager userDataManager, Func<IMediaSourceManager> mediaSourceManager, IJsonSerializer json)
         {
             _libraryManager = libraryManager;
             _repo = repo;
@@ -74,6 +77,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             _config = config;
             _userDataManager = userDataManager;
             _mediaSourceManager = mediaSourceManager;
+            _json = json;
         }
 
         public void AddParts(IEnumerable<ISyncProvider> providers)
@@ -86,6 +90,14 @@ namespace MediaBrowser.Server.Implementations.Sync
             get { return _providers.OfType<IServerSyncProvider>(); }
         }
 
+        private readonly ConcurrentDictionary<string, ISyncDataProvider> _dataProviders =
+            new ConcurrentDictionary<string, ISyncDataProvider>(StringComparer.OrdinalIgnoreCase);
+ 
+        public ISyncDataProvider GetDataProvider(IServerSyncProvider provider, SyncTarget target)
+        {
+            return _dataProviders.GetOrAdd(target.Id, key => new TargetDataProvider(provider, target, _appHost.SystemId, _logger, _json, _fileSystem, _config.CommonApplicationPaths));
+        }
+
         public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
         {
             var processor = GetSyncJobProcessor();

+ 242 - 0
MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs

@@ -0,0 +1,242 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Sync;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+    public class TargetDataProvider : ISyncDataProvider
+    {
+        private readonly SyncTarget _target;
+        private readonly IServerSyncProvider _provider;
+
+        private readonly SemaphoreSlim _dataLock = new SemaphoreSlim(1, 1);
+        private List<LocalItem> _items;
+
+        private readonly ILogger _logger;
+        private readonly IJsonSerializer _json;
+        private readonly IFileSystem _fileSystem;
+        private readonly IApplicationPaths _appPaths;
+        private readonly string _serverId;
+
+        private readonly SemaphoreSlim _cacheFileLock = new SemaphoreSlim(1, 1);
+
+        public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, string serverId, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths)
+        {
+            _logger = logger;
+            _json = json;
+            _provider = provider;
+            _target = target;
+            _fileSystem = fileSystem;
+            _appPaths = appPaths;
+            _serverId = serverId;
+        }
+
+        private string GetCachePath()
+        {
+            return Path.Combine(_appPaths.DataPath, "sync", _target.Id.GetMD5().ToString("N") + ".json");
+        }
+
+        private string GetRemotePath()
+        {
+            var parts = new List<string>
+            {
+                _serverId,
+                "data.json"
+            };
+
+            return _provider.GetFullPath(parts, _target);
+        }
+
+        private async Task CacheData(Stream stream)
+        {
+            var cachePath = GetCachePath();
+
+            await _cacheFileLock.WaitAsync().ConfigureAwait(false);
+
+            try
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(cachePath));
+                using (var fileStream = _fileSystem.GetFileStream(cachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+                {
+                    await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error saving sync data to {0}", ex, cachePath);
+            }
+            finally
+            {
+                _cacheFileLock.Release();
+            }
+        }
+
+        private async Task EnsureData(CancellationToken cancellationToken)
+        {
+            if (_items == null)
+            {
+                try
+                {
+                    using (var stream = await _provider.GetFile(GetRemotePath(), _target, new Progress<double>(), cancellationToken))
+                    {
+                        _items = _json.DeserializeFromStream<List<LocalItem>>(stream);
+                    }
+                }
+                catch (FileNotFoundException)
+                {
+                    _items = new List<LocalItem>();
+                }
+                catch (DirectoryNotFoundException)
+                {
+                    _items = new List<LocalItem>();
+                }
+
+                using (var memoryStream = new MemoryStream())
+                {
+                    _json.SerializeToStream(_items, memoryStream);
+                    
+                    // Now cache it
+                    memoryStream.Position = 0;
+                    await CacheData(memoryStream).ConfigureAwait(false);
+                }
+            }
+        }
+
+        private async Task SaveData(CancellationToken cancellationToken)
+        {
+            using (var stream = new MemoryStream())
+            {
+                _json.SerializeToStream(_items, stream);
+
+                // Save to sync provider
+                stream.Position = 0;
+                await _provider.SendFile(stream, GetRemotePath(), _target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+
+                // Now cache it
+                stream.Position = 0;
+                await CacheData(stream).ConfigureAwait(false);
+            }
+        }
+
+        private async Task<T> GetData<T>(Func<List<LocalItem>, T> dataFactory)
+        {
+            await _dataLock.WaitAsync().ConfigureAwait(false);
+
+            try
+            {
+                await EnsureData(CancellationToken.None).ConfigureAwait(false);
+
+                return dataFactory(_items);
+            }
+            finally
+            {
+                _dataLock.Release();
+            }
+        }
+
+        private async Task UpdateData(Func<List<LocalItem>, List<LocalItem>> action)
+        {
+            await _dataLock.WaitAsync().ConfigureAwait(false);
+
+            try
+            {
+                await EnsureData(CancellationToken.None).ConfigureAwait(false);
+
+                _items = action(_items);
+
+                await SaveData(CancellationToken.None).ConfigureAwait(false);
+            }
+            finally
+            {
+                _dataLock.Release();
+            }
+        }
+
+        public Task<List<string>> GetServerItemIds(SyncTarget target, string serverId)
+        {
+            return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).Select(i => i.ItemId).ToList());
+        }
+
+        public Task AddOrUpdate(SyncTarget target, LocalItem item)
+        {
+            return UpdateData(items =>
+            {
+                var list = items.Where(i => !string.Equals(i.Id, item.Id, StringComparison.OrdinalIgnoreCase))
+                    .ToList();
+
+                list.Add(item);
+
+                return list;
+            });
+        }
+
+        public Task Delete(SyncTarget target, string id)
+        {
+            return UpdateData(items => items.Where(i => !string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)).ToList());
+        }
+
+        public Task<LocalItem> Get(SyncTarget target, string id)
+        {
+            return GetData(items => items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)));
+        }
+
+        private async Task<List<LocalItem>> GetCachedData()
+        {
+            if (_items == null)
+            {
+                await _cacheFileLock.WaitAsync().ConfigureAwait(false);
+
+                try
+                {
+                    if (_items == null)
+                    {
+                        try
+                        {
+                            _items = _json.DeserializeFromFile<List<LocalItem>>(GetCachePath());
+                        }
+                        catch (FileNotFoundException)
+                        {
+                            _items = new List<LocalItem>();
+                        }
+                        catch (DirectoryNotFoundException)
+                        {
+                            _items = new List<LocalItem>();
+                        }
+                    }
+                }
+                finally
+                {
+                    _cacheFileLock.Release();
+                }
+            }
+
+            return _items.ToList();
+        }
+
+        public async Task<List<string>> GetCachedServerItemIds(SyncTarget target, string serverId)
+        {
+            var items = await GetCachedData().ConfigureAwait(false);
+
+            return items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase))
+                    .Select(i => i.ItemId)
+                    .ToList();
+        }
+
+        public async Task<LocalItem> GetCachedItem(SyncTarget target, string id)
+        {
+            var items = await GetCachedData().ConfigureAwait(false);
+
+            return items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
+        }
+    }
+}

+ 4 - 3
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -447,7 +447,7 @@ namespace MediaBrowser.Server.Startup.Common
             TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager);
             RegisterSingleInstance(TVSeriesManager);
 
-            SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager, UserDataManager, () => MediaSourceManager);
+            SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager, UserDataManager, () => MediaSourceManager, JsonSerializer);
             RegisterSingleInstance(SyncManager);
 
             DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this, () => DeviceManager, () => MediaSourceManager);
@@ -500,7 +500,7 @@ namespace MediaBrowser.Server.Startup.Common
             UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, playlistManager, CollectionManager, ServerConfigurationManager);
             RegisterSingleInstance(UserViewManager);
 
-            var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager);
+            var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager);
             RegisterSingleInstance<IContentDirectory>(contentDirectory);
 
             var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager);
@@ -573,7 +573,8 @@ namespace MediaBrowser.Server.Startup.Common
                 LibraryManager,
                 ChannelManager,
                 SessionManager,
-                () => SubtitleEncoder);
+                () => SubtitleEncoder,
+                () => MediaSourceManager);
             RegisterSingleInstance(MediaEncoder);
         }
 

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

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.581</version>
+        <version>3.0.582</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <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>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.581" />
+            <dependency id="MediaBrowser.Common" version="3.0.582" />
             <dependency id="NLog" version="3.2.0.0" />
             <dependency id="SimpleInjector" version="2.7.0" />
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.581</version>
+        <version>3.0.582</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <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">
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.581</version>
+        <version>3.0.582</version>
         <title>MediaBrowser.Model - Signed Edition</title>
         <authors>Media Browser Team</authors>
         <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">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.581</version>
+        <version>3.0.582</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.581" />
+            <dependency id="MediaBrowser.Common" version="3.0.582" />
         </dependencies>
     </metadata>
     <files>