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.cs211
1 files changed, 151 insertions, 60 deletions
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
index c9ed4637a..5bc8b8088 100644
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using System.Globalization;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Sync;
@@ -40,6 +41,7 @@ namespace MediaBrowser.Server.Implementations.Sync
CancellationToken cancellationToken)
{
var serverId = _appHost.SystemId;
+ var serverName = _appHost.FriendlyName;
await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
progress.Report(3);
@@ -51,7 +53,7 @@ namespace MediaBrowser.Server.Implementations.Sync
totalProgress += 1;
progress.Report(totalProgress);
});
- await GetNewMedia(provider, dataProvider, target, serverId, innerProgress, cancellationToken);
+ await GetNewMedia(provider, dataProvider, target, serverId, serverName, 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);
@@ -65,12 +67,12 @@ namespace MediaBrowser.Server.Implementations.Sync
SyncTarget target,
CancellationToken cancellationToken)
{
- var localIds = await dataProvider.GetServerItemIds(target, serverId).ConfigureAwait(false);
+ var jobItemIds = await dataProvider.GetSyncJobItemIds(target, serverId).ConfigureAwait(false);
var result = await _syncManager.SyncData(new SyncDataRequest
{
TargetId = target.Id,
- LocalItemIds = localIds
+ SyncJobItemIds = jobItemIds
}).ConfigureAwait(false);
@@ -93,6 +95,7 @@ namespace MediaBrowser.Server.Implementations.Sync
ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
+ string serverName,
IProgress<double> progress,
CancellationToken cancellationToken)
{
@@ -119,7 +122,7 @@ namespace MediaBrowser.Server.Implementations.Sync
progress.Report(totalProgress);
});
- await GetItem(provider, dataProvider, target, serverId, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
+ await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
numComplete++;
startingPercent = numComplete;
@@ -133,17 +136,16 @@ namespace MediaBrowser.Server.Implementations.Sync
ISyncDataProvider dataProvider,
SyncTarget target,
string serverId,
+ string serverName,
SyncedItem jobItem,
IProgress<double> progress,
CancellationToken cancellationToken)
{
var libraryItem = jobItem.Item;
var internalSyncJobItem = _syncManager.GetJobItem(jobItem.SyncJobItemId);
+ var internalSyncJob = _syncManager.GetJob(jobItem.SyncJobId);
- 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);
+ var localItem = CreateLocalItem(provider, jobItem, internalSyncJob, target, libraryItem, serverId, serverName, jobItem.OriginalFileName);
await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id);
@@ -152,7 +154,10 @@ namespace MediaBrowser.Server.Implementations.Sync
try
{
- var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem, target, cancellationToken).ConfigureAwait(false);
+ var fileTransferProgress = new ActionableProgress<double>();
+ fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
+
+ var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath, target, fileTransferProgress, cancellationToken).ConfigureAwait(false);
if (localItem.Item.MediaSources != null)
{
@@ -161,6 +166,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
mediaSource.Path = sendFileResult.Path;
mediaSource.Protocol = sendFileResult.Protocol;
+ mediaSource.RequiredHttpHeaders = sendFileResult.RequiredHttpHeaders;
mediaSource.SupportsTranscoding = false;
}
}
@@ -168,6 +174,15 @@ namespace MediaBrowser.Server.Implementations.Sync
// Create db record
await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
+ if (localItem.Item.MediaSources != null)
+ {
+ var mediaSource = localItem.Item.MediaSources.FirstOrDefault();
+ if (mediaSource != null)
+ {
+ await SendSubtitles(localItem, mediaSource, provider, dataProvider, target, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
progress.Report(92);
transferSuccess = true;
@@ -192,33 +207,112 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
+ private async Task SendSubtitles(LocalItem localItem, MediaSourceInfo mediaSource, IServerSyncProvider provider, ISyncDataProvider dataProvider, SyncTarget target, CancellationToken cancellationToken)
+ {
+ var failedSubtitles = new List<MediaStream>();
+ var requiresSave = false;
+
+ foreach (var mediaStream in mediaSource.MediaStreams
+ .Where(i => i.Type == MediaStreamType.Subtitle && i.IsExternal)
+ .ToList())
+ {
+ try
+ {
+ var remotePath = GetRemoteSubtitlePath(localItem, mediaStream, provider, target);
+ var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+
+ // This is the path that will be used when talking to the provider
+ mediaStream.ExternalId = remotePath;
+
+ // Keep track of all additional files for cleanup later.
+ localItem.AdditionalFiles.Add(remotePath);
+
+ // This is the public path clients will use
+ mediaStream.Path = sendFileResult.Path;
+ requiresSave = true;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending subtitle stream", ex);
+ failedSubtitles.Add(mediaStream);
+ }
+ }
+
+ if (failedSubtitles.Count > 0)
+ {
+ mediaSource.MediaStreams = mediaSource.MediaStreams.Except(failedSubtitles).ToList();
+ requiresSave = true;
+ }
+
+ if (requiresSave)
+ {
+ await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
+ }
+ }
+
+ 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 parentPath = provider.GetParentDirectoryPath(path, target);
+
+ path = Path.Combine(parentPath, filename);
+
+ return path;
+ }
+
+ private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced)
+ {
+ var path = item.LocalPath;
+
+ var name = Path.GetFileNameWithoutExtension(path);
+
+ if (!string.IsNullOrWhiteSpace(language))
+ {
+ name += "." + language.ToLower();
+ }
+
+ if (isForced)
+ {
+ name += ".foreign";
+ }
+
+ return name;
+ }
+
private async Task RemoveItem(IServerSyncProvider provider,
ISyncDataProvider dataProvider,
string serverId,
- string itemId,
+ string syncJobItemId,
SyncTarget target,
CancellationToken cancellationToken)
{
- var localItems = await dataProvider.GetCachedItems(target, serverId, itemId);
+ var localItems = await dataProvider.GetCachedItemsBySyncJobItemId(target, serverId, syncJobItemId);
foreach (var localItem in localItems)
{
- var files = await GetFiles(provider, localItem, target, cancellationToken);
+ var files = localItem.AdditionalFiles.ToList();
+ files.Insert(0, localItem.LocalPath);
foreach (var file in files)
{
- await provider.DeleteFile(file.Path, target, cancellationToken).ConfigureAwait(false);
+ _logger.Debug("Removing {0} from {1}.", file, target.Name);
+
+ await provider.DeleteFile(file, 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)
+ private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string remotePath, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken)
{
+ _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, remotePath);
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);
+ return await provider.SendFile(stream, remotePath, target, progress, cancellationToken).ConfigureAwait(false);
}
}
@@ -237,9 +331,9 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
- public LocalItem CreateLocalItem(IServerSyncProvider provider, string syncJobId, string syncJobItemId, SyncTarget target, BaseItemDto libraryItem, string serverId, string originalFileName)
+ public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncJob job, SyncTarget target, BaseItemDto libraryItem, string serverId, string serverName, string originalFileName)
{
- var path = GetDirectoryPath(provider, syncJobId, libraryItem, serverId);
+ var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName);
path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
var localPath = provider.GetFullPath(path, target);
@@ -256,18 +350,53 @@ namespace MediaBrowser.Server.Implementations.Sync
ItemId = libraryItem.Id,
ServerId = serverId,
LocalPath = localPath,
- Id = GetLocalId(syncJobItemId, libraryItem.Id)
+ Id = GetLocalId(syncedItem.SyncJobItemId, libraryItem.Id),
+ SyncJobItemId = syncedItem.SyncJobItemId
};
}
- private List<string> GetDirectoryPath(IServerSyncProvider provider, string syncJobId, BaseItemDto item, string serverId)
+ private List<string> GetDirectoryPath(IServerSyncProvider provider, SyncJob job, SyncedItem syncedItem, BaseItemDto item, string serverName)
{
var parts = new List<string>
{
- serverId,
- syncJobId
+ serverName
};
+ var profileOption = _syncManager.GetProfileOptions(job.TargetId)
+ .FirstOrDefault(i => string.Equals(i.Id, job.Profile, StringComparison.OrdinalIgnoreCase));
+
+ string name;
+
+ if (profileOption != null && !string.IsNullOrWhiteSpace(profileOption.Name))
+ {
+ name = profileOption.Name;
+
+ if (job.Bitrate.HasValue)
+ {
+ name += "-" + job.Bitrate.Value.ToString(CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ var qualityOption = _syncManager.GetQualityOptions(job.TargetId)
+ .FirstOrDefault(i => string.Equals(i.Id, job.Quality, StringComparison.OrdinalIgnoreCase));
+
+ if (qualityOption != null && !string.IsNullOrWhiteSpace(qualityOption.Name))
+ {
+ name += "-" + qualityOption.Name;
+ }
+ }
+ }
+ else
+ {
+ name = syncedItem.SyncJobName + "-" + syncedItem.SyncJobDateCreated
+ .ToLocalTime()
+ .ToString("g")
+ .Replace(" ", "-");
+ }
+
+ name = GetValidFilename(provider, name);
+ parts.Add(name);
+
if (item.IsType("episode"))
{
parts.Add("TV");
@@ -325,43 +454,5 @@ namespace MediaBrowser.Server.Implementations.Sync
// 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);
- }
}
}