Пример #1
0
        internal async Task UpdateSyncJobItemInternal(SyncJobItem jobItem)
        {
            await _repo.Update(jobItem).ConfigureAwait(false);

            if (SyncJobUpdated != null)
            {
                EventHelper.FireEventIfNotNull(SyncJobItemUpdated, this, new GenericEventArgs <SyncJobItem>
                {
                    Argument = jobItem
                }, _logger);
            }
        }
Пример #2
0
        private async Task ProcessJobItem(SyncJob job, SyncJobItem jobItem, IProgress <double> progress, CancellationToken cancellationToken)
        {
            var item = _libraryManager.GetItemById(jobItem.ItemId);

            if (item == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
                await _syncRepo.Update(jobItem).ConfigureAwait(false);

                return;
            }

            var deviceProfile = _syncManager.GetDeviceProfile(jobItem.TargetId);

            if (deviceProfile == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("Unable to locate SyncTarget for JobItem {0}, SyncTargetId {1}", jobItem.Id, jobItem.TargetId);
                await _syncRepo.Update(jobItem).ConfigureAwait(false);

                return;
            }

            jobItem.Progress = 0;
            jobItem.Status   = SyncJobItemStatus.Converting;

            var user = _userManager.GetUserById(job.UserId);

            var video = item as Video;

            if (video != null)
            {
                await Sync(jobItem, video, user, deviceProfile, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Audio)
            {
                await Sync(jobItem, (Audio)item, user, deviceProfile, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Photo)
            {
                await Sync(jobItem, (Photo)item, deviceProfile, cancellationToken).ConfigureAwait(false);
            }

            else
            {
                await SyncGeneric(jobItem, item, deviceProfile, cancellationToken).ConfigureAwait(false);
            }
        }
Пример #3
0
 public ISyncProvider GetSyncProvider(SyncJobItem jobItem)
 {
     foreach (var provider in _providers)
     {
         foreach (var target in GetSyncTargets(provider))
         {
             if (string.Equals(target.Id, jobItem.TargetId, StringComparison.OrdinalIgnoreCase))
             {
                 return(provider);
             }
         }
     }
     return(null);
 }
Пример #4
0
        private async Task ProcessJobItem(SyncJob job, SyncJobItem jobItem, bool enableConversion, IProgress <double> progress, CancellationToken cancellationToken)
        {
            var item = _libraryManager.GetItemById(jobItem.ItemId);

            if (item == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                return;
            }

            jobItem.Progress = 0;

            var syncOptions = _config.GetSyncOptions();
            var user        = _userManager.GetUserById(job.UserId);

            if (user == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("User not found. Cannot complete the sync job.");
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                return;
            }

            var video = item as Video;

            if (video != null)
            {
                await Sync(jobItem, job, video, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Audio)
            {
                await Sync(jobItem, job, (Audio)item, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Photo)
            {
                await Sync(jobItem, (Photo)item, cancellationToken).ConfigureAwait(false);
            }

            else
            {
                await SyncGeneric(jobItem, item, cancellationToken).ConfigureAwait(false);
            }
        }
Пример #5
0
        private SyncedItem GetJobItemInfo(SyncJobItem jobItem)
        {
            var job = _repo.GetJob(jobItem.JobId);

            var libraryItem = _libraryManager.GetItemById(jobItem.ItemId);

            var syncedItem = new SyncedItem
            {
                SyncJobId     = jobItem.JobId,
                SyncJobItemId = jobItem.Id,
                ServerId      = _appHost.SystemId,
                UserId        = job.UserId
            };

            var dtoOptions = new DtoOptions();

            // Remove some bloat
            dtoOptions.Fields.Remove(ItemFields.MediaStreams);
            dtoOptions.Fields.Remove(ItemFields.IndexOptions);
            dtoOptions.Fields.Remove(ItemFields.MediaSourceCount);
            dtoOptions.Fields.Remove(ItemFields.OriginalPrimaryImageAspectRatio);
            dtoOptions.Fields.Remove(ItemFields.Path);
            dtoOptions.Fields.Remove(ItemFields.SeriesGenres);
            dtoOptions.Fields.Remove(ItemFields.Settings);
            dtoOptions.Fields.Remove(ItemFields.SyncInfo);

            syncedItem.Item = _dtoService().GetBaseItemDto(libraryItem, dtoOptions);

            // TODO: this should be the media source of the transcoded output
            syncedItem.Item.MediaSources = syncedItem.Item.MediaSources
                                           .Where(i => string.Equals(i.Id, jobItem.MediaSourceId))
                                           .ToList();

            var mediaSource = syncedItem.Item.MediaSources
                              .FirstOrDefault(i => string.Equals(i.Id, jobItem.MediaSourceId));

            // This will be null for items that are not audio/video
            if (mediaSource == null)
            {
                syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path);
            }
            else
            {
                syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path);
            }

            return(syncedItem);
        }
Пример #6
0
        private SyncJobItem GetJobItem(IDataReader reader)
        {
            var info = new SyncJobItem
            {
                Id     = reader.GetGuid(0).ToString("N"),
                ItemId = reader.GetString(1)
            };

            if (!reader.IsDBNull(2))
            {
                info.ItemName = reader.GetString(2);
            }

            if (!reader.IsDBNull(3))
            {
                info.MediaSourceId = reader.GetString(3);
            }

            info.JobId = reader.GetString(4);
            info.RequiresConversion = reader.GetBoolean(5);

            if (!reader.IsDBNull(6))
            {
                info.OutputPath = reader.GetString(6);
            }

            if (!reader.IsDBNull(7))
            {
                info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader.GetString(7), true);
            }

            info.TargetId = reader.GetString(8);

            info.DateCreated = reader.GetDateTime(9);

            if (!reader.IsDBNull(10))
            {
                info.Progress = reader.GetDouble(10);
            }

            return(info);
        }
Пример #7
0
        private async Task ConvertSubtitles(SyncJobItem jobItem,
                                            IEnumerable <SubtitleStreamInfo> subtitles,
                                            StreamInfo streamInfo,
                                            CancellationToken cancellationToken)
        {
            var files = new List <ItemFileInfo>();

            var mediaStreams = jobItem.MediaSource.MediaStreams
                               .Where(i => i.Type != MediaStreamType.Subtitle || !i.IsExternal)
                               .ToList();

            var startingIndex = mediaStreams.Count == 0 ?
                                0 :
                                (mediaStreams.Select(i => i.Index).Max() + 1);

            foreach (var subtitle in subtitles)
            {
                var fileInfo = await ConvertSubtitles(jobItem.TemporaryPath, streamInfo, subtitle, cancellationToken).ConfigureAwait(false);

                // Reset this to a value that will be based on the output media
                fileInfo.Index = startingIndex;
                files.Add(fileInfo);

                mediaStreams.Add(new MediaStream
                {
                    Index                  = startingIndex,
                    Codec                  = subtitle.Format,
                    IsForced               = subtitle.IsForced,
                    IsExternal             = true,
                    Language               = subtitle.Language,
                    Path                   = fileInfo.Path,
                    SupportsExternalStream = true,
                    Type                   = MediaStreamType.Subtitle
                });

                startingIndex++;
            }

            jobItem.AdditionalFiles.AddRange(files);

            jobItem.MediaSource.MediaStreams = mediaStreams;
        }
Пример #8
0
        private SyncJobItem GetSyncJobItem(IDataReader reader)
        {
            var info = new SyncJobItem
            {
                Id     = reader.GetGuid(0).ToString("N"),
                ItemId = reader.GetString(1),
                JobId  = reader.GetString(2)
            };

            if (!reader.IsDBNull(3))
            {
                info.OutputPath = reader.GetString(3);
            }

            if (!reader.IsDBNull(4))
            {
                info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(4), true);
            }

            info.TargetId = reader.GetString(5);

            return(info);
        }
Пример #9
0
        public VideoOptions GetVideoOptions(SyncJobItem jobItem, SyncJob job)
        {
            var profile    = GetDeviceProfile(jobItem.TargetId);
            var maxBitrate = profile.MaxStaticBitrate;

            if (maxBitrate.HasValue)
            {
                if (job.Quality == SyncQuality.Medium)
                {
                    maxBitrate = Convert.ToInt32(maxBitrate.Value * .75);
                }
                else if (job.Quality == SyncQuality.Low)
                {
                    maxBitrate = Convert.ToInt32(maxBitrate.Value * .5);
                }
            }

            return(new VideoOptions
            {
                Profile = profile,
                MaxBitrate = maxBitrate
            });
        }
Пример #10
0
        private void FillMetadata(SyncJobItem jobItem)
        {
            var item = _libraryManager.GetItemById(jobItem.ItemId);

            if (item == null)
            {
                return;
            }

            var primaryImage  = item.GetImageInfo(ImageType.Primary, 0);
            var itemWithImage = item;

            if (primaryImage == null)
            {
                var parentWithImage = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));

                if (parentWithImage != null)
                {
                    itemWithImage = parentWithImage;
                    primaryImage  = parentWithImage.GetImageInfo(ImageType.Primary, 0);
                }
            }

            if (primaryImage != null)
            {
                try
                {
                    jobItem.PrimaryImageTag    = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Primary);
                    jobItem.PrimaryImageItemId = itemWithImage.Id.ToString("N");
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("Error getting image info", ex);
                }
            }
        }
Пример #11
0
 public string GetTemporaryPath(SyncJobItem jobItem)
 {
     return(Path.Combine(GetTemporaryPath(jobItem.JobId), jobItem.Id));
 }
Пример #12
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);
        }
Пример #13
0
 private async Task <string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
 {
     // TODO: Download
     return(mediaSource.Path);
 }
Пример #14
0
        public async Task EnsureJobItems(SyncJob job)
        {
            var user = _userManager.GetUserById(job.UserId);

            if (user == null)
            {
                throw new InvalidOperationException("Cannot proceed with sync because user no longer exists.");
            }

            var items = (await GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false))
                        .ToList();

            var jobItems = _syncManager.GetJobItems(new SyncJobItemQuery
            {
                JobId       = job.Id,
                AddMetadata = false
            }).Items.ToList();

            foreach (var item in items)
            {
                // Respect ItemLimit, if set
                if (job.ItemLimit.HasValue)
                {
                    if (jobItems.Count(j => j.Status != SyncJobItemStatus.RemovedFromDevice && j.Status != SyncJobItemStatus.Failed) >= job.ItemLimit.Value)
                    {
                        break;
                    }
                }

                var itemId = item.Id.ToString("N");

                var jobItem = jobItems.FirstOrDefault(i => string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase));

                if (jobItem != null)
                {
                    continue;
                }

                var index = jobItems.Count == 0 ?
                            0 :
                            (jobItems.Select(i => i.JobItemIndex).Max() + 1);

                jobItem = new SyncJobItem
                {
                    Id           = Guid.NewGuid().ToString("N"),
                    ItemId       = itemId,
                    ItemName     = GetSyncJobItemName(item),
                    JobId        = job.Id,
                    TargetId     = job.TargetId,
                    DateCreated  = DateTime.UtcNow,
                    JobItemIndex = index
                };

                await _syncRepo.Create(jobItem).ConfigureAwait(false);

                _syncManager.OnSyncJobItemCreated(jobItem);

                jobItems.Add(jobItem);
            }

            jobItems = jobItems
                       .OrderBy(i => i.DateCreated)
                       .ToList();

            await UpdateJobStatus(job, jobItems).ConfigureAwait(false);
        }
Пример #15
0
        public async Task Update(SyncJobItem jobItem)
        {
            if (jobItem == null)
            {
                throw new ArgumentNullException("jobItem");
            }

            await _writeLock.WaitAsync().ConfigureAwait(false);

            IDbTransaction transaction = null;

            try
            {
                transaction = _connection.BeginTransaction();

                var index = 0;

                _saveJobItemCommand.GetParameter(index++).Value = new Guid(jobItem.Id);
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemId;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.ItemName;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.MediaSourceId;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.JobId;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.RequiresConversion;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.OutputPath;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.Status.ToString();
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.TargetId;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.DateCreated;
                _saveJobItemCommand.GetParameter(index++).Value = jobItem.Progress;

                _saveJobItemCommand.Transaction = transaction;

                _saveJobItemCommand.ExecuteNonQuery();

                transaction.Commit();
            }
            catch (OperationCanceledException)
            {
                if (transaction != null)
                {
                    transaction.Rollback();
                }

                throw;
            }
            catch (Exception e)
            {
                _logger.ErrorException("Failed to save record:", e);

                if (transaction != null)
                {
                    transaction.Rollback();
                }

                throw;
            }
            finally
            {
                if (transaction != null)
                {
                    transaction.Dispose();
                }

                _writeLock.Release();
            }
        }
Пример #16
0
        private SyncJobItem GetJobItem(IDataReader reader)
        {
            var info = new SyncJobItem
            {
                Id     = reader.GetGuid(0).ToString("N"),
                ItemId = reader.GetString(1)
            };

            if (!reader.IsDBNull(2))
            {
                info.ItemName = reader.GetString(2);
            }

            if (!reader.IsDBNull(3))
            {
                info.MediaSourceId = reader.GetString(3);
            }

            info.JobId = reader.GetString(4);

            if (!reader.IsDBNull(5))
            {
                info.TemporaryPath = reader.GetString(5);
            }
            if (!reader.IsDBNull(6))
            {
                info.OutputPath = reader.GetString(6);
            }

            if (!reader.IsDBNull(7))
            {
                info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader.GetString(7), true);
            }

            info.TargetId = reader.GetString(8);

            info.DateCreated = reader.GetDateTime(9).ToUniversalTime();

            if (!reader.IsDBNull(10))
            {
                info.Progress = reader.GetDouble(10);
            }

            if (!reader.IsDBNull(11))
            {
                var json = reader.GetString(11);

                if (!string.IsNullOrWhiteSpace(json))
                {
                    info.AdditionalFiles = _json.DeserializeFromString <List <ItemFileInfo> >(json);
                }
            }

            if (!reader.IsDBNull(12))
            {
                var json = reader.GetString(12);

                if (!string.IsNullOrWhiteSpace(json))
                {
                    info.MediaSource = _json.DeserializeFromString <MediaSourceInfo>(json);
                }
            }

            info.IsMarkedForRemoval = reader.GetBoolean(13);
            info.JobItemIndex       = reader.GetInt32(14);

            return(info);
        }
Пример #17
0
 public Task Create(SyncJobItem jobItem)
 {
     return(Update(jobItem));
 }
Пример #18
0
 public Task Update(SyncJobItem jobItem)
 {
     return(InsertOrUpdate(jobItem, _updateJobItemCommand));
 }
Пример #19
0
 public bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate)
 {
     return(false);
 }
Пример #20
0
 public Task Create(SyncJobItem jobItem)
 {
     return(InsertOrUpdate(jobItem, true));
 }
Пример #21
0
        private async Task InsertOrUpdate(SyncJobItem jobItem, bool insert)
        {
            if (jobItem == null)
            {
                throw new ArgumentNullException("jobItem");
            }

            CheckDisposed();

            using (var connection = await CreateConnection().ConfigureAwait(false))
            {
                using (var cmd = connection.CreateCommand())
                {
                    if (insert)
                    {
                        cmd.CommandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @TemporaryPath, @OutputPath, @Status, @TargetId, @DateCreated, @Progress, @AdditionalFiles, @MediaSource, @IsMarkedForRemoval, @JobItemIndex, @ItemDateModifiedTicks)";

                        cmd.Parameters.Add(cmd, "@Id");
                        cmd.Parameters.Add(cmd, "@ItemId");
                        cmd.Parameters.Add(cmd, "@ItemName");
                        cmd.Parameters.Add(cmd, "@MediaSourceId");
                        cmd.Parameters.Add(cmd, "@JobId");
                        cmd.Parameters.Add(cmd, "@TemporaryPath");
                        cmd.Parameters.Add(cmd, "@OutputPath");
                        cmd.Parameters.Add(cmd, "@Status");
                        cmd.Parameters.Add(cmd, "@TargetId");
                        cmd.Parameters.Add(cmd, "@DateCreated");
                        cmd.Parameters.Add(cmd, "@Progress");
                        cmd.Parameters.Add(cmd, "@AdditionalFiles");
                        cmd.Parameters.Add(cmd, "@MediaSource");
                        cmd.Parameters.Add(cmd, "@IsMarkedForRemoval");
                        cmd.Parameters.Add(cmd, "@JobItemIndex");
                        cmd.Parameters.Add(cmd, "@ItemDateModifiedTicks");
                    }
                    else
                    {
                        // cmd
                        cmd.CommandText = "update SyncJobItems set ItemId=@ItemId,ItemName=@ItemName,MediaSourceId=@MediaSourceId,JobId=@JobId,TemporaryPath=@TemporaryPath,OutputPath=@OutputPath,Status=@Status,TargetId=@TargetId,DateCreated=@DateCreated,Progress=@Progress,AdditionalFiles=@AdditionalFiles,MediaSource=@MediaSource,IsMarkedForRemoval=@IsMarkedForRemoval,JobItemIndex=@JobItemIndex,ItemDateModifiedTicks=@ItemDateModifiedTicks where Id=@Id";

                        cmd.Parameters.Add(cmd, "@Id");
                        cmd.Parameters.Add(cmd, "@ItemId");
                        cmd.Parameters.Add(cmd, "@ItemName");
                        cmd.Parameters.Add(cmd, "@MediaSourceId");
                        cmd.Parameters.Add(cmd, "@JobId");
                        cmd.Parameters.Add(cmd, "@TemporaryPath");
                        cmd.Parameters.Add(cmd, "@OutputPath");
                        cmd.Parameters.Add(cmd, "@Status");
                        cmd.Parameters.Add(cmd, "@TargetId");
                        cmd.Parameters.Add(cmd, "@DateCreated");
                        cmd.Parameters.Add(cmd, "@Progress");
                        cmd.Parameters.Add(cmd, "@AdditionalFiles");
                        cmd.Parameters.Add(cmd, "@MediaSource");
                        cmd.Parameters.Add(cmd, "@IsMarkedForRemoval");
                        cmd.Parameters.Add(cmd, "@JobItemIndex");
                        cmd.Parameters.Add(cmd, "@ItemDateModifiedTicks");
                    }

                    IDbTransaction transaction = null;

                    try
                    {
                        transaction = connection.BeginTransaction();

                        var index = 0;

                        cmd.GetParameter(index++).Value = new Guid(jobItem.Id);
                        cmd.GetParameter(index++).Value = jobItem.ItemId;
                        cmd.GetParameter(index++).Value = jobItem.ItemName;
                        cmd.GetParameter(index++).Value = jobItem.MediaSourceId;
                        cmd.GetParameter(index++).Value = jobItem.JobId;
                        cmd.GetParameter(index++).Value = jobItem.TemporaryPath;
                        cmd.GetParameter(index++).Value = jobItem.OutputPath;
                        cmd.GetParameter(index++).Value = jobItem.Status.ToString();
                        cmd.GetParameter(index++).Value = jobItem.TargetId;
                        cmd.GetParameter(index++).Value = jobItem.DateCreated;
                        cmd.GetParameter(index++).Value = jobItem.Progress;
                        cmd.GetParameter(index++).Value = _json.SerializeToString(jobItem.AdditionalFiles);
                        cmd.GetParameter(index++).Value = jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource);
                        cmd.GetParameter(index++).Value = jobItem.IsMarkedForRemoval;
                        cmd.GetParameter(index++).Value = jobItem.JobItemIndex;
                        cmd.GetParameter(index++).Value = jobItem.ItemDateModifiedTicks;

                        cmd.Transaction = transaction;

                        cmd.ExecuteNonQuery();

                        transaction.Commit();
                    }
                    catch (OperationCanceledException)
                    {
                        if (transaction != null)
                        {
                            transaction.Rollback();
                        }

                        throw;
                    }
                    catch (Exception e)
                    {
                        Logger.ErrorException("Failed to save record:", e);

                        if (transaction != null)
                        {
                            transaction.Rollback();
                        }

                        throw;
                    }
                    finally
                    {
                        if (transaction != null)
                        {
                            transaction.Dispose();
                        }
                    }
                }
            }
        }
Пример #22
0
 public Task Update(SyncJobItem jobItem)
 {
     return(InsertOrUpdate(jobItem, false));
 }
Пример #23
0
 public Task Create(SyncJobItem jobItem)
 {
     return(InsertOrUpdate(jobItem, _insertJobItemCommand));
 }
Пример #24
0
        private async Task Sync(SyncJobItem jobItem, Audio item, User user, DeviceProfile profile, IProgress <double> progress, CancellationToken cancellationToken)
        {
            var options = new AudioOptions
            {
                Context      = EncodingContext.Static,
                ItemId       = item.Id.ToString("N"),
                DeviceId     = jobItem.TargetId,
                Profile      = profile,
                MediaSources = item.GetMediaSources(false, user).ToList()
            };

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

            jobItem.MediaSourceId = streamInfo.MediaSourceId;

            if (streamInfo.PlayMethod == PlayMethod.Transcode)
            {
                jobItem.Status             = SyncJobItemStatus.Converting;
                jobItem.RequiresConversion = true;
                await _syncRepo.Update(jobItem).ConfigureAwait(false);

                try
                {
                    jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, profile), progress, cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    jobItem.Status = SyncJobItemStatus.Queued;
                }
                catch (Exception ex)
                {
                    jobItem.Status = SyncJobItemStatus.Failed;
                    _logger.ErrorException("Error during sync transcoding", ex);
                }

                if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                {
                    await _syncRepo.Update(jobItem).ConfigureAwait(false);

                    return;
                }
            }
            else
            {
                jobItem.RequiresConversion = false;

                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.Progress = 50;
            jobItem.Status   = SyncJobItemStatus.Transferring;
            await _syncRepo.Update(jobItem).ConfigureAwait(false);
        }
Пример #25
0
        private async Task InsertOrUpdate(SyncJobItem jobItem, IDbCommand cmd)
        {
            if (jobItem == null)
            {
                throw new ArgumentNullException("jobItem");
            }

            CheckDisposed();

            await WriteLock.WaitAsync().ConfigureAwait(false);

            IDbTransaction transaction = null;

            try
            {
                transaction = _connection.BeginTransaction();

                var index = 0;

                cmd.GetParameter(index++).Value = new Guid(jobItem.Id);
                cmd.GetParameter(index++).Value = jobItem.ItemId;
                cmd.GetParameter(index++).Value = jobItem.ItemName;
                cmd.GetParameter(index++).Value = jobItem.MediaSourceId;
                cmd.GetParameter(index++).Value = jobItem.JobId;
                cmd.GetParameter(index++).Value = jobItem.TemporaryPath;
                cmd.GetParameter(index++).Value = jobItem.OutputPath;
                cmd.GetParameter(index++).Value = jobItem.Status.ToString();
                cmd.GetParameter(index++).Value = jobItem.TargetId;
                cmd.GetParameter(index++).Value = jobItem.DateCreated;
                cmd.GetParameter(index++).Value = jobItem.Progress;
                cmd.GetParameter(index++).Value = _json.SerializeToString(jobItem.AdditionalFiles);
                cmd.GetParameter(index++).Value = jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource);
                cmd.GetParameter(index++).Value = jobItem.IsMarkedForRemoval;
                cmd.GetParameter(index++).Value = jobItem.JobItemIndex;

                cmd.Transaction = transaction;

                cmd.ExecuteNonQuery();

                transaction.Commit();
            }
            catch (OperationCanceledException)
            {
                if (transaction != null)
                {
                    transaction.Rollback();
                }

                throw;
            }
            catch (Exception e)
            {
                Logger.ErrorException("Failed to save record:", e);

                if (transaction != null)
                {
                    transaction.Rollback();
                }

                throw;
            }
            finally
            {
                if (transaction != null)
                {
                    transaction.Dispose();
                }

                WriteLock.Release();
            }
        }
Пример #26
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);
        }
Пример #27
0
        private async Task ProcessJobItem(SyncJob job, SyncJobItem jobItem, bool enableConversion, IProgress <double> progress, CancellationToken cancellationToken)
        {
            var item = _libraryManager.GetItemById(jobItem.ItemId);

            if (item == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                return;
            }

            jobItem.Progress = 0;

            var syncOptions = _config.GetSyncOptions();
            var user        = _userManager.GetUserById(job.UserId);

            if (user == null)
            {
                jobItem.Status = SyncJobItemStatus.Failed;
                _logger.Error("User not found. Cannot complete the sync job.");
                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                return;
            }

            // See if there's already another active job item for the same target
            var existingJobItems = _syncManager.GetJobItems(new SyncJobItemQuery
            {
                AddMetadata = false,
                ItemId      = jobItem.ItemId,
                TargetId    = job.TargetId,
                Statuses    = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
            });

            var duplicateJobItems = existingJobItems.Items
                                    .Where(i => !string.Equals(i.Id, jobItem.Id, StringComparison.OrdinalIgnoreCase))
                                    .ToList();

            if (duplicateJobItems.Count > 0)
            {
                var syncProvider = _syncManager.GetSyncProvider(jobItem, job) as IHasDuplicateCheck;

                if (!duplicateJobItems.Any(i => AllowDuplicateJobItem(syncProvider, i, jobItem)))
                {
                    _logger.Debug("Cancelling sync job item because there is already another active job for the same target.");
                    jobItem.Status = SyncJobItemStatus.Cancelled;
                    await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);

                    return;
                }
            }

            var video = item as Video;

            if (video != null)
            {
                await Sync(jobItem, job, video, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Audio)
            {
                await Sync(jobItem, job, (Audio)item, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
            }

            else if (item is Photo)
            {
                await Sync(jobItem, (Photo)item, cancellationToken).ConfigureAwait(false);
            }

            else
            {
                await SyncGeneric(jobItem, item, cancellationToken).ConfigureAwait(false);
            }
        }
Пример #28
0
        private SyncJobItem GetJobItem(IReadOnlyList <IResultSetValue> reader)
        {
            var info = new SyncJobItem
            {
                Id     = reader[0].ReadGuid().ToString("N"),
                ItemId = reader[1].ToString()
            };

            if (reader[2].SQLiteType != SQLiteType.Null)
            {
                info.ItemName = reader[2].ToString();
            }

            if (reader[3].SQLiteType != SQLiteType.Null)
            {
                info.MediaSourceId = reader[3].ToString();
            }

            info.JobId = reader[4].ToString();

            if (reader[5].SQLiteType != SQLiteType.Null)
            {
                info.TemporaryPath = reader[5].ToString();
            }
            if (reader[6].SQLiteType != SQLiteType.Null)
            {
                info.OutputPath = reader[6].ToString();
            }

            if (reader[7].SQLiteType != SQLiteType.Null)
            {
                info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader[7].ToString(), true);
            }

            info.TargetId = reader[8].ToString();

            info.DateCreated = reader[9].ReadDateTime();

            if (reader[10].SQLiteType != SQLiteType.Null)
            {
                info.Progress = reader[10].ToDouble();
            }

            if (reader[11].SQLiteType != SQLiteType.Null)
            {
                var json = reader[11].ToString();

                if (!string.IsNullOrWhiteSpace(json))
                {
                    info.AdditionalFiles = _json.DeserializeFromString <List <ItemFileInfo> >(json);
                }
            }

            if (reader[12].SQLiteType != SQLiteType.Null)
            {
                var json = reader[12].ToString();

                if (!string.IsNullOrWhiteSpace(json))
                {
                    info.MediaSource = _json.DeserializeFromString <MediaSourceInfo>(json);
                }
            }

            info.IsMarkedForRemoval = reader[13].ToBool();
            info.JobItemIndex       = reader[14].ToInt();

            if (reader[15].SQLiteType != SQLiteType.Null)
            {
                info.ItemDateModifiedTicks = reader[15].ToInt64();
            }

            return(info);
        }
Пример #29
0
        private SyncedItem GetJobItemInfo(SyncJobItem jobItem)
        {
            var job = _repo.GetJob(jobItem.JobId);

            if (job == null)
            {
                _logger.Error("GetJobItemInfo job id {0} no longer exists", jobItem.JobId);
                return(null);
            }

            var libraryItem = _libraryManager.GetItemById(jobItem.ItemId);

            if (libraryItem == null)
            {
                _logger.Error("GetJobItemInfo library item with id {0} no longer exists", jobItem.ItemId);
                return(null);
            }

            var syncedItem = new SyncedItem
            {
                SyncJobId          = jobItem.JobId,
                SyncJobItemId      = jobItem.Id,
                ServerId           = _appHost.SystemId,
                UserId             = job.UserId,
                SyncJobName        = job.Name,
                SyncJobDateCreated = job.DateCreated,
                AdditionalFiles    = jobItem.AdditionalFiles.Select(i => new ItemFileInfo
                {
                    ImageType = i.ImageType,
                    Name      = i.Name,
                    Type      = i.Type,
                    Index     = i.Index
                }).ToList()
            };

            var dtoOptions = new DtoOptions();

            // Remove some bloat
            dtoOptions.Fields.Remove(ItemFields.MediaStreams);
            dtoOptions.Fields.Remove(ItemFields.IndexOptions);
            dtoOptions.Fields.Remove(ItemFields.MediaSourceCount);
            dtoOptions.Fields.Remove(ItemFields.Path);
            dtoOptions.Fields.Remove(ItemFields.SeriesGenres);
            dtoOptions.Fields.Remove(ItemFields.Settings);
            dtoOptions.Fields.Remove(ItemFields.SyncInfo);
            dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);

            syncedItem.Item = _dtoService().GetBaseItemDto(libraryItem, dtoOptions);

            var mediaSource = jobItem.MediaSource;

            syncedItem.Item.MediaSources = new List <MediaSourceInfo>();

            syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path);
            if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName))
            {
                syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path);
            }

            // This will be null for items that are not audio/video
            if (mediaSource != null)
            {
                syncedItem.OriginalFileName = Path.ChangeExtension(syncedItem.OriginalFileName, Path.GetExtension(mediaSource.Path));
                syncedItem.Item.MediaSources.Add(mediaSource);
            }
            if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName))
            {
                syncedItem.OriginalFileName = libraryItem.Name;
            }

            return(syncedItem);
        }
Пример #30
0
        private bool AllowDuplicateJobItem(IHasDuplicateCheck provider, SyncJobItem original, SyncJobItem duplicate)
        {
            if (provider != null)
            {
                return(provider.AllowDuplicateJobItem(original, duplicate));
            }

            return(true);
        }