Compare commits

..

1 Commits

Author SHA1 Message Date
bakerboy448
20b82146ac New: Deprecate Readarr 2025-10-30 23:41:08 -05:00
18 changed files with 141 additions and 156 deletions

View File

@@ -1,14 +1,18 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Label from 'Components/Label';
import Button from 'Components/Link/Button';
import Link from 'Components/Link/Link';
import Menu from 'Components/Menu/Menu';
import MenuContent from 'Components/Menu/MenuContent';
import { sizes } from 'Helpers/Props';
import { kinds, sizes } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import AddApplicationPresetMenuItem from './AddApplicationPresetMenuItem';
import styles from './AddApplicationItem.css';
const DEPRECATED_APPLICATIONS = ['Readarr'];
const OBSOLETE_APPLICATIONS = [];
class AddApplicationItem extends Component {
//
@@ -36,6 +40,8 @@ class AddApplicationItem extends Component {
} = this.props;
const hasPresets = !!presets && !!presets.length;
const isDeprecated = DEPRECATED_APPLICATIONS.includes(implementation);
const isObsolete = OBSOLETE_APPLICATIONS.includes(implementation);
return (
<div
@@ -49,6 +55,24 @@ class AddApplicationItem extends Component {
<div className={styles.overlay}>
<div className={styles.name}>
{implementationName}
{
isDeprecated &&
<Label
kind={kinds.WARNING}
title={translate('DeprecatedApplicationMessage', { applicationName: implementationName })}
>
{translate('Deprecated')}
</Label>
}
{
isObsolete &&
<Label
kind={kinds.DANGER}
title={translate('ObsoleteApplicationMessage', { applicationName: implementationName })}
>
{translate('Obsolete')}
</Label>
}
</div>
<div className={styles.actions}>

View File

@@ -10,6 +10,9 @@ import translate from 'Utilities/String/translate';
import EditApplicationModalConnector from './EditApplicationModalConnector';
import styles from './Application.css';
const DEPRECATED_APPLICATIONS = ['Readarr'];
const OBSOLETE_APPLICATIONS = [];
class Application extends Component {
//
@@ -61,10 +64,13 @@ class Application extends Component {
syncLevel,
fields,
tags,
tagList
tagList,
implementation
} = this.props;
const applicationUrl = fields.find((field) => field.name === 'baseUrl')?.value;
const isDeprecated = DEPRECATED_APPLICATIONS.includes(implementation);
const isObsolete = OBSOLETE_APPLICATIONS.includes(implementation);
return (
<Card
@@ -88,6 +94,26 @@ class Application extends Component {
}
</div>
{
isDeprecated &&
<Label
kind={kinds.WARNING}
title={translate('DeprecatedApplicationMessage', { applicationName: implementation })}
>
{translate('Deprecated')}
</Label>
}
{
isObsolete &&
<Label
kind={kinds.DANGER}
title={translate('ObsoleteApplicationMessage', { applicationName: implementation })}
>
{translate('Obsolete')}
</Label>
}
{
syncLevel === 'addOnly' &&
<Label kind={kinds.WARNING}>
@@ -141,6 +167,7 @@ class Application extends Component {
Application.propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
implementation: PropTypes.string.isRequired,
enable: PropTypes.bool.isRequired,
syncLevel: PropTypes.string.isRequired,
fields: PropTypes.arrayOf(PropTypes.object).isRequired,

View File

@@ -39,6 +39,9 @@ const syncLevelOptions = [
}
];
const DEPRECATED_APPLICATIONS = ['Readarr'];
const OBSOLETE_APPLICATIONS = [];
function EditApplicationModalContent(props) {
const {
advancedSettings,
@@ -60,6 +63,7 @@ function EditApplicationModalContent(props) {
const {
id,
implementation,
implementationName,
name,
syncLevel,
@@ -68,6 +72,9 @@ function EditApplicationModalContent(props) {
message
} = item;
const isDeprecated = DEPRECATED_APPLICATIONS.includes(implementation);
const isObsolete = OBSOLETE_APPLICATIONS.includes(implementation);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
@@ -90,6 +97,28 @@ function EditApplicationModalContent(props) {
{
!isFetching && !error &&
<Form {...otherProps}>
{
isDeprecated &&
<Alert
className={styles.message}
kind={kinds.WARNING}
>
<div>{translate('DeprecatedApplicationTitle', { applicationName: implementationName })}</div>
<div>{translate('DeprecatedApplicationMessage', { applicationName: implementationName })}</div>
</Alert>
}
{
isObsolete &&
<Alert
className={styles.message}
kind={kinds.DANGER}
>
<div>{translate('ObsoleteApplicationTitle', { applicationName: implementationName })}</div>
<div>{translate('ObsoleteApplicationMessage', { applicationName: implementationName })}</div>
</Alert>
}
{
!!message &&
<Alert

View File

@@ -5,5 +5,6 @@
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-bsd-crossbuild" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/dotnet-bsd-crossbuild/nuget/v3/index.json" />
<add key="Mono.Posix.NETStandard" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/Mono.Posix.NETStandard/nuget/v3/index.json" />
<add key="FluentMigrator" value="https://pkgs.dev.azure.com/Servarr/Servarr/_packaging/FluentMigrator/nuget/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -159,7 +159,7 @@ namespace NzbDrone.Core.Applications.Lidarr
case HttpStatusCode.BadRequest:
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
{
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app");
break;
}

View File

@@ -172,7 +172,7 @@ namespace NzbDrone.Core.Applications.Radarr
case HttpStatusCode.BadRequest:
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
{
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app");
break;
}

View File

@@ -14,6 +14,7 @@ using NzbDrone.Core.Indexers;
namespace NzbDrone.Core.Applications.Readarr
{
[Obsolete("Readarr is deprecated and will be removed in a future version")]
public class Readarr : ApplicationBase<ReadarrSettings>
{
public override string Name => "Readarr";

View File

@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Applications.Readarr
case HttpStatusCode.BadRequest:
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
{
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app");
break;
}

View File

@@ -159,7 +159,7 @@ namespace NzbDrone.Core.Applications.Sonarr
case HttpStatusCode.BadRequest:
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
{
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app");
break;
}

View File

@@ -144,7 +144,7 @@ namespace NzbDrone.Core.Applications.Whisparr
case HttpStatusCode.BadRequest:
if (ex.Response.Content.Contains("Query successful, but no results in the configured categories were returned from your indexer.", StringComparison.InvariantCultureIgnoreCase))
{
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: Prowlarr will not sync X Indexer to App");
_logger.Warn(ex, "No Results in configured categories. See FAQ Entry: https://wiki.servarr.com/prowlarr/faq#prowlarr-will-not-sync-x-indexer-to-app");
break;
}

View File

@@ -7,7 +7,7 @@ using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Core.Datastore.Migration
{
[Maintenance(MigrationStage.BeforeAll, TransactionBehavior.None)]
public class DatabaseEngineVersionCheck : ForwardOnlyMigration
public class DatabaseEngineVersionCheck : FluentMigrator.Migration
{
protected readonly Logger _logger;
@@ -22,6 +22,11 @@ namespace NzbDrone.Core.Datastore.Migration
IfDatabase("postgres").Execute.WithConnection(LogPostgresVersion);
}
public override void Down()
{
// No-op
}
private void LogSqliteVersion(IDbConnection conn, IDbTransaction tran)
{
using (var versionCmd = conn.CreateCommand())

View File

@@ -1,3 +1,5 @@
using System.Data;
using Dapper;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
@@ -8,7 +10,13 @@ namespace NzbDrone.Core.Datastore.Migration
{
protected override void MainDbUpgrade()
{
Update.Table("Indexers").Set(new { Redirect = true }).Where(new { Implementation = "Newznab", Redirect = false });
Execute.WithConnection(UpdateNewznabRedirectSetting);
}
private void UpdateNewznabRedirectSetting(IDbConnection conn, IDbTransaction tran)
{
var updateSql = "UPDATE \"Indexers\" SET \"Redirect\" = @Redirect WHERE \"Implementation\" = 'Newznab' AND \"Redirect\" = false";
conn.Execute(updateSql, new { Redirect = true }, transaction: tran);
}
}
}

View File

@@ -6,6 +6,7 @@ using FluentMigrator.Runner.Generators;
using FluentMigrator.Runner.Initialization;
using FluentMigrator.Runner.Processors;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Logging;
@@ -19,10 +20,13 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
public class MigrationController : IMigrationController
{
private readonly Logger _logger;
private readonly ILoggerProvider _migrationLoggerProvider;
public MigrationController(Logger logger)
public MigrationController(Logger logger,
ILoggerProvider migrationLoggerProvider)
{
_logger = logger;
_migrationLoggerProvider = migrationLoggerProvider;
}
public void Migrate(string connectionString, MigrationContext migrationContext, DatabaseType databaseType)
@@ -31,13 +35,16 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
_logger.Info("*** Migrating {0} ***", connectionString);
ServiceProvider serviceProvider;
var db = databaseType == DatabaseType.SQLite ? "sqlite" : "postgres";
var serviceProvider = new ServiceCollection()
serviceProvider = new ServiceCollection()
.AddLogging(b => b.AddNLog())
.AddFluentMigratorCore()
.Configure<RunnerOptions>(cfg => cfg.IncludeUntaggedMaintenances = true)
.ConfigureRunner(builder => builder
.ConfigureRunner(
builder => builder
.AddPostgres()
.AddNzbDroneSQLite()
.WithGlobalConnectionString(connectionString)

View File

@@ -1,17 +1,11 @@
using System.Data;
using FluentMigrator;
using FluentMigrator;
using FluentMigrator.Builders.Create;
using FluentMigrator.Builders.Create.Table;
using FluentMigrator.Runner;
using FluentMigrator.Runner.BatchParser;
using FluentMigrator.Runner.Generators;
using FluentMigrator.Runner.Generators.SQLite;
using FluentMigrator.Runner.Initialization;
using FluentMigrator.Runner.Processors;
using FluentMigrator.Runner.Processors.SQLite;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace NzbDrone.Core.Datastore.Migration.Framework
{
@@ -22,40 +16,23 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
return expressionRoot.Table(name).WithColumn("Id").AsInt32().PrimaryKey().Identity();
}
public static void AddParameter(this IDbCommand command, object value)
public static void AddParameter(this System.Data.IDbCommand command, object value)
{
var parameter = command.CreateParameter();
parameter.Value = value;
command.Parameters.Add(parameter);
}
public static IMigrationRunnerBuilder AddNzbDroneSQLite(this IMigrationRunnerBuilder builder, bool binaryGuid = false, bool useStrictTables = false)
public static IMigrationRunnerBuilder AddNzbDroneSQLite(this IMigrationRunnerBuilder builder)
{
builder.Services
.AddTransient<SQLiteBatchParser>()
.AddScoped<SQLiteDbFactory>()
.AddScoped<NzbDroneSQLiteProcessor>(sp =>
{
var factory = sp.GetService<SQLiteDbFactory>();
var logger = sp.GetService<ILogger<NzbDroneSQLiteProcessor>>();
var options = sp.GetService<IOptionsSnapshot<ProcessorOptions>>();
var connectionStringAccessor = sp.GetService<IConnectionStringAccessor>();
var sqliteQuoter = new SQLiteQuoter(false);
return new NzbDroneSQLiteProcessor(factory, sp.GetService<SQLiteGenerator>(), logger, options, connectionStringAccessor, sp, sqliteQuoter);
})
.AddScoped<ISQLiteTypeMap>(_ => new NzbDroneSQLiteTypeMap(useStrictTables))
.AddScoped<NzbDroneSQLiteProcessor>()
.AddScoped<IMigrationProcessor>(sp => sp.GetRequiredService<NzbDroneSQLiteProcessor>())
.AddScoped(
sp =>
{
var typeMap = sp.GetRequiredService<ISQLiteTypeMap>();
return new SQLiteGenerator(
new SQLiteQuoter(binaryGuid),
typeMap,
new OptionsWrapper<GeneratorOptions>(new GeneratorOptions()));
})
.AddScoped<SQLiteQuoter>()
.AddScoped<SQLiteGenerator>()
.AddScoped<IMigrationGenerator>(sp => sp.GetRequiredService<SQLiteGenerator>());
return builder;
}
}

View File

@@ -15,8 +15,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
{
public class NzbDroneSQLiteProcessor : SQLiteProcessor
{
private readonly SQLiteQuoter _quoter;
public NzbDroneSQLiteProcessor(SQLiteDbFactory factory,
SQLiteGenerator generator,
ILogger<NzbDroneSQLiteProcessor> logger,
@@ -26,7 +24,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
SQLiteQuoter quoter)
: base(factory, generator, logger, options, connectionStringAccessor, serviceProvider, quoter)
{
_quoter = quoter;
}
public override void Process(AlterColumnExpression expression)
@@ -38,7 +35,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
if (columnIndex == -1)
{
throw new ApplicationException($"Column {expression.Column.Name} does not exist on table {expression.TableName}.");
throw new ApplicationException(string.Format("Column {0} does not exist on table {1}.", expression.Column.Name, expression.TableName));
}
columnDefinitions[columnIndex] = expression.Column;
@@ -48,28 +45,6 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
ProcessAlterTable(tableDefinition);
}
public override void Process(AlterDefaultConstraintExpression expression)
{
var tableDefinition = GetTableSchema(expression.TableName);
var columnDefinitions = tableDefinition.Columns.ToList();
var columnIndex = columnDefinitions.FindIndex(c => c.Name == expression.ColumnName);
if (columnIndex == -1)
{
throw new ApplicationException($"Column {expression.ColumnName} does not exist on table {expression.TableName}.");
}
var changedColumn = columnDefinitions[columnIndex];
changedColumn.DefaultValue = expression.DefaultValue;
columnDefinitions[columnIndex] = changedColumn;
tableDefinition.Columns = columnDefinitions;
ProcessAlterTable(tableDefinition);
}
public override void Process(DeleteColumnExpression expression)
{
var tableDefinition = GetTableSchema(expression.TableName);
@@ -87,7 +62,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
if (columnsToRemove.Any())
{
throw new ApplicationException($"Column {columnsToRemove.First()} does not exist on table {expression.TableName}.");
throw new ApplicationException(string.Format("Column {0} does not exist on table {1}.", columnsToRemove.First(), expression.TableName));
}
ProcessAlterTable(tableDefinition);
@@ -103,12 +78,12 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
if (columnIndex == -1)
{
throw new ApplicationException($"Column {expression.OldName} does not exist on table {expression.TableName}.");
throw new ApplicationException(string.Format("Column {0} does not exist on table {1}.", expression.OldName, expression.TableName));
}
if (columnDefinitions.Any(c => c.Name == expression.NewName))
{
throw new ApplicationException($"Column {expression.NewName} already exists on table {expression.TableName}.");
throw new ApplicationException(string.Format("Column {0} already exists on table {1}.", expression.NewName, expression.TableName));
}
oldColumnDefinitions[columnIndex] = (ColumnDefinition)columnDefinitions[columnIndex].Clone();
@@ -153,20 +128,21 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
}
// What is the cleanest way to do this? Add function to Generator?
var columnsToInsert = string.Join(", ", tableDefinition.Columns.Select(c => _quoter.QuoteColumnName(c.Name)));
var columnsToFetch = string.Join(", ", (oldColumnDefinitions ?? tableDefinition.Columns).Select(c => _quoter.QuoteColumnName(c.Name)));
var quoter = new SQLiteQuoter();
var columnsToInsert = string.Join(", ", tableDefinition.Columns.Select(c => quoter.QuoteColumnName(c.Name)));
var columnsToFetch = string.Join(", ", (oldColumnDefinitions ?? tableDefinition.Columns).Select(c => quoter.QuoteColumnName(c.Name)));
Process(new CreateTableExpression { TableName = tempTableName, Columns = tableDefinition.Columns.ToList() });
Process(new CreateTableExpression() { TableName = tempTableName, Columns = tableDefinition.Columns.ToList() });
Process($"INSERT INTO {_quoter.QuoteTableName(tempTableName)} ({columnsToInsert}) SELECT {columnsToFetch} FROM {_quoter.QuoteTableName(tableName)}");
Process(string.Format("INSERT INTO {0} ({1}) SELECT {2} FROM {3}", quoter.QuoteTableName(tempTableName), columnsToInsert, columnsToFetch, quoter.QuoteTableName(tableName)));
Process(new DeleteTableExpression { TableName = tableName });
Process(new DeleteTableExpression() { TableName = tableName });
Process(new RenameTableExpression { OldName = tempTableName, NewName = tableName });
Process(new RenameTableExpression() { OldName = tempTableName, NewName = tableName });
foreach (var index in tableDefinition.Indexes)
{
Process(new CreateIndexExpression { Index = index });
Process(new CreateIndexExpression() { Index = index });
}
}
}

View File

@@ -1,76 +0,0 @@
using System.Data;
using FluentMigrator.Runner.Generators.Base;
using FluentMigrator.Runner.Generators.SQLite;
namespace NzbDrone.Core.Datastore.Migration.Framework;
// Based on https://github.com/fluentmigrator/fluentmigrator/blob/v6.2.0/src/FluentMigrator.Runner.SQLite/Generators/SQLite/SQLiteTypeMap.cs
public sealed class NzbDroneSQLiteTypeMap : TypeMapBase, ISQLiteTypeMap
{
public bool UseStrictTables { get; }
public NzbDroneSQLiteTypeMap(bool useStrictTables = false)
{
UseStrictTables = useStrictTables;
SetupTypeMaps();
}
// Must be kept in sync with upstream
protected override void SetupTypeMaps()
{
SetTypeMap(DbType.Binary, "BLOB");
SetTypeMap(DbType.Byte, "INTEGER");
SetTypeMap(DbType.Int16, "INTEGER");
SetTypeMap(DbType.Int32, "INTEGER");
SetTypeMap(DbType.Int64, "INTEGER");
SetTypeMap(DbType.SByte, "INTEGER");
SetTypeMap(DbType.UInt16, "INTEGER");
SetTypeMap(DbType.UInt32, "INTEGER");
SetTypeMap(DbType.UInt64, "INTEGER");
if (!UseStrictTables)
{
SetTypeMap(DbType.Currency, "NUMERIC");
SetTypeMap(DbType.Decimal, "NUMERIC");
SetTypeMap(DbType.Double, "NUMERIC");
SetTypeMap(DbType.Single, "NUMERIC");
SetTypeMap(DbType.VarNumeric, "NUMERIC");
SetTypeMap(DbType.Date, "DATETIME");
SetTypeMap(DbType.DateTime, "DATETIME");
SetTypeMap(DbType.DateTime2, "DATETIME");
SetTypeMap(DbType.Time, "DATETIME");
SetTypeMap(DbType.Guid, "UNIQUEIDENTIFIER");
// Custom so that we can use DateTimeOffset in Postgres for appropriate DB typing
SetTypeMap(DbType.DateTimeOffset, "DATETIME");
}
else
{
SetTypeMap(DbType.Currency, "TEXT");
SetTypeMap(DbType.Decimal, "TEXT");
SetTypeMap(DbType.Double, "REAL");
SetTypeMap(DbType.Single, "REAL");
SetTypeMap(DbType.VarNumeric, "TEXT");
SetTypeMap(DbType.Date, "TEXT");
SetTypeMap(DbType.DateTime, "TEXT");
SetTypeMap(DbType.DateTime2, "TEXT");
SetTypeMap(DbType.Time, "TEXT");
SetTypeMap(DbType.Guid, "TEXT");
// Custom so that we can use DateTimeOffset in Postgres for appropriate DB typing
SetTypeMap(DbType.DateTimeOffset, "TEXT");
}
SetTypeMap(DbType.AnsiString, "TEXT");
SetTypeMap(DbType.String, "TEXT");
SetTypeMap(DbType.AnsiStringFixedLength, "TEXT");
SetTypeMap(DbType.StringFixedLength, "TEXT");
SetTypeMap(DbType.Boolean, "INTEGER");
}
public override string GetTypeMap(DbType type, int? size, int? precision)
{
return base.GetTypeMap(type, size: null, precision: null);
}
}

View File

@@ -538,6 +538,12 @@
"NotificationsTelegramSettingsIncludeAppName": "Include {appName} in Title",
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Optionally prefix message title with {appName} to differentiate notifications from different applications",
"OAuthPopupMessage": "Pop-ups are being blocked by your browser",
"Obsolete": "Obsolete",
"ObsoleteApplicationMessage": "{applicationName} is obsolete and has been removed",
"ObsoleteApplicationTitle": "{applicationName} is Obsolete",
"Deprecated": "Deprecated",
"DeprecatedApplicationMessage": "{applicationName} is deprecated and will be removed in a future version",
"DeprecatedApplicationTitle": "{applicationName} is Deprecated",
"Ok": "Ok",
"OnApplicationUpdate": "On Application Update",
"OnApplicationUpdateHelpText": "On Application Update",

View File

@@ -13,9 +13,9 @@
<PackageReference Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="Polly" Version="8.6.4" />
<PackageReference Include="FluentMigrator.Runner" Version="6.2.0" />
<PackageReference Include="FluentMigrator.Runner.Postgres" Version="6.2.0" />
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="6.2.0" />
<PackageReference Include="Servarr.FluentMigrator.Runner" Version="3.3.2.9" />
<PackageReference Include="Servarr.FluentMigrator.Runner.Postgres" Version="3.3.2.9" />
<PackageReference Include="Servarr.FluentMigrator.Runner.SQLite" Version="3.3.2.9" />
<PackageReference Include="System.Drawing.Common" Version="8.0.19" />
<PackageReference Include="System.Memory" Version="4.6.3" />
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />