|
|
@ -1,5 +1,4 @@
|
|
|
|
using System.Globalization;
|
|
|
|
using MediaBrowser.Common.Configuration;
|
|
|
|
using MediaBrowser.Common.Configuration;
|
|
|
|
|
|
|
|
using MediaBrowser.Common.IO;
|
|
|
|
using MediaBrowser.Common.IO;
|
|
|
|
using MediaBrowser.Common.Progress;
|
|
|
|
using MediaBrowser.Common.Progress;
|
|
|
|
using MediaBrowser.Controller;
|
|
|
|
using MediaBrowser.Controller;
|
|
|
@ -12,12 +11,14 @@ using MediaBrowser.Model.MediaInfo;
|
|
|
|
using MediaBrowser.Model.Sync;
|
|
|
|
using MediaBrowser.Model.Sync;
|
|
|
|
using System;
|
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
using Patterns.IO;
|
|
|
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -29,6 +30,9 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
private readonly IConfigurationManager _config;
|
|
|
|
private readonly IConfigurationManager _config;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public const string PathSeparatorString = "/";
|
|
|
|
|
|
|
|
public const char PathSeparatorChar = '/';
|
|
|
|
|
|
|
|
|
|
|
|
public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config)
|
|
|
|
public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger = logger;
|
|
|
|
_logger = logger;
|
|
|
@ -71,7 +75,24 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
SyncTarget target,
|
|
|
|
SyncTarget target,
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var jobItemIds = await dataProvider.GetSyncJobItemIds(target, serverId).ConfigureAwait(false);
|
|
|
|
var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false);
|
|
|
|
|
|
|
|
var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
|
|
var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var jobItemIds = new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var localItem in localItems)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// TODO: Remove this after a while
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(localItem.FileId))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
jobItemIds.Add(localItem.SyncJobItemId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (remoteIds.Contains(localItem.FileId, StringComparer.OrdinalIgnoreCase))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
jobItemIds.Add(localItem.SyncJobItemId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = await _syncManager.SyncData(new SyncDataRequest
|
|
|
|
var result = await _syncManager.SyncData(new SyncDataRequest
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -163,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
var fileTransferProgress = new ActionableProgress<double>();
|
|
|
|
var fileTransferProgress = new ActionableProgress<double>();
|
|
|
|
fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
|
|
|
|
fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
|
|
|
|
|
|
|
|
|
|
|
|
var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath, target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false);
|
|
|
|
var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath.Split(PathSeparatorChar), target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
|
|
if (localItem.Item.MediaSources != null)
|
|
|
|
if (localItem.Item.MediaSources != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -177,6 +198,8 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
localItem.FileId = sendFileResult.Id;
|
|
|
|
|
|
|
|
|
|
|
|
// Create db record
|
|
|
|
// Create db record
|
|
|
|
await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
|
|
|
|
await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
|
@ -228,10 +251,10 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, options, new Progress<double>(), cancellationToken).ConfigureAwait(false);
|
|
|
|
var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, options, new Progress<double>(), cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
|
|
// This is the path that will be used when talking to the provider
|
|
|
|
// This is the path that will be used when talking to the provider
|
|
|
|
mediaStream.ExternalId = remotePath;
|
|
|
|
mediaStream.ExternalId = sendFileResult.Id;
|
|
|
|
|
|
|
|
|
|
|
|
// Keep track of all additional files for cleanup later.
|
|
|
|
// Keep track of all additional files for cleanup later.
|
|
|
|
localItem.AdditionalFiles.Add(remotePath);
|
|
|
|
localItem.AdditionalFiles.Add(sendFileResult.Id);
|
|
|
|
|
|
|
|
|
|
|
|
// This is the public path clients will use
|
|
|
|
// This is the public path clients will use
|
|
|
|
mediaStream.Path = sendFileResult.Path;
|
|
|
|
mediaStream.Path = sendFileResult.Path;
|
|
|
@ -256,17 +279,15 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target)
|
|
|
|
private string[] GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var path = item.LocalPath;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var filename = GetSubtitleSaveFileName(item, stream.Language, stream.IsForced) + "." + stream.Codec.ToLower();
|
|
|
|
var filename = GetSubtitleSaveFileName(item, stream.Language, stream.IsForced) + "." + stream.Codec.ToLower();
|
|
|
|
|
|
|
|
|
|
|
|
var parentPath = provider.GetParentDirectoryPath(path, target);
|
|
|
|
var pathParts = item.LocalPath.Split(PathSeparatorChar);
|
|
|
|
|
|
|
|
var list = pathParts.Take(pathParts.Length - 1).ToList();
|
|
|
|
path = Path.Combine(parentPath, filename);
|
|
|
|
list.Add(filename);
|
|
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
return list.ToArray();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced)
|
|
|
|
private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced)
|
|
|
@ -300,12 +321,16 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
foreach (var localItem in localItems)
|
|
|
|
foreach (var localItem in localItems)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var files = localItem.AdditionalFiles.ToList();
|
|
|
|
var files = localItem.AdditionalFiles.ToList();
|
|
|
|
files.Insert(0, localItem.LocalPath);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Remove this. Have to check it for now since this is a new property
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(localItem.FileId))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
files.Insert(0, localItem.FileId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var file in files)
|
|
|
|
foreach (var file in files)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger.Debug("Removing {0} from {1}.", file, target.Name);
|
|
|
|
_logger.Debug("Removing {0} from {1}.", file, target.Name);
|
|
|
|
|
|
|
|
|
|
|
|
await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false);
|
|
|
|
await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -313,9 +338,9 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, SyncOptions options, IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
|
private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string[] pathParts, SyncTarget target, SyncOptions options, IProgress<double> progress, CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, remotePath);
|
|
|
|
_logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, string.Join("/", pathParts));
|
|
|
|
using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
|
|
|
|
using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Stream stream = fileStream;
|
|
|
|
Stream stream = fileStream;
|
|
|
@ -325,7 +350,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes);
|
|
|
|
stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return await provider.SendFile(stream, remotePath, target, progress, cancellationToken).ConfigureAwait(false);
|
|
|
|
return await provider.SendFile(stream, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -349,7 +374,7 @@ namespace MediaBrowser.Server.Implementations.Sync
|
|
|
|
var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName);
|
|
|
|
var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName);
|
|
|
|
path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
|
|
|
|
path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
|
|
|
|
|
|
|
|
|
|
|
|
var localPath = provider.GetFullPath(path, target);
|
|
|
|
var localPath = string.Join(PathSeparatorString, path.ToArray());
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mediaSource in libraryItem.MediaSources)
|
|
|
|
foreach (var mediaSource in libraryItem.MediaSources)
|
|
|
|
{
|
|
|
|
{
|
|
|
|