protected Validation <BaseError, ProgramSchedule> CollectionTypeMustBeValid(
        IProgramScheduleItemRequest item,
        ProgramSchedule programSchedule)
    {
        switch (item.CollectionType)
        {
        case ProgramScheduleItemCollectionType.Collection:
            if (item.CollectionId is null)
            {
                return(BaseError.New("[Collection] is required for collection type 'Collection'"));
            }

            break;

        case ProgramScheduleItemCollectionType.TelevisionShow:
            if (item.MediaItemId is null)
            {
                return(BaseError.New("[MediaItem] is required for collection type 'TelevisionShow'"));
            }

            break;

        case ProgramScheduleItemCollectionType.TelevisionSeason:
            if (item.MediaItemId is null)
            {
                return(BaseError.New("[MediaItem] is required for collection type 'TelevisionSeason'"));
            }

            break;

        case ProgramScheduleItemCollectionType.Artist:
            if (item.MediaItemId is null)
            {
                return(BaseError.New("[MediaItem] is required for collection type 'Artist'"));
            }

            break;

        case ProgramScheduleItemCollectionType.MultiCollection:
            if (item.MultiCollectionId is null)
            {
                return(BaseError.New("[MultiCollection] is required for collection type 'MultiCollection'"));
            }

            break;

        case ProgramScheduleItemCollectionType.SmartCollection:
            if (item.SmartCollectionId is null)
            {
                return(BaseError.New("[SmartCollection] is required for collection type 'SmartCollection'"));
            }

            break;

        default:
            return(BaseError.New("[CollectionType] is invalid"));
        }

        return(programSchedule);
    }
예제 #2
0
    public async Task <Either <BaseError, List <JellyfinMovie> > > GetMovieLibraryItems(
        string address,
        string apiKey,
        int mediaSourceId,
        string libraryId)
    {
        try
        {
            if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{mediaSourceId}", out string userId))
            {
                IJellyfinApi service = RestService.For <IJellyfinApi>(address);
                JellyfinLibraryItemsResponse items = await service.GetMovieLibraryItems(apiKey, userId, libraryId);

                return(items.Items
                       .Map(ProjectToMovie)
                       .Somes()
                       .ToList());
            }

            return(BaseError.New("Jellyfin admin user id is not available"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting jellyfin movie library items");
            return(BaseError.New(ex.Message));
        }
    }
    protected static async Task <Either <BaseError, ProgramSchedule> > FillerConfigurationMustBeValid(
        TvContext dbContext,
        IProgramScheduleItemRequest item,
        ProgramSchedule programSchedule)
    {
        var allFillerIds = Optional(item.PreRollFillerId)
                           .Append(Optional(item.MidRollFillerId))
                           .Append(Optional(item.PostRollFillerId))
                           .ToList();

        List <FillerPreset> allFiller = await dbContext.FillerPresets
                                        .Filter(fp => allFillerIds.Contains(fp.Id))
                                        .ToListAsync();

        if (allFiller.Count(f => f.PadToNearestMinute.HasValue) > 1)
        {
            return(BaseError.New("Schedule may only contain one filler preset that is configured to pad"));
        }

        if (allFiller.Any(fp => fp.PadToNearestMinute.HasValue) && !item.FallbackFillerId.HasValue)
        {
            return(BaseError.New("Fallback filler is required when padding"));
        }

        return(programSchedule);
    }
예제 #4
0
    public async Task <Either <BaseError, List <JellyfinCollection> > > GetCollectionLibraryItems(
        string address,
        string apiKey,
        int mediaSourceId)
    {
        try
        {
            if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{mediaSourceId}", out string userId))
            {
                // TODO: should we enumerate collection libraries here?

                if (_memoryCache.TryGetValue("jellyfin_collections_library_item_id", out string itemId))
                {
                    IJellyfinApi service = RestService.For <IJellyfinApi>(address);
                    JellyfinLibraryItemsResponse items =
                        await service.GetCollectionLibraryItems(apiKey, userId, itemId);

                    return(items.Items
                           .Map(ProjectToCollection)
                           .Somes()
                           .ToList());
                }

                return(BaseError.New("Jellyfin collection item id is not available"));
            }

            return(BaseError.New("Jellyfin admin user id is not available"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting jellyfin collection library items");
            return(BaseError.New(ex.Message));
        }
    }
예제 #5
0
    private async Task <Either <BaseError, MediaItemScanResult <Song> > > UpdateMetadata(
        MediaItemScanResult <Song> result,
        string ffprobePath)
    {
        try
        {
            Song   song = result.Item;
            string path = song.GetHeadVersion().MediaFiles.Head().Path;

            bool shouldUpdate = Optional(song.SongMetadata).Flatten().HeadOrNone().Match(
                m => m.MetadataKind == MetadataKind.Fallback ||
                m.DateUpdated != _localFileSystem.GetLastWriteTime(path),
                true);

            if (shouldUpdate)
            {
                song.SongMetadata ??= new List <SongMetadata>();

                _logger.LogDebug("Refreshing {Attribute} for {Path}", "Metadata", path);
                if (await _localMetadataProvider.RefreshTagMetadata(song, ffprobePath))
                {
                    result.IsUpdated = true;
                }
            }

            return(result);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #6
0
        private static async Task <Either <BaseError, Season> > AddSeason(
            TvContext dbContext,
            Show show,
            int libraryPathId,
            int seasonNumber)
        {
            try
            {
                var season = new Season
                {
                    LibraryPathId  = libraryPathId,
                    ShowId         = show.Id,
                    SeasonNumber   = seasonNumber,
                    Episodes       = new List <Episode>(),
                    SeasonMetadata = new List <SeasonMetadata>
                    {
                        new()
                        {
                            DateAdded = DateTime.UtcNow
                        }
                    }
                };
                await dbContext.Seasons.AddAsync(season);

                await dbContext.SaveChangesAsync();

                return(season);
            }
            catch (Exception ex)
            {
                return(BaseError.New(ex.Message));
            }
        }
    private async Task <Either <BaseError, Episode> > UpdateThumbnail(Episode episode, CancellationToken cancellationToken)
    {
        try
        {
            Option <string> maybeThumbnail = LocateThumbnail(episode);
            foreach (string thumbnailFile in maybeThumbnail)
            {
                foreach (EpisodeMetadata metadata in episode.EpisodeMetadata)
                {
                    await RefreshArtwork(
                        thumbnailFile,
                        metadata,
                        ArtworkKind.Thumbnail,
                        None,
                        None,
                        cancellationToken);
                }
            }

            return(episode);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #8
0
        private Task <Either <BaseError, FFprobe> > GetProbeOutput(string ffprobePath, string filePath)
        {
            var startInfo = new ProcessStartInfo
            {
                FileName = ffprobePath,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                UseShellExecute        = false
            };

            startInfo.ArgumentList.Add("-v");
            startInfo.ArgumentList.Add("quiet");
            startInfo.ArgumentList.Add("-print_format");
            startInfo.ArgumentList.Add("json");
            startInfo.ArgumentList.Add("-show_format");
            startInfo.ArgumentList.Add("-show_streams");
            startInfo.ArgumentList.Add("-i");
            startInfo.ArgumentList.Add(filePath);

            var probe = new Process
            {
                StartInfo = startInfo
            };

            probe.Start();
            return(probe.StandardOutput.ReadToEndAsync().MapAsync <string, Either <BaseError, FFprobe> >(
                       async output =>
            {
                await probe.WaitForExitAsync();
                return probe.ExitCode == 0
                        ? JsonConvert.DeserializeObject <FFprobe>(output)
                        : BaseError.New($"FFprobe at {ffprobePath} exited with code {probe.ExitCode}");
            }));
        }
예제 #9
0
    public async Task <Either <BaseError, List <EmbyCollection> > > GetCollectionLibraryItems(string address, string apiKey)
    {
        try
        {
            // TODO: should we enumerate collection libraries here?

            if (_memoryCache.TryGetValue("emby_collections_library_item_id", out string itemId))
            {
                IEmbyApi service = RestService.For <IEmbyApi>(address);
                EmbyLibraryItemsResponse items = await service.GetCollectionLibraryItems(apiKey, itemId);

                return(items.Items
                       .Map(ProjectToCollection)
                       .Somes()
                       .ToList());
            }

            return(BaseError.New("Emby collection item id is not available"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting Emby collection library items");
            return(BaseError.New(ex.Message));
        }
    }
예제 #10
0
    public async Task <Either <BaseError, List <PlexLibrary> > > GetLibraries(
        PlexConnection connection,
        PlexServerAuthToken token)
    {
        try
        {
            IPlexServerApi service = RestService.For <IPlexServerApi>(
                new HttpClient
            {
                BaseAddress = new Uri(connection.Uri),
                Timeout     = TimeSpan.FromSeconds(10)
            });
            List <PlexLibraryResponse> directory =
                await service.GetLibraries(token.AuthToken).Map(r => r.MediaContainer.Directory);

            return(directory
                   // .Filter(l => l.Hidden == 0)
                   .Filter(l => l.Type.ToLowerInvariant() is "movie" or "show")
                   .Map(Project)
                   .Somes()
                   .ToList());
        }
        catch (Exception ex)
        {
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #11
0
        public async Task <Either <BaseError, bool> > RefreshStatistics(string ffprobePath, MediaItem mediaItem)
        {
            try
            {
                string filePath = mediaItem switch
                {
                    Movie m => m.MediaVersions.Head().MediaFiles.Head().Path,
                    Episode e => e.MediaVersions.Head().MediaFiles.Head().Path,
                       _ => throw new ArgumentOutOfRangeException(nameof(mediaItem))
                };

                Either <BaseError, FFprobe> maybeProbe = await GetProbeOutput(ffprobePath, filePath);

                return(await maybeProbe.Match(
                           async ffprobe =>
                {
                    MediaVersion version = ProjectToMediaVersion(ffprobe);
                    bool result = await ApplyVersionUpdate(mediaItem, version, filePath);
                    return Right <BaseError, bool>(result);
                },
                           error => Task.FromResult(Left <BaseError, bool>(error))));
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id);
                return(BaseError.New(ex.Message));
            }
        }
예제 #12
0
    public async Task <Either <BaseError, Tuple <EpisodeMetadata, MediaVersion> > > GetEpisodeMetadataAndStatistics(
        PlexLibrary library,
        string key,
        PlexConnection connection,
        PlexServerAuthToken token)
    {
        try
        {
            IPlexServerApi service = XmlServiceFor(connection.Uri);
            Option <PlexXmlVideoMetadataResponseContainer> maybeResponse = await service
                                                                           .GetVideoMetadata(key, token.AuthToken)
                                                                           .Map(Optional)
                                                                           .Map(r => r.Filter(m => m.Metadata.Media.Count > 0 && m.Metadata.Media[0].Part.Count > 0));

            return(maybeResponse.Match(
                       response =>
            {
                Option <MediaVersion> maybeVersion = ProjectToMediaVersion(response.Metadata);
                return maybeVersion.Match <Either <BaseError, Tuple <EpisodeMetadata, MediaVersion> > >(
                    version => Tuple(
                        ProjectToEpisodeMetadata(response.Metadata, library.MediaSourceId),
                        version),
                    () => BaseError.New("Unable to locate metadata"));
            },
                       () => BaseError.New("Unable to locate metadata")));
        }
        catch (Exception ex)
        {
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #13
0
        private async Task <Either <BaseError, MediaItemScanResult <PlexMovie> > > AddPlexMovie(
            TvContext context,
            PlexLibrary library,
            PlexMovie item)
        {
            try
            {
                item.LibraryPathId = library.Paths.Head().Id;

                await context.PlexMovies.AddAsync(item);

                await context.SaveChangesAsync();

                await context.Entry(item).Reference(i => i.LibraryPath).LoadAsync();

                await context.Entry(item.LibraryPath).Reference(lp => lp.Library).LoadAsync();

                return(new MediaItemScanResult <PlexMovie>(item)
                {
                    IsAdded = true
                });
            }
            catch (Exception ex)
            {
                return(BaseError.New(ex.ToString()));
            }
        }
예제 #14
0
    private async Task <Either <BaseError, MediaItemScanResult <OtherVideo> > > UpdateMetadata(
        MediaItemScanResult <OtherVideo> result)
    {
        try
        {
            OtherVideo otherVideo = result.Item;
            if (!Optional(otherVideo.OtherVideoMetadata).Flatten().Any())
            {
                otherVideo.OtherVideoMetadata ??= new List <OtherVideoMetadata>();

                string path = otherVideo.MediaVersions.Head().MediaFiles.Head().Path;
                _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path);
                if (await _localMetadataProvider.RefreshFallbackMetadata(otherVideo))
                {
                    result.IsUpdated = true;
                }
            }

            return(result);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #15
0
    public async Task <Either <BaseError, TraktList> > GetUserList(string user, string list)
    {
        try
        {
            TraktListResponse response = await JsonService().GetUserList(
                _traktConfiguration.Value.ClientId,
                user,
                list);

            return(new TraktList
            {
                TraktId = response.Ids.Trakt,
                User = response.User.Username,
                List = response.Ids.Slug,
                Name = response.Name,
                Description = response.Description,
                ItemCount = response.ItemCount,
                Items = new List <TraktListItem>()
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting trakt list");
            return(BaseError.New(ex.Message));
        }
    }
예제 #16
0
    public async Task <Either <BaseError, bool> > RefreshStatistics(
        string ffmpegPath,
        string ffprobePath,
        MediaItem mediaItem,
        string mediaItemPath)
    {
        try
        {
            Either <BaseError, FFprobe> maybeProbe = await GetProbeOutput(ffprobePath, mediaItemPath);

            return(await maybeProbe.Match(
                       async ffprobe =>
            {
                MediaVersion version = ProjectToMediaVersion(mediaItemPath, ffprobe);
                if (version.Duration.TotalSeconds < 1)
                {
                    await AnalyzeDuration(ffmpegPath, mediaItemPath, version);
                }

                bool result = await ApplyVersionUpdate(mediaItem, version, mediaItemPath);
                return Right <BaseError, bool>(result);
            },
                       error => Task.FromResult(Left <BaseError, bool>(error))));
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to refresh statistics for media item {Id}", mediaItem.Id);
            _client.Notify(ex);
            return(BaseError.New(ex.Message));
        }
    }
예제 #17
0
        private static async Task CreateResponseAsync(HttpContext context, BaseError error)
        {
            error.ActivityId = context.TraceIdentifier;
            var response = JsonConvert.SerializeObject(new ErrorResponse <BaseError>(error));

            context.Response.StatusCode = error.ErrorCode;
            await context.Response.WriteAsync(response);
        }
    protected static Validation <BaseError, ProgramSchedule> PlayoutModeMustBeValid(
        IProgramScheduleItemRequest item,
        ProgramSchedule programSchedule)
    {
        if (item.MultiCollectionId.HasValue)
        {
            switch (item.PlaybackOrder)
            {
            case PlaybackOrder.Chronological:
            case PlaybackOrder.Random:
                return(BaseError.New($"Invalid playback order for multi collection: '{item.PlaybackOrder}'"));

            case PlaybackOrder.Shuffle:
            case PlaybackOrder.ShuffleInOrder:
                break;
            }
        }

        switch (item.PlayoutMode)
        {
        case PlayoutMode.Flood:
        case PlayoutMode.One:
            break;

        case PlayoutMode.Multiple:
            if (item.MultipleCount.GetValueOrDefault() < 0)
            {
                return(BaseError.New(
                           "[MultipleCount] must be greater than or equal to 0 for playout mode 'multiple'"));
            }

            break;

        case PlayoutMode.Duration:
            if (item.PlayoutDuration is null)
            {
                return(BaseError.New("[PlayoutDuration] is required for playout mode 'duration'"));
            }

            if (item.TailMode == TailMode.Filler && item.TailFillerId == null)
            {
                return(BaseError.New("Tail Filler is required with tail mode Filler"));
            }

            if (item.TailFillerId != null && item.TailMode != TailMode.Filler)
            {
                return(BaseError.New("Tail Filler will not be used unless tail mode is set to Filler"));
            }

            break;

        default:
            return(BaseError.New("[PlayoutMode] is invalid"));
        }

        return(programSchedule);
    }
        /// <inheritdoc/>
        public virtual BaseError Handle(Exception exception)
        {
            if (!CanHandle(exception))
            {
                throw new NotSupportedException($"Cannot handle exception of type {exception.GetType()}");
            }

            BaseError error = HandleException(exception as TException);

            return(error);
        }
예제 #20
0
 private async Task <Either <BaseError, bool> > UpdateSubtitles(EmbyEpisode episode, string localPath)
 {
     try
     {
         return(await _localSubtitlesProvider.UpdateSubtitles(episode, localPath, false));
     }
     catch (Exception ex)
     {
         return(BaseError.New(ex.ToString()));
     }
 }
예제 #21
0
 /// <summary>
 /// </summary>
 /// <param name="baseError"></param>
 /// <returns></returns>
 public Task <HttpResponseMessage> CreateErrorResponse(BaseError baseError)
 {
     ResponseMessage = Request.CreateResponse(
         new
     {
         Status       = DefaultResponseStatus.Failure.ToString(),
         ErrorMessage = new BaseErrorResponse((int)baseError.HttpStatusCode, baseError.Message),
     });
     ResponseMessage.StatusCode = baseError.HttpStatusCode;
     return(Task.FromResult(ResponseMessage));
 }
예제 #22
0
 public async Task <Either <BaseError, string> > GetLatestReleaseNotes(CancellationToken cancellationToken)
 {
     try
     {
         IGitHubApi service = RestService.For <IGitHubApi>("https://api.github.com");
         return(await service.GetReleases(cancellationToken).Map(releases => releases.Head().Body));
     }
     catch (Exception ex)
     {
         return(BaseError.New(ex.ToString()));
     }
 }
        public ProceesResultErrorType GetErrorType(BaseError error)
        {
            KeyValuePair <Type, ProceesResultErrorType>?err = ERROR2ENUM_DIC.FirstOrDefault(x => x.Key == error.GetType());

            if (err == null)
            {
                return(ProceesResultErrorType.Unknown);
            }
            else
            {
                return(err.Value.Value);
            }
        }
예제 #24
0
 public static CustomActionResult GetErrorResponse(BaseError error, string uri, string message)
 {
     return(new CustomActionResult
     {
         Response = new ErrorResponse
         {
             Errors = new List <BaseError> {
                 error
             },
             Uri = uri,
             Message = message,
         }
     });
 }
예제 #25
0
    private async Task <Either <BaseError, MediaItemScanResult <Movie> > > UpdateSubtitles(MediaItemScanResult <Movie> result)
    {
        try
        {
            await _localSubtitlesProvider.UpdateSubtitles(result.Item, None, true);

            return(result);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #26
0
    private async Task <Either <BaseError, Episode> > UpdateSubtitles(Episode episode)
    {
        try
        {
            await _localSubtitlesProvider.UpdateSubtitles(episode, None, true);

            return(episode);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #27
0
    private async Task <Either <BaseError, MediaItemScanResult <MusicVideo> > > UpdateMetadata(
        MediaItemScanResult <MusicVideo> result)
    {
        try
        {
            MusicVideo musicVideo = result.Item;

            Option <string> maybeNfoFile = LocateNfoFile(musicVideo);
            if (maybeNfoFile.IsNone)
            {
                if (!Optional(musicVideo.MusicVideoMetadata).Flatten().Any())
                {
                    musicVideo.MusicVideoMetadata ??= new List <MusicVideoMetadata>();

                    string path = musicVideo.MediaVersions.Head().MediaFiles.Head().Path;
                    _logger.LogDebug("Refreshing {Attribute} for {Path}", "Fallback Metadata", path);
                    if (await _localMetadataProvider.RefreshFallbackMetadata(musicVideo))
                    {
                        result.IsUpdated = true;
                    }
                }
            }

            foreach (string nfoFile in maybeNfoFile)
            {
                bool shouldUpdate = Optional(musicVideo.MusicVideoMetadata).Flatten().HeadOrNone().Match(
                    m => m.MetadataKind == MetadataKind.Fallback ||
                    m.DateUpdated != _localFileSystem.GetLastWriteTime(nfoFile),
                    true);

                if (shouldUpdate)
                {
                    _logger.LogDebug("Refreshing {Attribute} from {Path}", "Sidecar Metadata", nfoFile);
                    if (await _localMetadataProvider.RefreshSidecarMetadata(musicVideo, nfoFile))
                    {
                        result.IsUpdated = true;
                    }
                }
            }

            return(result);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }
예제 #28
0
    public async Task <Either <BaseError, PlexAuthPin> > StartPinFlow()
    {
        try
        {
            string clientIdentifier = await _plexSecretStore.GetClientIdentifier();

            PlexPinResponse pinResponse = await _plexTvApi.StartPinFlow(AppName, clientIdentifier);

            return(new PlexAuthPin(pinResponse.Id, pinResponse.Code, clientIdentifier));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error starting plex pin flow");
            return(BaseError.New(ex.Message));
        }
    }
예제 #29
0
        public static async Task <BaseResult> SendEmail(string email, string subject, string emailTemplate)
        {
            var result = new BaseResult();
            //ToDo: Template logic!!!
            MailAddress addr;

            try
            {
                addr = new MailAddress(email);
            }
            catch (Exception e)
            {
                result.ResultStatus = false;

                var error = new BaseError()
                {
                    StatusCode       = null,
                    ErrorDescription = "Faild Email address!",
                    ExceptionMessage = e.Message
                };

                result.Errors.Add(error);

                return(result);
            }

            var status = await SendEmailCore(email, subject, emailTemplate);

            if (status != HttpStatusCode.Accepted)
            {
                result.ResultStatus = false;

                var error = new BaseError
                {
                    StatusCode       = status,
                    ErrorDescription = "Check SendEmailCore, Send fails",
                    ExceptionMessage = null
                };
                result.Errors.Add(error);

                return(result);
            }

            return(new BaseResult {
                ResultStatus = true
            });
        }
예제 #30
0
    private async Task <Either <BaseError, MediaItemScanResult <Song> > > UpdateThumbnail(
        MediaItemScanResult <Song> result,
        string ffmpegPath,
        CancellationToken cancellationToken)
    {
        try
        {
            // reload the song from the database at this point
            if (result.IsAdded)
            {
                LibraryPath libraryPath = result.Item.LibraryPath;
                string      path        = result.Item.GetHeadVersion().MediaFiles.Head().Path;
                foreach (MediaItemScanResult <Song> s in (await _songRepository.GetOrAdd(libraryPath, path))
                         .RightToSeq())
                {
                    result.Item = s.Item;
                }
            }

            Song            song           = result.Item;
            Option <string> maybeThumbnail = LocateThumbnail(song);
            if (maybeThumbnail.IsNone)
            {
                await ExtractEmbeddedArtwork(song, ffmpegPath, cancellationToken);
            }


            foreach (string thumbnailFile in maybeThumbnail)
            {
                SongMetadata metadata = song.SongMetadata.Head();
                await RefreshArtwork(
                    thumbnailFile,
                    metadata,
                    ArtworkKind.Thumbnail,
                    ffmpegPath,
                    None,
                    cancellationToken);
            }

            return(result);
        }
        catch (Exception ex)
        {
            _client.Notify(ex);
            return(BaseError.New(ex.ToString()));
        }
    }