From d9518be3ed3923d3fd2ff4470c9dfbd7c80ad8d9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 7 Mar 2015 23:44:31 -0500 Subject: [PATCH] update server sync --- .../Playback/Hls/DynamicHlsService.cs | 2 +- MediaBrowser.Api/Sync/SyncService.cs | 34 +++ .../Sync/IServerSyncProvider.cs | 12 +- .../ContentDirectory/ContentDirectory.cs | 7 +- .../ContentDirectory/ControlHandler.cs | 5 +- .../Encoder/AudioEncoder.cs | 2 +- .../Encoder/BaseEncoder.cs | 6 +- .../Encoder/EncodingJobFactory.cs | 6 +- .../Encoder/MediaEncoder.cs | 10 +- .../Encoder/VideoEncoder.cs | 2 +- .../ApiClient/ApiClientExtensions.cs | 9 +- MediaBrowser.Model/ApiClient/IApiClient.cs | 7 +- MediaBrowser.Model/Sync/SyncDialogOptions.cs | 24 +- ...MediaBrowser.Server.Implementations.csproj | 1 + .../Sync/MediaSync.cs | 7 +- .../Sync/MultiProviderSync.cs | 8 +- .../Sync/ServerSyncScheduledTask.cs | 2 +- .../Sync/SyncManager.cs | 14 +- .../Sync/TargetDataProvider.cs | 242 ++++++++++++++++++ .../ApplicationHost.cs | 7 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 24 files changed, 349 insertions(+), 70 deletions(-) create mode 100644 MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index b99b3f77d4..35ecbd1428 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/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); } } } diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs index 3f57ca2a04..06c2dc2df3 100644 --- a/MediaBrowser.Api/Sync/SyncService.cs +++ b/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 + { + 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); } diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 775a3648d2..98ea2ce06e 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -12,13 +12,13 @@ namespace MediaBrowser.Controller.Sync /// /// Transfers the file. /// - /// The input file. - /// The path. + /// The stream. + /// The remote path. /// The target. /// The progress. /// The cancellation token. /// Task. - Task SendFile(string inputFile, string path, SyncTarget target, IProgress progress, CancellationToken cancellationToken); + Task SendFile(Stream stream, string remotePath, SyncTarget target, IProgress progress, CancellationToken cancellationToken); /// /// Deletes the file. @@ -62,11 +62,5 @@ namespace MediaBrowser.Controller.Sync /// The target. /// Task<List<DeviceFileInfo>>. Task> GetFileSystemEntries(string path, SyncTarget target); - - /// - /// Gets the data provider. - /// - /// ISyncDataProvider. - ISyncDataProvider GetDataProvider(); } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index 75f1579ac4..2ab27fde50 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs +++ b/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); } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 17a9e7dc0a..5ccea52bad 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/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> GetResult(string methodName, Headers methodParams) diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 52221d349e..a6a87a3fcb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/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) { } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index ead080c605..6ddc3487de 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/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 Start(EncodingJobOptions options, IProgress 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); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 3488551dc3..916174c4bf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/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 CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress progress, CancellationToken cancellationToken) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index b75d7bee30..7fd91bf6f8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -70,8 +70,9 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly IChannelManager ChannelManager; protected readonly ISessionManager SessionManager; protected readonly Func SubtitleEncoder; + protected readonly Func 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 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 subtitleEncoder, Func 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); diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 941649addc..efd0bd9098 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/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) { } diff --git a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs b/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs index b5bf299907..4ae4fe8223 100644 --- a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs +++ b/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs @@ -35,7 +35,14 @@ namespace MediaBrowser.Model.ApiClient public static Task 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 + }); } } } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index ca49c6c5ad..ebf3dd6bd2 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -1519,11 +1519,8 @@ namespace MediaBrowser.Model.ApiClient /// /// Gets the synchronize options. /// - /// The user identifier. - /// The item ids. - /// The parent identifier. - /// The category. + /// The job information. /// Task<SyncOptions>. - Task GetSyncOptions(IEnumerable itemIds, string userId, string parentId = null, SyncCategory? category = null); + Task GetSyncOptions(SyncJobRequest jobInfo); } } \ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncDialogOptions.cs b/MediaBrowser.Model/Sync/SyncDialogOptions.cs index 751fbbb138..080f7f2a86 100644 --- a/MediaBrowser.Model/Sync/SyncDialogOptions.cs +++ b/MediaBrowser.Model/Sync/SyncDialogOptions.cs @@ -24,29 +24,7 @@ namespace MediaBrowser.Model.Sync { Targets = new List(); Options = new List(); - QualityOptions = new List - { - 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(); } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 607ba3c41a..41f9700411 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -316,6 +316,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 70c366bf52..246a82b203 100644 --- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs +++ b/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(), cancellationToken); + using (var stream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + { + await provider.SendFile(stream, item.LocalPath, target, new Progress(), cancellationToken).ConfigureAwait(false); + } } private string GetLocalId(string serverId, string itemId) diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs index cbfa82f1d6..a8bc24c2a1 100644 --- a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs +++ b/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++; diff --git a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs index 170860dc27..33b1e13bd1 100644 --- a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Implementations.Sync public Task Execute(CancellationToken cancellationToken, IProgress progress) { - return new MultiProviderSync(_syncManager, _appHost, _logger, _fileSystem) + return new MultiProviderSync((SyncManager)_syncManager, _appHost, _logger, _fileSystem) .Sync(ServerSyncProviders, progress, cancellationToken); } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index d0d65d4379..8474cc8c51 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/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 _mediaSourceManager; + private readonly IJsonSerializer _json; private ISyncProvider[] _providers = { }; @@ -58,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Sync public event EventHandler> SyncJobItemUpdated; public event EventHandler> SyncJobItemCreated; - public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func mediaSourceManager) + public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func mediaEncoder, IFileSystem fileSystem, Func subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func 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 providers) @@ -86,6 +90,14 @@ namespace MediaBrowser.Server.Implementations.Sync get { return _providers.OfType(); } } + private readonly ConcurrentDictionary _dataProviders = + new ConcurrentDictionary(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 CreateJob(SyncJobRequest request) { var processor = GetSyncJobProcessor(); diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs new file mode 100644 index 0000000000..d068a9e4a7 --- /dev/null +++ b/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 _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 + { + _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(), cancellationToken)) + { + _items = _json.DeserializeFromStream>(stream); + } + } + catch (FileNotFoundException) + { + _items = new List(); + } + catch (DirectoryNotFoundException) + { + _items = new List(); + } + + 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(), cancellationToken).ConfigureAwait(false); + + // Now cache it + stream.Position = 0; + await CacheData(stream).ConfigureAwait(false); + } + } + + private async Task GetData(Func, 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> 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> 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 Get(SyncTarget target, string id) + { + return GetData(items => items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase))); + } + + private async Task> GetCachedData() + { + if (_items == null) + { + await _cacheFileLock.WaitAsync().ConfigureAwait(false); + + try + { + if (_items == null) + { + try + { + _items = _json.DeserializeFromFile>(GetCachePath()); + } + catch (FileNotFoundException) + { + _items = new List(); + } + catch (DirectoryNotFoundException) + { + _items = new List(); + } + } + } + finally + { + _cacheFileLock.Release(); + } + } + + return _items.ToList(); + } + + public async Task> 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 GetCachedItem(SyncTarget target, string id) + { + var items = await GetCachedData().ConfigureAwait(false); + + return items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 4eb510f189..1c8e14f9e9 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/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(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); } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 94d843174e..05c2807c0a 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.581 + 3.0.582 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 578cda06a2..8d6ecd1a1e 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.581 + 3.0.582 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index ae5e261f90..c907f9c513 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.581 + 3.0.582 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index d3de0bb79c..31064cfe0a 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.581 + 3.0.582 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - +