Example #1
0
        private async Task Sync(SyncJobItem jobItem, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress <double> progress, CancellationToken cancellationToken)
        {
            var job               = _syncManager.GetJob(jobItem.JobId);
            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(_mediaEncoder, _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(jobItem.JobId).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(jobItem.JobId).ConfigureAwait(false);
                        }
                    });

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

                    jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
                    _syncManager.OnConversionComplete(jobItem);
                }
                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.ItemDateModifiedTicks = item.DateModified.Ticks;
                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);
        }