diff options
24 files changed, 349 insertions, 70 deletions
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index b99b3f77d..35ecbd142 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 3f57ca2a0..06c2dc2df 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<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); } diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 775a3648d..98ea2ce06 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/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<List<DeviceFileInfo>>.</returns> Task<List<DeviceFileInfo>> GetFileSystemEntries(string path, SyncTarget target); - - /// <summary> - /// Gets the data provider. - /// </summary> - /// <returns>ISyncDataProvider.</returns> - ISyncDataProvider GetDataProvider(); } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index 75f1579ac..2ab27fde5 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 17a9e7dc0..5ccea52ba 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<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams) diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index 52221d349..a6a87a3fc 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 ead080c60..6ddc3487d 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<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); diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index 3488551dc..916174c4b 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<EncodingJob> CreateJob(EncodingJobOptions options, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index b75d7bee3..7fd91bf6f 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<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); diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index 941649add..efd0bd909 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 b5bf29990..4ae4fe822 100644 --- a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs +++ b/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 + }); } } } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index ca49c6c5a..ebf3dd6bd 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/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<SyncOptions>.</returns> - Task<SyncDialogOptions> GetSyncOptions(IEnumerable<string> itemIds, string userId, string parentId = null, SyncCategory? category = null); + Task<SyncDialogOptions> GetSyncOptions(SyncJobRequest jobInfo); } }
\ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncDialogOptions.cs b/MediaBrowser.Model/Sync/SyncDialogOptions.cs index 751fbbb13..080f7f2a8 100644 --- a/MediaBrowser.Model/Sync/SyncDialogOptions.cs +++ b/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>(); } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 607ba3c41..41f970041 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/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" /> diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs index 70c366bf5..246a82b20 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<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) diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs index cbfa82f1d..a8bc24c2a 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 170860dc2..33b1e13bd 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<double> 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 d0d65d437..8474cc8c5 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<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(); diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs new file mode 100644 index 000000000..d068a9e4a --- /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<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)); + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 4eb510f18..1c8e14f9e 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<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); } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 94d843174..05c2807c0 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/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> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 578cda06a..8d6ecd1a1 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/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> diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index ae5e261f9..c907f9c51 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/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> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index d3de0bb79..31064cfe0 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/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> |
