Example #1
0
        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, string.Join("/", pathParts));
            var supportsDirectCopy = provider as ISupportsDirectCopy;
            if (supportsDirectCopy != null)
            {
                return await supportsDirectCopy.SendFile(inputPath, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
            }

            using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
            {
                Stream stream = fileStream;

                if (options.UploadSpeedLimitBytes > 0 && provider is IRemoteSyncProvider)
                {
                    stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes);
                }

                return await provider.SendFile(stream, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
            }
        }
Example #2
0
        private async Task Sync(SyncJobItem jobItem, SyncJob job, Audio item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken)
        {
            var jobOptions = _syncManager.GetAudioOptions(jobItem, job);
            var conversionOptions = new AudioOptions
            {
                Profile = jobOptions.DeviceProfile
            };

            conversionOptions.DeviceId = jobItem.TargetId;
            conversionOptions.Context = EncodingContext.Static;
            conversionOptions.ItemId = item.Id.ToString("N");
            conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList();

            var streamInfo = new StreamBuilder(_logger).BuildAudioItem(conversionOptions);
            var mediaSource = streamInfo.MediaSource;

            jobItem.MediaSourceId = streamInfo.MediaSourceId;
            jobItem.TemporaryPath = GetTemporaryPath(jobItem);

            if (streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting)
            {
                if (!enableConversion)
                {
                    return;
                }

                jobItem.Status = SyncJobItemStatus.Converting;
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                await UpdateJobStatus(job).ConfigureAwait(false);

                try
                {
                    var lastJobUpdate = DateTime.MinValue;
                    var innerProgress = new ActionableProgress<double>();
                    innerProgress.RegisterAction(async pct =>
                    {
                        progress.Report(pct);

                        if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                        {
                            jobItem.Progress = pct / 2;
                            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                            await UpdateJobStatus(job).ConfigureAwait(false);
                        }
                    });

                    jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, conversionOptions.Profile)
                    {
                        OutputDirectory = jobItem.TemporaryPath,
                        CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit

                    }, innerProgress, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    jobItem.Status = SyncJobItemStatus.Queued;
                    jobItem.Progress = 0;
                }
                catch (Exception ex)
                {
                    jobItem.Status = SyncJobItemStatus.Failed;
                    _logger.ErrorException("Error during sync transcoding", ex);
                }

                if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                {
                    await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                    return;
                }

                jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, false).ConfigureAwait(false);
            }
            else
            {
                if (mediaSource.Protocol == MediaProtocol.File)
                {
                    jobItem.OutputPath = mediaSource.Path;
                }
                else if (mediaSource.Protocol == MediaProtocol.Http)
                {
                    jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
                }

                jobItem.MediaSource = mediaSource;
            }

            jobItem.MediaSource.SupportsTranscoding = false;

            jobItem.Progress = 50;
            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
        }
Example #3
0
        private async Task SendSubtitles(LocalItem localItem, MediaSourceInfo mediaSource, IServerSyncProvider provider, ISyncDataProvider dataProvider, SyncTarget target, SyncOptions options, 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, options, new Progress<double>(), cancellationToken).ConfigureAwait(false);

                    // This is the path that will be used when talking to the provider
                    mediaStream.ExternalId = sendFileResult.Id;

                    // Keep track of all additional files for cleanup later.
                    localItem.AdditionalFiles.Add(sendFileResult.Id);

                    // 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);
            }
        }
Example #4
0
        private async Task Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken)
        {
            var jobOptions = _syncManager.GetVideoOptions(jobItem, job);
            var conversionOptions = new VideoOptions
            {
                Profile = jobOptions.DeviceProfile
            };

            conversionOptions.DeviceId = jobItem.TargetId;
            conversionOptions.Context = EncodingContext.Static;
            conversionOptions.ItemId = item.Id.ToString("N");
            conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList();

            var streamInfo = new StreamBuilder(_logger).BuildVideoItem(conversionOptions);
            var mediaSource = streamInfo.MediaSource;

            // No sense creating external subs if we're already burning one into the video
            var externalSubs = streamInfo.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ?
                new List<SubtitleStreamInfo>() :
                streamInfo.GetExternalSubtitles(false, true, null, null);

            // Mark as requiring conversion if transcoding the video, or if any subtitles need to be extracted
            var requiresVideoTranscoding = streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting;
            var requiresConversion = requiresVideoTranscoding || externalSubs.Any(i => RequiresExtraction(i, mediaSource));

            if (requiresConversion && !enableConversion)
            {
                return;
            }

            jobItem.MediaSourceId = streamInfo.MediaSourceId;
            jobItem.TemporaryPath = GetTemporaryPath(jobItem);

            if (requiresConversion)
            {
                jobItem.Status = SyncJobItemStatus.Converting;
            }

            if (requiresVideoTranscoding)
            {
                // Save the job item now since conversion could take a while
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                await UpdateJobStatus(job).ConfigureAwait(false);

                try
                {
                    var lastJobUpdate = DateTime.MinValue;
                    var innerProgress = new ActionableProgress<double>();
                    innerProgress.RegisterAction(async pct =>
                    {
                        progress.Report(pct);

                        if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                        {
                            jobItem.Progress = pct / 2;
                            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                            await UpdateJobStatus(job).ConfigureAwait(false);
                        }
                    });

                    jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, conversionOptions.Profile)
                    {
                        OutputDirectory = jobItem.TemporaryPath,
                        CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit,
                        ReadInputAtNativeFramerate = !syncOptions.EnableFullSpeedTranscoding

                    }, innerProgress, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    jobItem.Status = SyncJobItemStatus.Queued;
                    jobItem.Progress = 0;
                }
                catch (Exception ex)
                {
                    jobItem.Status = SyncJobItemStatus.Failed;
                    _logger.ErrorException("Error during sync transcoding", ex);
                }

                if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                {
                    await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                    return;
                }

                jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, true).ConfigureAwait(false);
            }
            else
            {
                if (mediaSource.Protocol == MediaProtocol.File)
                {
                    jobItem.OutputPath = mediaSource.Path;
                }
                else if (mediaSource.Protocol == MediaProtocol.Http)
                {
                    jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
                }

                jobItem.MediaSource = mediaSource;
            }

            jobItem.MediaSource.SupportsTranscoding = false;

            if (externalSubs.Count > 0)
            {
                // Save the job item now since conversion could take a while
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false);
            }

            jobItem.Progress = 50;
            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
        }