Esempio n. 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Notifier"/> class.
        /// </summary>
        /// <param name="logger">Instance of the <see cref="ILogger{Notifier}"/> interface.</param>
        /// <param name="serverConfiguration">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
        /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
        /// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
        /// <param name="userViewManager">Instance of the <see cref="IUserViewManager"/> interface.</param>
        /// <param name="httpClient">Instance of the <see cref="IHttpClient"/> interface.</param>
        /// <param name="applicationHost">Instance of the <see cref="IApplicationHost"/> interface.</param>
        public Notifier(
            ILogger <Notifier> logger,
            IServerConfigurationManager serverConfiguration,
            ILibraryManager libraryManager,
            ILocalizationManager localizationManager,
            IUserViewManager userViewManager,
            IHttpClient httpClient,
            IApplicationHost applicationHost)
        {
            _logger = logger;
            _serverConfiguration   = serverConfiguration;
            _libraryManager        = libraryManager;
            _localizationManager   = localizationManager;
            _userViewManager       = userViewManager;
            _httpClient            = httpClient;
            _applicationHost       = applicationHost;
            _jsonSerializerOptions = JsonDefaults.GetOptions();
            _jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

            _queuedMessageHandler           = new System.Timers.Timer(Constants.MessageQueueSendInterval);
            _queuedMessageHandler.AutoReset = true;
            _queuedMessageHandler.Elapsed  += QueuedMessageSender;
            _queuedMessageHandler.Start();

            _queuedUpdateHandler           = new System.Timers.Timer(Constants.RecheckIntervalMs);
            _queuedUpdateHandler.AutoReset = true;
            _queuedUpdateHandler.Elapsed  += CheckForMetadata;
            _queuedUpdateHandler.Start();

            _libraryManager.ItemAdded += ItemAddHandler;
            _logger.LogDebug("Registered ItemAdd handler");
        }
Esempio n. 2
0
        public PluginManager(
            ILogger <PluginManager> logger,
            IApplicationHost appHost,
            ServerConfiguration config,
            string pluginsPath,
            Version appVersion)
        {
            _logger      = logger ?? throw new ArgumentNullException(nameof(logger));
            _pluginsPath = pluginsPath;
            _appVersion  = appVersion ?? throw new ArgumentNullException(nameof(appVersion));
            _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions())
            {
                WriteIndented = true
            };

            // We need to use the default GUID converter, so we need to remove any custom ones.
            for (int a = _jsonOptions.Converters.Count - 1; a >= 0; a--)
            {
                if (_jsonOptions.Converters[a] is JsonGuidConverter convertor)
                {
                    _jsonOptions.Converters.Remove(convertor);
                    break;
                }
            }

            _config         = config;
            _appHost        = appHost;
            _minimumVersion = new Version(0, 0, 0, 1);
            _plugins        = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List <LocalPlugin>();
        }
 public KodiSyncQueueController(ILogger <KodiSyncQueueController> logger, IUserManager userManager, ILibraryManager libraryManager)
 {
     _logger                = logger;
     _userManager           = userManager;
     _libraryManager        = libraryManager;
     _jsonSerializerOptions = JsonDefaults.GetOptions();
 }
 public DbRepo(string dPath, ILogger <DbRepo> logger)
 {
     _logger = logger;
     _logger.LogInformation("Creating DB Repository...");
     Directory.CreateDirectory(dPath);
     _liteDb = new LiteDatabase($"filename={dPath}/kodisyncqueue.db;mode=exclusive");
     _jsonSerializerOptions = JsonDefaults.GetOptions();
 }
Esempio n. 5
0
 public NotificationController(
     ILogger <NotificationController> logger,
     IHttpClientFactory httpClientFactory)
 {
     _logger                = logger;
     _httpClientFactory     = httpClientFactory;
     _jsonSerializerOptions = JsonDefaults.GetOptions();
 }
        public static async Task <T> DeserializeFromHttp <T>(HttpResponseMessage response)
        {
            var contentStream = await response.Content.ReadAsStreamAsync();

            var result = await JsonSerializer.DeserializeAsync <T>(contentStream, JsonDefaults.GetOptions());

            return(result);
        }
Esempio n. 7
0
        public SqliteDisplayPreferencesRepository(ILogger <SqliteDisplayPreferencesRepository> logger, IApplicationPaths appPaths, IFileSystem fileSystem)
            : base(logger)
        {
            _fileSystem = fileSystem;

            _jsonOptions = JsonDefaults.GetOptions();

            DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
        }
Esempio n. 8
0
        public SqliteUserRepository(
            ILogger <SqliteUserRepository> logger,
            IServerApplicationPaths appPaths)
            : base(logger)
        {
            _jsonOptions = JsonDefaults.GetOptions();

            DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
        }
Esempio n. 9
0
        public Bf4Client()
        {
            _httpClient = new HttpClient()
            {
                BaseAddress = new Uri("https://battlelog.battlefield.com")
            };

            _jsonOptions = JsonDefaults.GetOptions();
        }
Esempio n. 10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PluginsController"/> class.
 /// </summary>
 /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
 /// <param name="pluginManager">Instance of the <see cref="IPluginManager"/> interface.</param>
 /// <param name="config">Instance of the <see cref="IConfigurationManager"/> interface.</param>
 public PluginsController(
     IInstallationManager installationManager,
     IPluginManager pluginManager,
     IConfigurationManager config)
 {
     _installationManager = installationManager;
     _pluginManager       = pluginManager;
     _serializerOptions   = JsonDefaults.GetOptions();
     _config = config;
 }
Esempio n. 11
0
        public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
        {
            _httpClientFactory    = httpClientFactory;
            _fileSystem           = fileSystem;
            _configurationManager = configurationManager;
            _appHost = appHost;

            _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions());
            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableInt32Converter());
        }
Esempio n. 12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="NotificationsService"/> class.
 /// </summary>
 /// <param name="logger">Instance of the <see cref="ILogger{NoticiationsService}"/> interface.</param>
 /// <param name="serverConfiguration">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 /// <param name="httpClient">Instance of the <see cref="IHttpClient"/> interface.</param>
 public NotificationsService(
     ILogger <NotificationsService> logger,
     IServerConfigurationManager serverConfiguration,
     IHttpClient httpClient)
 {
     _logger = logger;
     _serverConfiguration   = serverConfiguration;
     _httpClient            = httpClient;
     _jsonSerializerOptions = JsonDefaults.GetOptions();
     _jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
 }
Esempio n. 13
0
        public async Task Format(Uri url, EventInfo info)
        {
            var body = new PlexFormatPayload
            {
                @event  = GetEventName(info.Event),
                user    = true,
                owner   = info.User.HasPermission(Data.Enums.PermissionKind.IsAdministrator),
                Account = new
                {
                    id    = info.User.Id,
                    title = info.User.Username
                },
                Server = new
                {
                    title = info.Server.Name,
                    uuid  = info.Server.Id
                },
                Player = new
                {
                    local         = true,
                    publicAddress = info.Session?.RemoteEndPoint,
                    title         = info.Session?.DeviceName,
                    uuid          = info.Session?.Id,
                },
                Metadata = new
                {
                    librarySectionType = GetSectionType(info.Item),
                    guid             = GetGuid(info.Item),
                    title            = info.Item.Name,
                    type             = GetMediaType(info.Item),
                    parentTitle      = info.Item.DisplayParent.Name,
                    grandparentTitle = info.Item.DisplayParent.DisplayParent.Name,
                    addedAt          = ((DateTimeOffset)info.Item.DateCreated).ToUnixTimeSeconds(),
                    updatedAt        = ((DateTimeOffset)info.Item.DateModified).ToUnixTimeSeconds(),
                    year             = info.Item.ProductionYear,
                    duration         = info.Item.RunTimeTicks / 1000,
                }
            };
            var content = JsonSerializer.Serialize(body, JsonDefaults.GetOptions());
            var form    = new MultipartFormDataContent
            {
                { new StringContent(content), "payload" }
            };

            using (var httpClient = new HttpClient())
            {
                var response = await httpClient.PostAsync(url, form);

                response.EnsureSuccessStatusCode();
                var status = await response.Content.ReadAsStringAsync();

                // status from SIMKL can be "OK" or "FAIL"
            }
        }
Esempio n. 14
0
        public WebSocketConnection(
            ILogger <WebSocketConnection> logger,
            WebSocket socket,
            IPAddress?remoteEndPoint,
            IQueryCollection query)
        {
            _logger        = logger;
            _socket        = socket;
            RemoteEndPoint = remoteEndPoint;
            QueryString    = query;

            _jsonOptions     = JsonDefaults.GetOptions();
            LastActivityDate = DateTime.Now;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="OmdbItemProvider"/> class.
        /// </summary>
        /// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
        /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
        /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
        /// <param name="configurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
        public OmdbItemProvider(
            IHttpClientFactory httpClientFactory,
            ILibraryManager libraryManager,
            IFileSystem fileSystem,
            IServerConfigurationManager configurationManager)
        {
            _httpClientFactory    = httpClientFactory;
            _libraryManager       = libraryManager;
            _fileSystem           = fileSystem;
            _configurationManager = configurationManager;

            _jsonOptions = new JsonSerializerOptions(JsonDefaults.GetOptions());
            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
            _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStructConverter <int>());
        }
        private async Task <BookResult> FetchBookData(string googleBookId, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var url = string.Format(GoogleApiUrls.DetailsUrl, googleBookId);

            var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);

            using (var response = await httpClient.GetAsync(url).ConfigureAwait(false))
            {
                await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

                return(await JsonSerializer.DeserializeAsync <BookResult>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false));
            }
        }
Esempio n. 17
0
        public async Task Format(Uri url, EventInfo info)
        {
            var item = _dto.GetBaseItemDto(info.Item, new DtoOptions(true), info.User);
            var user = _users.GetUserDto(info.User);
            var body = new DefaultFormatPayload
            {
                Event   = info.Event,
                Item    = item,
                Session = info.Session,
                User    = user,
                Server  = info.Server
            };

            var content = new StringContent(JsonSerializer.Serialize(body, JsonDefaults.GetOptions()), Encoding.UTF8, "application/json");
            await _http.PostAsync(url, content);
        }
Esempio n. 18
0
 public MediaEncoder(
     ILogger <MediaEncoder> logger,
     IServerConfigurationManager configurationManager,
     IFileSystem fileSystem,
     ILocalizationManager localization,
     Lazy <EncodingHelper> encodingHelperFactory,
     IConfiguration config)
 {
     _logger = logger;
     _configurationManager    = configurationManager;
     _fileSystem              = fileSystem;
     _localization            = localization;
     _encodingHelperFactory   = encodingHelperFactory;
     _startupOptionFFmpegPath = config.GetValue <string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
     _jsonSerializerOptions   = JsonDefaults.GetOptions();
 }
        private async Task <SearchResult> GetSearchResultsInternal(BookInfo item, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            // pattern match the filename
            // year can be included for better results
            GetBookMetadata(item);

            var url = string.Format(GoogleApiUrls.SearchUrl, WebUtility.UrlEncode(item.Name), 0, 20);

            var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);

            using (var response = await httpClient.GetAsync(url).ConfigureAwait(false))
            {
                await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);

                return(await JsonSerializer.DeserializeAsync <SearchResult>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false));
            }
        }
Esempio n. 20
0
        public HdHomerunHost(
            IServerConfigurationManager config,
            ILogger <HdHomerunHost> logger,
            IFileSystem fileSystem,
            IHttpClientFactory httpClientFactory,
            IServerApplicationHost appHost,
            ISocketFactory socketFactory,
            INetworkManager networkManager,
            IStreamHelper streamHelper,
            IMemoryCache memoryCache)
            : base(config, logger, fileSystem, memoryCache)
        {
            _httpClientFactory = httpClientFactory;
            _appHost           = appHost;
            _socketFactory     = socketFactory;
            _networkManager    = networkManager;
            _streamHelper      = streamHelper;

            _jsonOptions = JsonDefaults.GetOptions();
        }
        /// <summary>
        /// Adds the images.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="path">The path.</param>
        /// <param name="releaseId">The release identifier.</param>
        /// <param name="releaseGroupId">The release group identifier.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        private async Task AddImages(List <RemoteImageInfo> list, string path, string releaseId, string releaseGroupId, CancellationToken cancellationToken)
        {
            Stream fileStream = File.OpenRead(path);
            var    obj        = await JsonSerializer.DeserializeAsync <ArtistProvider.ArtistResponse>(fileStream, JsonDefaults.GetOptions()).ConfigureAwait(false);

            if (obj.albums != null)
            {
                var album = obj.albums.FirstOrDefault(i => string.Equals(i.release_group_id, releaseGroupId, StringComparison.OrdinalIgnoreCase));

                if (album != null)
                {
                    PopulateImages(list, album.albumcover, ImageType.Primary, 1000, 1000);
                    PopulateImages(list, album.cdart, ImageType.Disc, 1000, 1000);
                }
            }
        }
        private SmartMatchResult GetResultSmartMatch(IReadOnlyList <IResultSetValue> reader)
        {
            var index = 0;

            var result = new SmartMatchResult
            {
                Id = reader[0].ReadGuidFromBlob()
            };

            index++;
            result.ItemName = reader[index].ToString();

            index++;
            result.DisplayName = reader[index].ToString();

            index++;
            result.OrganizerType = (FileOrganizerType)Enum.Parse(typeof(FileOrganizerType), reader[index].ToString(), true);

            index++;
            if (reader[index].SQLiteType != SQLiteType.Null)
            {
                result.MatchStrings.AddRange(JsonSerializer.Deserialize <List <string> >(reader[index].ToString(), JsonDefaults.GetOptions()));
            }

            return(result);
        }
Esempio n. 23
0
        /// <summary>
        /// Adds the images.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="path">The path.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        private async Task AddImages(List <RemoteImageInfo> list, string path, CancellationToken cancellationToken)
        {
            Stream fileStream = File.OpenRead(path);
            var    obj        = await JsonSerializer.DeserializeAsync <ArtistResponse>(fileStream, JsonDefaults.GetOptions()).ConfigureAwait(false);

            PopulateImages(list, obj.artistbackground, ImageType.Backdrop, 1920, 1080);
            PopulateImages(list, obj.artistthumb, ImageType.Primary, 500, 281);
            PopulateImages(list, obj.hdmusiclogo, ImageType.Logo, 800, 310);
            PopulateImages(list, obj.musicbanner, ImageType.Banner, 1000, 185);
            PopulateImages(list, obj.musiclogo, ImageType.Logo, 400, 155);
            PopulateImages(list, obj.hdmusicarts, ImageType.Art, 1000, 562);
            PopulateImages(list, obj.musicarts, ImageType.Art, 500, 281);
        }
Esempio n. 24
0
        /// <summary>
        /// Downloads the artist data.
        /// </summary>
        /// <param name="musicBrainzId">The music brainz id.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task{System.Boolean}.</returns>
        internal async Task DownloadArtistJson(string musicBrainzId, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var url = string.Format(
                CultureInfo.InvariantCulture,
                Plugin.BaseUrl,
                Plugin.ApiKey,
                musicBrainzId,
                "music");

            var clientKey = Plugin.Instance.Configuration.PersonalApiKey;

            if (!string.IsNullOrWhiteSpace(clientKey))
            {
                url += "&client_key=" + clientKey;
            }

            var jsonPath = GetArtistJsonPath(_config.ApplicationPaths, musicBrainzId);

            Directory.CreateDirectory(Path.GetDirectoryName(jsonPath));

            try
            {
                var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
                using (var httpResponse = await httpClient.GetAsync(new Uri(url), cancellationToken).ConfigureAwait(false))
                    using (var response = httpResponse.Content)
                        using (var saveFileStream = new FileStream(jsonPath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
                        {
                            await response.CopyToAsync(saveFileStream, CancellationToken.None).ConfigureAwait(false);
                        }
            }
            catch (HttpRequestException ex)
            {
                if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
                {
                    Stream fileStream = File.OpenWrite(jsonPath);
                    await JsonSerializer.SerializeAsync(fileStream, new ArtistResponse(), JsonDefaults.GetOptions()).ConfigureAwait(false);
                }
                else
                {
                    throw;
                }
            }
        }
Esempio n. 25
0
        /// <summary>
        /// Downloads the series json.
        /// </summary>
        /// <param name="tvdbId">The TVDB identifier.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        internal async Task DownloadSeriesJson(string tvdbId, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var url = string.Format(
                CultureInfo.InvariantCulture,
                Plugin.BaseUrl,
                Plugin.ApiKey,
                tvdbId,
                "tv");

            var clientKey = Plugin.Instance.Configuration.PersonalApiKey;

            if (!string.IsNullOrWhiteSpace(clientKey))
            {
                url += "&client_key=" + clientKey;
            }

            var path = GetJsonPath(tvdbId);

            Directory.CreateDirectory(Path.GetDirectoryName(path));

            try
            {
                var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
                using (var httpResponse = await httpClient.GetAsync(new Uri(url), cancellationToken).ConfigureAwait(false))
                    using (var response = httpResponse.Content)
                        using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
                        {
                            await response.CopyToAsync(fileStream, CancellationToken.None).ConfigureAwait(false);
                        }
            }
            catch (HttpRequestException exception)
            {
                if (exception.StatusCode.HasValue && exception.StatusCode.Value == HttpStatusCode.NotFound)
                {
                    // If the user has automatic updates enabled, save a dummy object to prevent repeated download attempts
                    Stream fileStream = File.OpenWrite(path);
                    await JsonSerializer.SerializeAsync(fileStream, new RootObject(), JsonDefaults.GetOptions()).ConfigureAwait(false);

                    return;
                }

                throw;
            }
        }
Esempio n. 26
0
        private async Task AddImages(List <RemoteImageInfo> list, string path)
        {
            Stream fileStream = File.OpenRead(path);
            var    root       = await JsonSerializer.DeserializeAsync <RootObject>(fileStream, JsonDefaults.GetOptions()).ConfigureAwait(false);

            AddImages(list, root);
        }
Esempio n. 27
0
        public async Task Test(string fileName)
        {
            var path = Path.Join("Test Data", fileName);

            using (var stream = File.OpenRead(path))
            {
                await JsonSerializer.DeserializeAsync <InternalMediaInfoResult>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false);
            }
        }
        private async Task AddImages(List <RemoteImageInfo> list, int seasonNumber, string path, CancellationToken cancellationToken)
        {
            Stream fileStream = File.OpenRead(path);
            var    root       = await JsonSerializer.DeserializeAsync <SeriesProvider.RootObject>(fileStream, JsonDefaults.GetOptions()).ConfigureAwait(false);

            AddImages(list, root, seasonNumber, cancellationToken);
        }
Esempio n. 29
0
        /// <inheritdoc/>
        public void Perform()
        {
            var dataPath = _paths.DataPath;

            _logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");

            using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null))
            {
                var dbContext = _provider.CreateContext();

                var queryResult = connection.Query("SELECT * FROM LocalUsersv2");

                dbContext.RemoveRange(dbContext.Users);
                dbContext.SaveChanges();

                foreach (var entry in queryResult)
                {
                    UserMockup?mockup = JsonSerializer.Deserialize <UserMockup>(entry[2].ToBlob(), JsonDefaults.GetOptions());
                    if (mockup == null)
                    {
                        continue;
                    }

                    var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);

                    var config = File.Exists(Path.Combine(userDataDir, "config.xml"))
                        ? (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), Path.Combine(userDataDir, "config.xml"))
                        : new UserConfiguration();
                    var policy = File.Exists(Path.Combine(userDataDir, "policy.xml"))
                        ? (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), Path.Combine(userDataDir, "policy.xml"))
                        : new UserPolicy();
                    policy.AuthenticationProviderId = policy.AuthenticationProviderId?.Replace(
                        "Emby.Server.Implementations.Library",
                        "Jellyfin.Server.Implementations.Users",
                        StringComparison.Ordinal)
                                                      ?? typeof(DefaultAuthenticationProvider).FullName;

                    policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName;
                    int?maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
                    {
                        -1 => null,
                        0 => 3,
                        _ => policy.LoginAttemptsBeforeLockout
                    };

                    var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId)
                    {
                        Id                         = entry[1].ReadGuidFromBlob(),
                        InternalId                 = entry[0].ToInt64(),
                        MaxParentalAgeRating       = policy.MaxParentalRating,
                        EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
                        RemoteClientBitrateLimit   = policy.RemoteClientBitrateLimit,
                        InvalidLoginAttemptCount   = policy.InvalidLoginAttemptCount,
                        LoginAttemptsBeforeLockout = maxLoginAttempts,
                        SubtitleMode               = config.SubtitleMode,
                        HidePlayedInLatest         = config.HidePlayedInLatest,
                        EnableLocalPassword        = config.EnableLocalPassword,
                        PlayDefaultAudioTrack      = config.PlayDefaultAudioTrack,
                        DisplayCollectionsView     = config.DisplayCollectionsView,
                        DisplayMissingEpisodes     = config.DisplayMissingEpisodes,
                        AudioLanguagePreference    = config.AudioLanguagePreference,
                        RememberAudioSelections    = config.RememberAudioSelections,
                        EnableNextEpisodeAutoPlay  = config.EnableNextEpisodeAutoPlay,
                        RememberSubtitleSelections = config.RememberSubtitleSelections,
                        SubtitleLanguagePreference = config.SubtitleLanguagePreference,
                        Password                   = mockup.Password,
                        EasyPassword               = mockup.EasyPassword,
                        LastLoginDate              = mockup.LastLoginDate,
                        LastActivityDate           = mockup.LastActivityDate
                    };

                    if (mockup.ImageInfos.Length > 0)
                    {
                        ItemImageInfo info = mockup.ImageInfos[0];

                        user.ProfileImage = new ImageInfo(info.Path)
                        {
                            LastModified = info.DateModified
                        };
                    }

                    user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
                    user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
                    user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
                    user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
                    user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
                    user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
                    user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
                    user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
                    user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
                    user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
                    user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
                    user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
                    user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
                    user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
                    user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
                    user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
                    user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
                    user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
                    user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
                    user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
                    user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);

                    foreach (var policyAccessSchedule in policy.AccessSchedules)
                    {
                        user.AccessSchedules.Add(policyAccessSchedule);
                    }

                    user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
                    user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
                    user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
                    user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
                    user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
                    user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
                    user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
                    user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
                    user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);

                    dbContext.Users.Add(user);
                }

                dbContext.SaveChanges();
            }

            try
            {
                File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old"));

                var journalPath = Path.Combine(dataPath, DbFilename + "-journal");
                if (File.Exists(journalPath))
                {
                    File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal"));
                }
            }
            catch (IOException e)
            {
                _logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
            }
        }

#nullable disable
        /// <inheritdoc/>
        public void SaveResult(SmartMatchResult result, CancellationToken cancellationToken)
        {
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }

            cancellationToken.ThrowIfCancellationRequested();

            using (WriteLock.Write())
            {
                using (var connection = CreateConnection())
                {
                    connection.RunInTransaction(
                        db =>
                    {
                        var commandText = "replace into SmartMatch (Id, ItemName, DisplayName, OrganizerType, MatchStrings) values (@Id, @ItemName, @DisplayName, @OrganizerType, @MatchStrings)";

                        using (var statement = db.PrepareStatement(commandText))
                        {
                            statement.TryBind("@Id", result.Id.ToGuidBlob());

                            statement.TryBind("@ItemName", result.ItemName);
                            statement.TryBind("@DisplayName", result.DisplayName);
                            statement.TryBind("@OrganizerType", result.OrganizerType.ToString());
                            statement.TryBind("@MatchStrings", JsonSerializer.Serialize(result.MatchStrings, JsonDefaults.GetOptions()));

                            statement.MoveNext();
                        }
                    },
                        TransactionMode);
                }
            }
        }