aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Sync
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-07-26 13:30:15 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-07-26 13:30:15 -0400
commit37c27a26e90b7eff62cec9e2b6a6c003e79fcbe4 (patch)
treed8c628a1f1ffeb33ba021158822eeffa74928f4e /MediaBrowser.Server.Implementations/Sync
parentd56fa09ccc5e1a5f9440645330ce337273fa3bd7 (diff)
added sync job database
Diffstat (limited to 'MediaBrowser.Server.Implementations/Sync')
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs150
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncRepository.cs429
2 files changed, 560 insertions, 19 deletions
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
index 50f1030f3..a8d723ce3 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
@@ -1,9 +1,14 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
+using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,56 +18,131 @@ namespace MediaBrowser.Server.Implementations.Sync
{
public class SyncManager : ISyncManager
{
- private ISyncProvider[] _providers = new ISyncProvider[] { };
+ private readonly ILibraryManager _libraryManager;
+ private readonly ISyncRepository _repo;
+ private readonly IImageProcessor _imageProcessor;
+ private readonly ILogger _logger;
+
+ private ISyncProvider[] _providers = { };
+
+ public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger)
+ {
+ _libraryManager = libraryManager;
+ _repo = repo;
+ _imageProcessor = imageProcessor;
+ _logger = logger;
+ }
public void AddParts(IEnumerable<ISyncProvider> providers)
{
_providers = providers.ToArray();
}
- public Task<List<SyncJob>> CreateJob(SyncJobRequest request)
+ public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
{
- throw new NotImplementedException();
+ var items = GetItemsForSync(request.ItemIds).ToList();
+
+ if (items.Count == 1)
+ {
+ request.Name = GetDefaultName(items[0]);
+ }
+
+ if (string.IsNullOrWhiteSpace(request.Name))
+ {
+ throw new ArgumentException("Please supply a name for the sync job.");
+ }
+
+ var target = GetSyncTargets(request.UserId)
+ .First(i => string.Equals(request.TargetId, i.Id));
+
+ var jobId = Guid.NewGuid().ToString("N");
+
+ var job = new SyncJob
+ {
+ Id = jobId,
+ Name = request.Name,
+ TargetId = target.Id,
+ UserId = request.UserId,
+ UnwatchedOnly = request.UnwatchedOnly,
+ Limit = request.Limit,
+ LimitType = request.LimitType,
+ RequestedItemIds = request.ItemIds,
+ DateCreated = DateTime.UtcNow,
+ DateLastModified = DateTime.UtcNow
+ };
+
+ await _repo.Create(job).ConfigureAwait(false);
+
+ return new SyncJobCreationResult
+ {
+ Job = GetJob(jobId)
+ };
}
public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
{
- throw new NotImplementedException();
- }
+ var result = _repo.GetJobs(query);
- public QueryResult<SyncSchedule> GetSchedules(SyncScheduleQuery query)
- {
- throw new NotImplementedException();
- }
+ result.Items.ForEach(FillMetadata);
- public Task CancelJob(string id)
- {
- throw new NotImplementedException();
+ return result;
}
- public Task CancelSchedule(string id)
+ private void FillMetadata(SyncJob job)
{
- throw new NotImplementedException();
+ var item = GetItemsForSync(job.RequestedItemIds)
+ .FirstOrDefault();
+
+ if (item != null)
+ {
+ var hasSeries = item as IHasSeries;
+ if (hasSeries != null)
+ {
+ job.ParentName = hasSeries.SeriesName;
+ }
+
+ var hasAlbumArtist = item as IHasAlbumArtist;
+ if (hasAlbumArtist != null)
+ {
+ job.ParentName = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ }
+
+ var primaryImage = item.GetImageInfo(ImageType.Primary, 0);
+
+ if (primaryImage != null)
+ {
+ try
+ {
+ job.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
+ job.PrimaryImageItemId = item.Id.ToString("N");
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting image info", ex);
+ }
+ }
+ }
}
- public SyncJob GetJob(string id)
+ public Task CancelJob(string id)
{
throw new NotImplementedException();
}
- public SyncSchedule GetSchedule(string id)
+ public SyncJob GetJob(string id)
{
- throw new NotImplementedException();
+ return _repo.GetJob(id);
}
public IEnumerable<SyncTarget> GetSyncTargets(string userId)
{
return _providers
- .SelectMany(GetSyncTargets)
+ .SelectMany(i => GetSyncTargets(i, userId))
.OrderBy(i => i.Name);
}
- private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider)
+ private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider, string userId)
{
var providerId = GetSyncProviderId(provider);
@@ -120,5 +200,37 @@ namespace MediaBrowser.Server.Implementations.Sync
return false;
}
+
+ private IEnumerable<BaseItem> GetItemsForSync(IEnumerable<string> itemIds)
+ {
+ return itemIds.SelectMany(GetItemsForSync).DistinctBy(i => i.Id);
+ }
+
+ private IEnumerable<BaseItem> GetItemsForSync(string id)
+ {
+ var item = _libraryManager.GetItemById(id);
+
+ if (item == null)
+ {
+ throw new ArgumentException("Item with Id " + id + " not found.");
+ }
+
+ if (!SupportsSync(item))
+ {
+ throw new ArgumentException("Item with Id " + id + " does not support sync.");
+ }
+
+ return GetItemsForSync(item);
+ }
+
+ private IEnumerable<BaseItem> GetItemsForSync(BaseItem item)
+ {
+ return new[] { item };
+ }
+
+ private string GetDefaultName(BaseItem item)
+ {
+ return item.Name;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
new file mode 100644
index 000000000..bb22e992b
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
@@ -0,0 +1,429 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
+using MediaBrowser.Server.Implementations.Persistence;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Sync
+{
+ public class SyncRepository : ISyncRepository
+ {
+ private IDbConnection _connection;
+ private readonly ILogger _logger;
+ private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ private IDbCommand _saveJobCommand;
+ private IDbCommand _saveJobItemCommand;
+
+ public SyncRepository(ILogger logger, IServerApplicationPaths appPaths)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ }
+
+ public async Task Initialize()
+ {
+ var dbFile = Path.Combine(_appPaths.DataPath, "sync.db");
+
+ _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
+
+ string[] queries = {
+
+ "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, UnwatchedOnly BIT, SyncLimit BigInt, LimitType TEXT, IsDynamic BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
+ "create index if not exists idx_SyncJobs on SyncJobs(Id)",
+
+ "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT)",
+ "create index if not exists idx_SyncJobItems on SyncJobs(Id)",
+
+ //pragmas
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
+ };
+
+ _connection.RunQueries(queries, _logger);
+
+ PrepareStatements();
+ }
+
+ private void PrepareStatements()
+ {
+ _saveJobCommand = _connection.CreateCommand();
+ _saveJobCommand.CommandText = "replace into SyncJobs (Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Quality, @Status, @Progress, @UserId, @ItemIds, @UnwatchedOnly, @SyncLimit, @LimitType, @IsDynamic, @DateCreated, @DateLastModified, @ItemCount)";
+
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Id");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@TargetId");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Name");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Quality");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Status");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@Progress");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@UserId");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemIds");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@UnwatchedOnly");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@SyncLimit");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@LimitType");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@IsDynamic");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateCreated");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateLastModified");
+ _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount");
+
+ _saveJobItemCommand = _connection.CreateCommand();
+ _saveJobItemCommand.CommandText = "replace into SyncJobItems (Id, ItemId, JobId, OutputPath, Status, TargetId) values (@Id, @ItemId, @JobId, @OutputPath, @Status, @TargetId)";
+
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Id");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@ItemId");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@JobId");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@OutputPath");
+ _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Status");
+ }
+
+ private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount from SyncJobs";
+ private const string BaseJobItemSelectText = "select Id, ItemId, JobId, OutputPath, Status, TargetId from SyncJobItems";
+
+ public SyncJob GetJob(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ var guid = new Guid(id);
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobSelectText + " where Id=@Id";
+
+ cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ {
+ if (reader.Read())
+ {
+ return GetJob(reader);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private SyncJob GetJob(IDataReader reader)
+ {
+ var info = new SyncJob
+ {
+ Id = reader.GetGuid(0).ToString("N"),
+ TargetId = reader.GetString(1),
+ Name = reader.GetString(2)
+ };
+
+ if (!reader.IsDBNull(3))
+ {
+ info.Quality = (SyncQuality)Enum.Parse(typeof(SyncQuality), reader.GetString(3), true);
+ }
+
+ if (!reader.IsDBNull(4))
+ {
+ info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
+ }
+
+ if (!reader.IsDBNull(5))
+ {
+ info.Progress = reader.GetDouble(5);
+ }
+
+ if (!reader.IsDBNull(6))
+ {
+ info.UserId = reader.GetString(6);
+ }
+
+ if (!reader.IsDBNull(7))
+ {
+ info.RequestedItemIds = reader.GetString(7).Split(',').ToList();
+ }
+
+ if (!reader.IsDBNull(8))
+ {
+ info.UnwatchedOnly = reader.GetBoolean(8);
+ }
+
+ if (!reader.IsDBNull(9))
+ {
+ info.Limit = reader.GetInt64(9);
+ }
+
+ if (!reader.IsDBNull(10))
+ {
+ info.LimitType = (SyncLimitType)Enum.Parse(typeof(SyncLimitType), reader.GetString(10), true);
+ }
+
+ info.IsDynamic = reader.GetBoolean(11);
+ info.DateCreated = reader.GetDateTime(12).ToUniversalTime();
+ info.DateLastModified = reader.GetDateTime(13).ToUniversalTime();
+ info.ItemCount = reader.GetInt32(14);
+
+ return info;
+ }
+
+ public Task Create(SyncJob job)
+ {
+ return Update(job);
+ }
+
+ public async Task Update(SyncJob job)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ await _writeLock.WaitAsync().ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = _connection.BeginTransaction();
+
+ var index = 0;
+
+ _saveJobCommand.GetParameter(index++).Value = new Guid(job.Id);
+ _saveJobCommand.GetParameter(index++).Value = job.TargetId;
+ _saveJobCommand.GetParameter(index++).Value = job.Name;
+ _saveJobCommand.GetParameter(index++).Value = job.Quality;
+ _saveJobCommand.GetParameter(index++).Value = job.Status;
+ _saveJobCommand.GetParameter(index++).Value = job.Progress;
+ _saveJobCommand.GetParameter(index++).Value = job.UserId;
+ _saveJobCommand.GetParameter(index++).Value = string.Join(",", job.RequestedItemIds.ToArray());
+ _saveJobCommand.GetParameter(index++).Value = job.UnwatchedOnly;
+ _saveJobCommand.GetParameter(index++).Value = job.Limit;
+ _saveJobCommand.GetParameter(index++).Value = job.LimitType;
+ _saveJobCommand.GetParameter(index++).Value = job.IsDynamic;
+ _saveJobCommand.GetParameter(index++).Value = job.DateCreated;
+ _saveJobCommand.GetParameter(index++).Value = job.DateLastModified;
+ _saveJobCommand.GetParameter(index++).Value = job.ItemCount;
+
+ _saveJobCommand.Transaction = transaction;
+
+ _saveJobCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save record:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
+ public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobSelectText;
+
+ var whereClauses = new List<string>();
+
+ var startIndex = query.StartIndex ?? 0;
+
+ if (startIndex > 0)
+ {
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY DateLastModified DESC LIMIT {0})",
+ startIndex.ToString(_usCulture)));
+ }
+
+ if (whereClauses.Count > 0)
+ {
+ cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
+
+ cmd.CommandText += " ORDER BY DateLastModified DESC";
+
+ if (query.Limit.HasValue)
+ {
+ cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ cmd.CommandText += "; select count (Id) from SyncJobs";
+
+ var list = new List<SyncJob>();
+ var count = 0;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
+ {
+ while (reader.Read())
+ {
+ list.Add(GetJob(reader));
+ }
+
+ if (reader.NextResult() && reader.Read())
+ {
+ count = reader.GetInt32(0);
+ }
+ }
+
+ return new QueryResult<SyncJob>()
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+ }
+ }
+
+ public SyncJobItem GetJobItem(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ var guid = new Guid(id);
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = BaseJobItemSelectText + " where Id=@Id";
+
+ cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
+
+ using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ {
+ if (reader.Read())
+ {
+ return GetSyncJobItem(reader);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public Task Create(SyncJobItem jobItem)
+ {
+ return Update(jobItem);
+ }
+
+ public async Task Update(SyncJobItem jobItem)
+ {
+ if (jobItem == null)
+ {
+ throw new ArgumentNullException("jobItem");
+ }
+
+ await _writeLock.WaitAsync().ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = _connection.BeginTransaction();
+
+ var index = 0;
+
+ _saveJobItemCommand.GetParameter(index++).Value = new Guid(jobItem.Id);
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemId;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.JobId;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.Status;
+ _saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
+
+ _saveJobItemCommand.Transaction = transaction;
+
+ _saveJobItemCommand.ExecuteNonQuery();
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save record:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
+ private SyncJobItem GetSyncJobItem(IDataReader reader)
+ {
+ var info = new SyncJobItem
+ {
+ Id = reader.GetGuid(0).ToString("N"),
+ ItemId = reader.GetString(1),
+ JobId = reader.GetString(2)
+ };
+
+ if (!reader.IsDBNull(3))
+ {
+ info.OutputPath = reader.GetString(3);
+ }
+
+ if (!reader.IsDBNull(4))
+ {
+ info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
+ }
+
+ info.TargetId = reader.GetString(5);
+
+ return info;
+ }
+ }
+}