aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/Sync/MediaSync.cs')
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MediaSync.cs231
1 files changed, 212 insertions, 19 deletions
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
index 099e45a6e..c9ed4637a 100644
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
@@ -1,9 +1,18 @@
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Sync;
using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -14,26 +23,25 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ISyncManager _syncManager;
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
- public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost)
+ public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem)
{
_logger = logger;
_syncManager = syncManager;
_appHost = appHost;
+ _fileSystem = fileSystem;
}
- public async Task Sync(IServerSyncProvider provider,
+ public async Task Sync(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
IProgress<double> progress,
CancellationToken cancellationToken)
{
var serverId = _appHost.SystemId;
- await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false);
- progress.Report(2);
-
- // Do the data sync twice so the server knows what was removed from the device
- await SyncData(provider, serverId, target, cancellationToken).ConfigureAwait(false);
+ await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
progress.Report(3);
var innerProgress = new ActionableProgress<double>();
@@ -43,16 +51,21 @@ namespace MediaBrowser.Server.Implementations.Sync
totalProgress += 1;
progress.Report(totalProgress);
});
- await GetNewMedia(provider, target, serverId, innerProgress, cancellationToken);
+ await GetNewMedia(provider, dataProvider, target, serverId, innerProgress, cancellationToken);
+
+ // Do the data sync twice so the server knows what was removed from the device
+ await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
+
progress.Report(100);
}
private async Task SyncData(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
string serverId,
SyncTarget target,
CancellationToken cancellationToken)
{
- var localIds = await provider.GetServerItemIds(serverId, target, cancellationToken).ConfigureAwait(false);
+ var localIds = await dataProvider.GetServerItemIds(target, serverId).ConfigureAwait(false);
var result = await _syncManager.SyncData(new SyncDataRequest
{
@@ -67,23 +80,24 @@ namespace MediaBrowser.Server.Implementations.Sync
{
try
{
- await RemoveItem(provider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false);
+ await RemoveItem(provider, dataProvider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
- _logger.ErrorException("Error deleting item from sync target. Id: {0}", ex, itemIdToRemove);
+ _logger.ErrorException("Error deleting item from device. Id: {0}", ex, itemIdToRemove);
}
}
}
private async Task GetNewMedia(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
IProgress<double> progress,
CancellationToken cancellationToken)
{
- var jobItems = await _syncManager.GetReadySyncItems(target.Id).ConfigureAwait(false);
-
+ var jobItems = await _syncManager.GetReadySyncItems(target.Id).ConfigureAwait(false);
+
var numComplete = 0;
double startingPercent = 0;
double percentPerItem = 1;
@@ -105,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.Sync
progress.Report(totalProgress);
});
- await GetItem(provider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
+ await GetItem(provider, dataProvider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
numComplete++;
startingPercent = numComplete;
@@ -116,6 +130,7 @@ namespace MediaBrowser.Server.Implementations.Sync
}
private async Task GetItem(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
SyncedItem jobItem,
@@ -128,6 +143,8 @@ namespace MediaBrowser.Server.Implementations.Sync
var fileTransferProgress = new ActionableProgress<double>();
fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
+ var localItem = CreateLocalItem(provider, jobItem.SyncJobId, jobItem.SyncJobItemId, target, libraryItem, serverId, jobItem.OriginalFileName);
+
await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id);
var transferSuccess = false;
@@ -135,8 +152,21 @@ namespace MediaBrowser.Server.Implementations.Sync
try
{
- //await provider.TransferItemFile(serverId, libraryItem.Id, internalSyncJobItem.OutputPath, target, cancellationToken)
- // .ConfigureAwait(false);
+ var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem, target, cancellationToken).ConfigureAwait(false);
+
+ if (localItem.Item.MediaSources != null)
+ {
+ var mediaSource = localItem.Item.MediaSources.FirstOrDefault();
+ if (mediaSource != null)
+ {
+ mediaSource.Path = sendFileResult.Path;
+ mediaSource.Protocol = sendFileResult.Protocol;
+ mediaSource.SupportsTranscoding = false;
+ }
+ }
+
+ // Create db record
+ await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
progress.Report(92);
@@ -162,13 +192,176 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
- private Task RemoveItem(IServerSyncProvider provider,
+ private async Task RemoveItem(IServerSyncProvider provider,
+ ISyncDataProvider dataProvider,
string serverId,
string itemId,
SyncTarget target,
CancellationToken cancellationToken)
{
- return provider.DeleteItem(serverId, itemId, target, cancellationToken);
+ var localItems = await dataProvider.GetCachedItems(target, serverId, itemId);
+
+ foreach (var localItem in localItems)
+ {
+ var files = await GetFiles(provider, localItem, target, cancellationToken);
+
+ foreach (var file in files)
+ {
+ await provider.DeleteFile(file.Path, target, cancellationToken).ConfigureAwait(false);
+ }
+
+ await dataProvider.Delete(target, localItem.Id).ConfigureAwait(false);
+ }
+ }
+
+ private async Task<SendFileResult> SendFile(IServerSyncProvider provider, string inputPath, LocalItem item, SyncTarget target, CancellationToken cancellationToken)
+ {
+ using (var stream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ {
+ return await provider.SendFile(stream, item.LocalPath, target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private static string GetLocalId(string jobItemId, string itemId)
+ {
+ var bytes = Encoding.UTF8.GetBytes(jobItemId + itemId);
+ bytes = CreateMd5(bytes);
+ return BitConverter.ToString(bytes, 0, bytes.Length).Replace("-", string.Empty);
+ }
+
+ private static byte[] CreateMd5(byte[] value)
+ {
+ using (var provider = MD5.Create())
+ {
+ return provider.ComputeHash(value);
+ }
+ }
+
+ public LocalItem CreateLocalItem(IServerSyncProvider provider, string syncJobId, string syncJobItemId, SyncTarget target, BaseItemDto libraryItem, string serverId, string originalFileName)
+ {
+ var path = GetDirectoryPath(provider, syncJobId, libraryItem, serverId);
+ path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
+
+ var localPath = provider.GetFullPath(path, target);
+
+ foreach (var mediaSource in libraryItem.MediaSources)
+ {
+ mediaSource.Path = localPath;
+ mediaSource.Protocol = MediaProtocol.File;
+ }
+
+ return new LocalItem
+ {
+ Item = libraryItem,
+ ItemId = libraryItem.Id,
+ ServerId = serverId,
+ LocalPath = localPath,
+ Id = GetLocalId(syncJobItemId, libraryItem.Id)
+ };
+ }
+
+ private List<string> GetDirectoryPath(IServerSyncProvider provider, string syncJobId, BaseItemDto item, string serverId)
+ {
+ var parts = new List<string>
+ {
+ serverId,
+ syncJobId
+ };
+
+ if (item.IsType("episode"))
+ {
+ parts.Add("TV");
+ if (!string.IsNullOrWhiteSpace(item.SeriesName))
+ {
+ parts.Add(item.SeriesName);
+ }
+ }
+ else if (item.IsVideo)
+ {
+ parts.Add("Videos");
+ parts.Add(item.Name);
+ }
+ else if (item.IsAudio)
+ {
+ parts.Add("Music");
+
+ if (!string.IsNullOrWhiteSpace(item.AlbumArtist))
+ {
+ parts.Add(item.AlbumArtist);
+ }
+
+ if (!string.IsNullOrWhiteSpace(item.Album))
+ {
+ parts.Add(item.Album);
+ }
+ }
+ else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
+ {
+ parts.Add("Photos");
+
+ if (!string.IsNullOrWhiteSpace(item.Album))
+ {
+ parts.Add(item.Album);
+ }
+ }
+
+ return parts.Select(i => GetValidFilename(provider, i)).ToList();
+ }
+
+ private string GetLocalFileName(IServerSyncProvider provider, BaseItemDto item, string originalFileName)
+ {
+ var filename = originalFileName;
+
+ if (string.IsNullOrWhiteSpace(filename))
+ {
+ filename = item.Name;
+ }
+
+ return GetValidFilename(provider, filename);
+ }
+
+ 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<List<ItemFileInfo>> GetFiles(IServerSyncProvider provider, LocalItem item, SyncTarget target, CancellationToken cancellationToken)
+ {
+ var path = item.LocalPath;
+ path = provider.GetParentDirectoryPath(path, target);
+
+ var list = await provider.GetFileSystemEntries(path, target, cancellationToken).ConfigureAwait(false);
+
+ var itemFiles = new List<ItemFileInfo>();
+
+ var name = Path.GetFileNameWithoutExtension(item.LocalPath);
+
+ foreach (var file in list.Where(f => f.Name.Contains(name)))
+ {
+ var itemFile = new ItemFileInfo
+ {
+ Path = file.Path,
+ Name = file.Name
+ };
+
+ if (IsSubtitleFile(file.Name))
+ {
+ itemFile.Type = ItemFileType.Subtitles;
+ }
+
+ itemFiles.Add(itemFile);
+ }
+
+ return itemFiles;
+ }
+
+ private static readonly string[] SupportedSubtitleExtensions = { ".srt", ".vtt" };
+ private bool IsSubtitleFile(string path)
+ {
+ var ext = Path.GetExtension(path) ?? string.Empty;
+
+ return SupportedSubtitleExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
}
}
}