using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Controller; 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; using Interfaces.IO; 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 IServerApplicationHost _appHost; public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths) { _logger = logger; _json = json; _provider = provider; _target = target; _fileSystem = fileSystem; _appPaths = appPaths; _appHost = appHost; } private string[] GetRemotePath() { var parts = new List { _appHost.FriendlyName, "data.json" }; parts = parts.Select(i => GetValidFilename(_provider, i)).ToList(); return parts.ToArray(); } private string GetValidFilename(IServerSyncProvider provider, string filename) { // We can always add this method to the sync provider if it's really needed return _fileSystem.GetValidFilename(filename); } private async Task EnsureData(CancellationToken cancellationToken) { if (_items == null) { _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name); var fileResult = await _provider.GetFiles(new FileQuery { FullPath = GetRemotePath().ToArray() }, _target, cancellationToken).ConfigureAwait(false); if (fileResult.Items.Length > 0) { using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress(), cancellationToken)) { _items = _json.DeserializeFromStream>(stream); } } else { _items = new List(); } } } 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); } } 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> GetLocalItems(SyncTarget target, string serverId) { return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).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))); } public Task> GetItems(SyncTarget target, string serverId, string itemId) { return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase)).ToList()); } public Task> GetItemsBySyncJobItemId(SyncTarget target, string serverId, string syncJobItemId) { return GetData(items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.SyncJobItemId, syncJobItemId, StringComparison.OrdinalIgnoreCase)).ToList()); } } }