commit #11

Merged
wjones merged 1 commits from development into main 2026-07-08 13:59:27 -04:00
2450 changed files with 73125 additions and 3 deletions
Showing only changes of commit 41e737f3ec - Show all commits
+2 -2
View File
@@ -45,7 +45,7 @@
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="11.0.0-preview.1.26104.118" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="11.0.0-preview.1.26104.118" />
@@ -67,7 +67,7 @@
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageVersion Include="Serilog.Enrichers.Thread" Version="4.0.0" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.1" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
+2
View File
@@ -17,6 +17,8 @@
<ApplicationIcon>Jellyfin.Server.ico</ApplicationIcon>
<IsPackable>true</IsPackable>
<EnableDefaultContentItems>false</EnableDefaultContentItems>
<!-- Enable assembly binding redirects in runtimeconfig.json -->
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
@@ -0,0 +1,140 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
<PropertyGroup>
<NoWarn>$(NoWarn);CA1024;CA1031;CA1032;CA1034;CA1054;CA1055;CA1056;CA1062;CA1308;CA1303;CA1305;CA1508;CA1722;CA1724;CA1805;CA1813;CA1815;CA1816;CA1819;CA1821;CA1822;CA1823;CA1824;CA1825;CA1826;CA1830;CA1836;CA1839;CA1840;CA1841;CA1859;CA1861;CA1873;CA1911;CA2000;CA2007;CA2016;CA2025;CA2026;CA2100;CA2101;CA2102;CA2201;CA2202;CA2207;CA2208;CA2219;CA2220;CA2222;CA2225;CA2226;CA2230;CA2231;CA2234;CA2241;CA2242;CA2243;CA2244;CA2245;CA2246;CA2247;CA2248;CA2249;CA2250;CA2253;CA3001;CA3002;CA3003;CA3004;CA3005;CA3006;CA3007;CA3008;CA3009;CA3010;CA3011;CA3012;CA3061;CA3062;CA5359;CA5360;CA5361;CA5362;CA5363;CA5364;CA5365;CA5366;CA5367;CA5368;CA5369;CA5370;CA5371;CA5372;CA5373;CA5374;CA5375;CA5376;CA5377;CA5378;CA5379;CA5380;CA5381;CA5382;CA5383;CA5384;CA5385;CA5386;CA5387;CA5388;CA5389;CA5390;CA5391;CA5392;CA5393;CA5394;CA5395;CA5396;CA5397;CA5398;CA5399;CA5400;CA5401;CA5402;CA5403;NU1903;CA1848;CA1515;CA1812;CA1849;CA1860;CA1867;CA1517;CS1061;CS0103</NoWarn>
<ProjectGuid>{07E39F42-A2C6-4B32-AF8C-725F957A73FF}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA1024;CA1031;CA1032;CA1034;CA1054;CA1055;CA1056;CA1062;CA1308;CA1303;CA1305;CA1508;CA1722;CA1724;CA1805;CA1813;CA1815;CA1816;CA1819;CA1821;CA1822;CA1823;CA1824;CA1825;CA1826;CA1830;CA1836;CA1839;CA1840;CA1841;CA1859;CA1861;CA1873;CA1911;CA2000;CA2007;CA2016;CA2025;CA2026;CA2100;CA2101;CA2102;CA2201;CA2202;CA2207;CA2208;CA2219;CA2220;CA2222;CA2225;CA2226;CA2230;CA2231;CA2234;CA2241;CA2242;CA2243;CA2244;CA2245;CA2246;CA2247;CA2248;CA2249;CA2250;CA2253;CA3001;CA3002;CA3003;CA3004;CA3005;CA3006;CA3007;CA3008;CA3009;CA3010;CA3011;CA3012;CA3061;CA3062;CA5359;CA5360;CA5361;CA5362;CA5363;CA5364;CA5365;CA5366;CA5367;CA5368;CA5369;CA5370;CA5371;CA5372;CA5373;CA5374;CA5375;CA5376;CA5377;CA5378;CA5379;CA5380;CA5381;CA5382;CA5383;CA5384;CA5385;CA5386;CA5387;CA5388;CA5389;CA5390;CA5391;CA5392;CA5393;CA5394;CA5395;CA5396;CA5397;CA5398;CA5399;CA5400;CA5401;CA5402;CA5403;NU1903;CA1848;CA1515;CA1812;CA1849;CA1860;CA1867;CA1517;CS1061;CS0103</NoWarn>
<AssemblyName>jellyfin</AssemblyName>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ServerGarbageCollection>false</ServerGarbageCollection>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ApplicationIcon>Jellyfin.Server.ico</ApplicationIcon>
<IsPackable>true</IsPackable>
<EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Filters/**/*.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Jellyfin.Server.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/Configuration/*" />
</ItemGroup>
<!-- Code Analyzers -->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="IDisposableAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Jellyfin.Sdk" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Content Remove="startup.json.example" />
</ItemGroup>
<ItemGroup>
<None Include="startup.json.linux">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Include="startup.json.windows">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" />
<PackageReference Include="Microsoft.Kiota.Abstractions" />
<PackageReference Include="Morestachio" />
<PackageReference Include="prometheus-net" />
<PackageReference Include="prometheus-net.AspNetCore" />
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Enrichers.Thread" />
<PackageReference Include="Serilog.Expressions" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Async" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Sinks.Graylog" />
<!-- SQLite package for migration routines only (SQLite -> PostgreSQL) -->
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Jellyfin.Drawing\Jellyfin.Drawing.csproj" />
<ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
<ProjectReference Include="..\src\Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj" />
<ProjectReference Include="..\src\Jellyfin.LiveTv\Jellyfin.LiveTv.csproj" />
<ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />
<ProjectReference Include="..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />
<ProjectReference Include="..\src\Jellyfin.Database\Jellyfin.Database.Implementations\Jellyfin.Database.Implementations.csproj" />
<ProjectReference Include="..\src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres\Jellyfin.Database.Providers.Postgres.csproj" />
</ItemGroup>
<ItemGroup>
<!-- Web content and static files -->
<None Include="wwwroot\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Link>wwwroot\%(RecursiveDir)%(Filename)%(Extension)</Link>
</None>
<None Include="wwwroot\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Link>wwwroot\%(Filename)%(Extension)</Link>
</None>
<!-- ServerSetupApp files -->
<None Include="ServerSetupApp\index.mstemplate.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<!-- Runtime configuration for binding redirects -->
</ItemGroup>
<ItemGroup>
<!-- SQL initialization scripts for PostgreSQL -->
<None Include="..\scripts\sql\**\*.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Link>sql\%(RecursiveDir)%(Filename)%(Extension)</Link>
</None>
<!-- Utility scripts (excluding SQL files) -->
<None Include="..\scripts\**\*" Exclude="..\scripts\sql\**\*.sql">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Link>scripts\%(RecursiveDir)%(Filename)%(Extension)</Link>
</None>
</ItemGroup>
</Project>
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Emby.Photos</name>
</assembly>
<members>
<member name="T:Emby.Photos.PhotoProvider">
<summary>
Metadata provider for photos.
</summary>
<param name="logger">The logger.</param>
<param name="imageProcessor">The image processor.</param>
</member>
<member name="M:Emby.Photos.PhotoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{Emby.Photos.PhotoProvider},MediaBrowser.Controller.Drawing.IImageProcessor)">
<summary>
Metadata provider for photos.
</summary>
<param name="logger">The logger.</param>
<param name="imageProcessor">The image processor.</param>
</member>
<member name="P:Emby.Photos.PhotoProvider.Name">
<inheritdoc />
</member>
<member name="M:Emby.Photos.PhotoProvider.HasChanged(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="M:Emby.Photos.PhotoProvider.FetchAsync(MediaBrowser.Controller.Entities.Photo,MediaBrowser.Controller.Providers.MetadataRefreshOptions,System.Threading.CancellationToken)">
<inheritdoc />
</member>
</members>
</doc>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.CodeAnalysis</name>
</assembly>
<members>
<member name="T:Jellyfin.CodeAnalysis.AsyncDisposalPatternAnalyzer">
<summary>
Analyzer to detect sync disposal of async-created IAsyncDisposable objects.
</summary>
</member>
<member name="F:Jellyfin.CodeAnalysis.AsyncDisposalPatternAnalyzer.AsyncDisposableSyncDisposal">
<summary>
Diagnostic descriptor for sync disposal of async-created IAsyncDisposable objects.
</summary>
</member>
<member name="P:Jellyfin.CodeAnalysis.AsyncDisposalPatternAnalyzer.SupportedDiagnostics">
<inheritdoc/>
</member>
<member name="M:Jellyfin.CodeAnalysis.AsyncDisposalPatternAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext)">
<inheritdoc/>
</member>
</members>
</doc>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,519 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.Database.Providers.Postgres</name>
</assembly>
<members>
<member name="T:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions">
<summary>
Extension methods for optimized bulk operations on PostgreSQL.
These methods help reduce dead tuple generation during media library scans by batching
operations and using more efficient transaction patterns.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.GetRecommendedBatchSize(System.Int32)">
<summary>
Gets the recommended batch size for bulk operations based on the data size.
PostgreSQL works best with smaller batches to reduce dead tuples during scans.
</summary>
<param name="entityCount">The total number of entities to process.</param>
<returns>The recommended batch size.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.SaveChangesInBatchesAsync(Microsoft.EntityFrameworkCore.DbContext,System.Int32,System.Threading.CancellationToken)">
<summary>
Saves changes in batches to reduce dead tuple generation during bulk operations.
This is especially useful during media library scans where many items are created/updated/deleted.
</summary>
<param name="context">The database context.</param>
<param name="batchSize">The batch size for SaveChanges calls. If 0 or negative, uses automatic sizing.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>Total number of changes saved.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.BulkDeleteAsync(Microsoft.EntityFrameworkCore.DbContext,System.String,System.String,System.String,System.Threading.CancellationToken)">
<summary>
Deletes entities in batches using raw SQL for better performance.
This reduces dead tuple generation compared to loading and deleting entities individually.
</summary>
<param name="context">The database context.</param>
<param name="schema">The PostgreSQL schema name.</param>
<param name="tableName">The table name.</param>
<param name="whereClause">Optional WHERE clause (without WHERE keyword). If null, all rows are deleted.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>The number of rows deleted.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.BulkTruncateAsync(Microsoft.EntityFrameworkCore.DbContext,System.String,System.String,System.Boolean,System.Threading.CancellationToken)">
<summary>
Truncates a table completely, which is much faster than DELETE for large tables.
Note: This cannot be used if there are foreign key references.
</summary>
<param name="context">The database context.</param>
<param name="schema">The PostgreSQL schema name.</param>
<param name="tableName">The table name.</param>
<param name="cascadeDelete">If true, will CASCADE delete related rows. Use with caution.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.BulkAddAsync``1(Microsoft.EntityFrameworkCore.DbContext,System.Collections.Generic.IEnumerable{``0},System.Int32,System.Boolean,System.Threading.CancellationToken)">
<summary>
Adds a large collection of entities to the context in batches to avoid memory issues.
</summary>
<typeparam name="T">The entity type.</typeparam>
<param name="context">The database context.</param>
<param name="entities">The entities to add.</param>
<param name="batchSize">The batch size for adding entities.</param>
<param name="saveAfterEachBatch">Whether to save changes after each batch.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>Total number of entities saved.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.BulkUpdateAsync``1(Microsoft.EntityFrameworkCore.DbContext,System.Collections.Generic.IEnumerable{``0},System.Int32,System.Threading.CancellationToken)">
<summary>
Updates entities in batches to reduce dead tuple generation.
</summary>
<typeparam name="T">The entity type.</typeparam>
<param name="context">The database context.</param>
<param name="entities">The entities to update.</param>
<param name="batchSize">The batch size for updates.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>Total number of entities updated.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.BulkRemoveAsync``1(Microsoft.EntityFrameworkCore.DbContext,System.Collections.Generic.IEnumerable{``0},System.Int32,System.Threading.CancellationToken)">
<summary>
Removes entities in batches to reduce dead tuple generation.
</summary>
<typeparam name="T">The entity type.</typeparam>
<param name="context">The database context.</param>
<param name="entities">The entities to remove.</param>
<param name="batchSize">The batch size for removals.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>Total number of entities removed.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationExtensions.VacuumTableAsync(Microsoft.EntityFrameworkCore.DbContext,System.String,System.String,System.Threading.CancellationToken)">
<summary>
Executes VACUUM ANALYZE on a specific table to reclaim dead tuples and update statistics.
This is useful to call after bulk deletion operations.
</summary>
<param name="context">The database context.</param>
<param name="schema">The PostgreSQL schema name.</param>
<param name="tableName">The table name.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction">
<summary>
Helper class for managing transactions during bulk operations to reduce dead tuple generation.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.#ctor(Jellyfin.Database.Implementations.JellyfinDbContext)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction"/> class.
</summary>
<param name="context">The database context.</param>
</member>
<member name="P:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.IsCommitted">
<summary>
Gets a value indicating whether the transaction has been committed.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.BeginAsync(System.Threading.CancellationToken)">
<summary>
Begins a new transaction with isolation level optimized for bulk operations.
</summary>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.CommitAsync(System.Threading.CancellationToken)">
<summary>
Commits the transaction.
</summary>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.RollbackAsync(System.Threading.CancellationToken)">
<summary>
Rolls back the transaction.
</summary>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.Dispose">
<summary>
Disposes the transaction if not already committed.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkOperationTransaction.DisposeAsync">
<summary>
Disposes the transaction asynchronously if not already committed.
</summary>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.BulkTransactionExtensions">
<summary>
Extension methods for managing bulk operation transactions.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkTransactionExtensions.ExecuteBulkOperationAsync(Jellyfin.Database.Implementations.JellyfinDbContext,System.Func{System.Threading.Tasks.Task},System.Threading.CancellationToken)">
<summary>
Executes a bulk operation within a transaction.
</summary>
<param name="context">The database context.</param>
<param name="operation">The bulk operation to execute.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.BulkTransactionExtensions.ExecuteBulkOperationAsync``1(Jellyfin.Database.Implementations.JellyfinDbContext,System.Func{System.Threading.Tasks.Task{``0}},System.Threading.CancellationToken)">
<summary>
Executes a bulk operation within a transaction and returns a result.
</summary>
<typeparam name="T">The type of result returned by the operation.</typeparam>
<param name="context">The database context.</param>
<param name="operation">The bulk operation to execute.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>The result of the operation.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException">
<summary>
Represents an error that occurs during the execution of a PostgreSQL subprocess (e.g., psql, pg_dump).
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException"/> class.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException.#ctor(System.String)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException"/> class with a specified error message.
</summary>
<param name="message">The message that describes the error.</param>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException.#ctor(System.String,System.Exception)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresSubprocessException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
</summary>
<param name="message">The error message that explains the reason for the exception.</param>
<param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException">
<summary>
Exception thrown when there is a version mismatch between PostgreSQL server and client tools (pg_dump/pg_restore).
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException.#ctor(System.String,System.String)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException"/> class.
</summary>
<param name="serverVersion">The PostgreSQL server version.</param>
<param name="clientVersion">The pg_dump/pg_restore client version.</param>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException.#ctor(System.String,System.String,System.Exception)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException"/> class.
</summary>
<param name="serverVersion">The PostgreSQL server version.</param>
<param name="clientVersion">The pg_dump/pg_restore client version.</param>
<param name="innerException">The inner exception.</param>
</member>
<member name="P:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException.ServerVersion">
<summary>
Gets the PostgreSQL server version.
</summary>
</member>
<member name="P:Jellyfin.Database.Providers.Postgres.Exceptions.PostgresVersionMismatchException.ClientVersion">
<summary>
Gets the pg_dump/pg_restore client version.
</summary>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.InitialCreate">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.InitialCreate.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.InitialCreate.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.InitialCreate.BuildTargetModel(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.AddBasePerformanceIndexes">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddBasePerformanceIndexes.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddBasePerformanceIndexes.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.AddSupplementaryIndexes">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddSupplementaryIndexes.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddSupplementaryIndexes.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.AddLibraryOptionsTable">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddLibraryOptionsTable.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.AddLibraryOptionsTable.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.SplitMediaStreamInfos">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.SplitMediaStreamInfos.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.SplitMediaStreamInfos.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.DropBaseItemFlatExtensionColumns">
<summary>
Removes the 17 flat extension columns from library.BaseItems now that all data has been
migrated to the BaseItemTvExtras, BaseItemLiveTvExtras, and BaseItemAudioExtras tables.
Must run AFTER SplitBaseItemExtras (20260503000000).
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.DropBaseItemFlatExtensionColumns.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.DropBaseItemFlatExtensionColumns.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.PostgresDesignTimeJellyfinDbFactory">
<summary>
The design time factory for <see cref="T:Jellyfin.Database.Implementations.JellyfinDbContext"/>.
This is only used for the creation of migrations and not during runtime.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.PostgresDesignTimeJellyfinDbFactory.CreateDbContext(System.String[])">
<inheritdoc/>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.Migrations.SplitBaseItemExtras">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.SplitBaseItemExtras.Up(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.Migrations.SplitBaseItemExtras.Down(Microsoft.EntityFrameworkCore.Migrations.MigrationBuilder)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.ModelBuilderExtensions">
<summary>
Model builder extensions for PostgreSQL.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.ModelBuilderExtensions.UseValueConverterForType``1(Microsoft.EntityFrameworkCore.ModelBuilder,Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter)">
<summary>
Specify value converter for the object type.
</summary>
<param name="modelBuilder">The model builder.</param>
<param name="converter">The <see cref="T:Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter`2"/>.</param>
<typeparam name="T">The type to convert.</typeparam>
<returns>The modified <see cref="T:Microsoft.EntityFrameworkCore.ModelBuilder"/>.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.ModelBuilderExtensions.SetDefaultDateTimeKind(Microsoft.EntityFrameworkCore.ModelBuilder,System.DateTimeKind)">
<summary>
Specify the default <see cref="T:System.DateTimeKind"/>.
</summary>
<param name="modelBuilder">The model builder to extend.</param>
<param name="kind">The <see cref="T:System.DateTimeKind"/> to specify.</param>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.PostgresBackupService">
<summary>
Service for creating and restoring PostgreSQL database backups using pg_dump/pg_restore.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresBackupService.#ctor(Microsoft.Extensions.Logging.ILogger{Jellyfin.Database.Providers.Postgres.PostgresBackupService},MediaBrowser.Common.Configuration.IConfigurationManager)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.PostgresBackupService"/> class.
</summary>
<param name="logger">The logger.</param>
<param name="configurationManager">The configuration manager.</param>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresBackupService.CreateBackupAsync(System.String,System.Threading.CancellationToken)">
<summary>
Creates a backup of the PostgreSQL database.
</summary>
<param name="backupDirectory">The directory to save the backup file.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>The path to the created backup file.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresBackupService.RestoreBackupAsync(System.String,System.Threading.CancellationToken)">
<summary>
Restores a PostgreSQL database from a backup file.
</summary>
<param name="backupFilePath">The path to the backup file.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the restore operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresBackupService.ParseVersionMismatchError(System.String)">
<summary>
Parses PostgreSQL version mismatch error message to extract server and client versions.
</summary>
<param name="errorMessage">The error message from pg_dump.</param>
<returns>A tuple containing server version and client version.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider">
<summary>
Configures Jellyfin to use a PostgreSQL database.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.#ctor(MediaBrowser.Common.Configuration.IApplicationPaths,Microsoft.Extensions.Logging.ILogger{Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider},MediaBrowser.Common.Configuration.IConfigurationManager)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider"/> class.
</summary>
<param name="applicationPaths">Service to construct the fallback when the old data path configuration is used.</param>
<param name="logger">A logger.</param>
<param name="configurationManager">The configuration manager (optional, for backup support).</param>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas">
<summary>
Schema names mapping to legacy database files.
</summary>
</member>
<member name="F:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.ActivityLog">
<summary>
Schema for activity log tables (legacy: activitylog.db).
</summary>
</member>
<member name="F:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.Authentication">
<summary>
Schema for authentication tables (legacy: authentication.db).
</summary>
</member>
<member name="F:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.DisplayPreferences">
<summary>
Schema for display preferences tables (legacy: displaypreferences.db).
</summary>
</member>
<member name="F:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.Library">
<summary>
Schema for library tables (legacy: library.db).
</summary>
</member>
<member name="F:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.Users">
<summary>
Schema for user tables (legacy: users.db).
</summary>
</member>
<member name="P:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Schemas.All">
<summary>
Gets all schema names.
</summary>
</member>
<member name="P:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.DbContextFactory">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.Initialise(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder,Jellyfin.Database.Implementations.DbConfiguration.DatabaseConfigurationOptions)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.EnsureDatabaseExistsAsync(Jellyfin.Database.Implementations.DbConfiguration.DatabaseConfigurationOptions,System.Threading.CancellationToken)">
<summary>
Ensures the PostgreSQL database exists, creating it if necessary.
</summary>
<param name="databaseConfiguration">The database configuration.</param>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.EnsureSchemasExistAsync(System.String,System.Int32,System.String,System.String,System.String,System.Int32,System.Threading.CancellationToken)">
<summary>
Ensures all required schemas exist in the PostgreSQL database.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.EnsureTablesExistAsync(System.Threading.CancellationToken)">
<summary>
Ensures all required database tables exist by applying pending migrations.
</summary>
<param name="cancellationToken">Cancellation token.</param>
<returns>A task representing the asynchronous operation.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.RunScheduledOptimisation(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.AssignEntitiesToSchemas(Microsoft.EntityFrameworkCore.ModelBuilder)">
<summary>
Assigns entities to PostgreSQL schemas based on their legacy database origin.
</summary>
<param name="modelBuilder">The model builder.</param>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.RegisterViewMappings(Microsoft.EntityFrameworkCore.ModelBuilder)">
<summary>
Registers read-only keyless view projections for PostgreSQL.
Each view lives in the <c>library</c> schema and is mapped with
<c>HasNoKey().ToView()</c> so EF Core never tries to create a table or migration for it.
</summary>
<param name="modelBuilder">The model builder.</param>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.RunShutdownTask(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.ConfigureConventions(Microsoft.EntityFrameworkCore.ModelConfigurationBuilder)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.MigrationBackupFast(System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.RestoreBackupFast(System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.DeleteBackup(System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.IsLocalHost(System.String)">
<summary>
Determines if the given host is localhost.
</summary>
<param name="host">The host to check.</param>
<returns>True if the host is localhost or 127.0.0.1, false otherwise.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.PurgeDatabase(Jellyfin.Database.Implementations.JellyfinDbContext,System.Collections.Generic.IEnumerable{System.String})">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.FindSchemaScriptPath">
<summary>
Finds the full path to the schema initialization script.
</summary>
<returns>The full path to the script, or null if not found.</returns>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.PostgresDatabaseProvider.FindPsqlPath">
<summary>
Finds the path to the psql executable.
</summary>
<returns>The path to psql, or just "psql" if it's in the system PATH.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.SnakeCaseNamingConvention">
<summary>
EF Core convention that maps all entity property names to snake_case column names.
Applied only by the PostgreSQL provider so SQLite is unaffected.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.SnakeCaseNamingConvention.ProcessPropertyAdded(Microsoft.EntityFrameworkCore.Metadata.Builders.IConventionPropertyBuilder,Microsoft.EntityFrameworkCore.Metadata.Conventions.IConventionContext{Microsoft.EntityFrameworkCore.Metadata.Builders.IConventionPropertyBuilder})">
<inheritdoc />
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.SnakeCaseNamingConvention.ToSnakeCase(System.String)">
<summary>Converts a PascalCase identifier to snake_case.</summary>
<param name="name">The identifier to convert.</param>
<returns>The snake_case representation of the identifier.</returns>
</member>
<member name="T:Jellyfin.Database.Providers.Postgres.ValueConverters.DateTimeKindValueConverter">
<summary>
ValueConverter to specify DateTime kind for PostgreSQL.
</summary>
</member>
<member name="M:Jellyfin.Database.Providers.Postgres.ValueConverters.DateTimeKindValueConverter.#ctor(System.DateTimeKind,Microsoft.EntityFrameworkCore.Storage.ValueConversion.ConverterMappingHints)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Database.Providers.Postgres.ValueConverters.DateTimeKindValueConverter"/> class.
</summary>
<param name="kind">The kind to specify.</param>
<param name="mappingHints">The mapping hints.</param>
</member>
</members>
</doc>
+352
View File
@@ -0,0 +1,352 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.Drawing.Skia</name>
</assembly>
<members>
<member name="T:Jellyfin.Drawing.Skia.PercentPlayedDrawer">
<summary>
Static helper class used to draw percentage-played indicators on images.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.PercentPlayedDrawer.Process(SkiaSharp.SKCanvas,MediaBrowser.Model.Drawing.ImageDimensions,System.Double)">
<summary>
Draw a percentage played indicator on a canvas.
</summary>
<param name="canvas">The canvas to draw the indicator on.</param>
<param name="imageSize">The size of the image being drawn on.</param>
<param name="percent">The percentage played to display with the indicator.</param>
</member>
<member name="T:Jellyfin.Drawing.Skia.SkiaEncoder">
<summary>
Image encoder that uses <see cref="N:SkiaSharp"/> to manipulate images.
</summary>
</member>
<member name="F:Jellyfin.Drawing.Skia.SkiaEncoder.UpscaleSamplingOptions">
<summary>
The default sampling options, equivalent to old high quality filter settings when upscaling.
</summary>
</member>
<member name="F:Jellyfin.Drawing.Skia.SkiaEncoder.DefaultSamplingOptions">
<summary>
The sampling options, used for downscaling images, equivalent to old high quality filter settings when not upscaling.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.#ctor(Microsoft.Extensions.Logging.ILogger{Jellyfin.Drawing.Skia.SkiaEncoder},MediaBrowser.Common.Configuration.IApplicationPaths)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Drawing.Skia.SkiaEncoder"/> class.
</summary>
<param name="logger">The application logger.</param>
<param name="appPaths">The application paths.</param>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.Name">
<inheritdoc/>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.SupportsImageCollageCreation">
<inheritdoc/>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.SupportsImageEncoding">
<inheritdoc/>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.SupportedInputFormats">
<inheritdoc/>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.SupportedOutputFormats">
<inheritdoc/>
</member>
<member name="P:Jellyfin.Drawing.Skia.SkiaEncoder.DefaultTypeFace">
<summary>
Gets the default typeface to use.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.IsNativeLibAvailable">
<summary>
Check if the native lib is available.
</summary>
<returns>True if the native lib is available, otherwise false.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.GetImageFormat(MediaBrowser.Model.Drawing.ImageFormat)">
<summary>
Convert a <see cref="T:MediaBrowser.Model.Drawing.ImageFormat"/> to a <see cref="T:SkiaSharp.SKEncodedImageFormat"/>.
</summary>
<param name="selectedFormat">The format to convert.</param>
<returns>The converted format.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.GetImageSize(System.String)">
<inheritdoc />
<exception cref="T:System.IO.FileNotFoundException">The path is not valid.</exception>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.GetImageBlurHash(System.Int32,System.Int32,System.String)">
<inheritdoc />
<exception cref="T:System.ArgumentNullException">The path is null.</exception>
<exception cref="T:System.IO.FileNotFoundException">The path is not valid.</exception>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.Decode(System.String,System.Boolean,System.Nullable{MediaBrowser.Model.Drawing.ImageOrientation},SkiaSharp.SKEncodedOrigin@)">
<summary>
Decode an image.
</summary>
<param name="path">The filepath of the image to decode.</param>
<param name="forceCleanBitmap">Whether to force clean the bitmap.</param>
<param name="orientation">The orientation of the image.</param>
<param name="origin">The detected origin of the image.</param>
<returns>The resulting bitmap of the image.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.ResizeImage(SkiaSharp.SKBitmap,SkiaSharp.SKImageInfo,System.Boolean,System.Boolean)">
<summary>
Resizes an image on the CPU, by utilizing a surface and canvas.
The convolutional matrix kernel used in this resize function gives a (light) sharpening effect.
This technique is similar to effect that can be created using for example the [Convolution matrix filter in GIMP](https://docs.gimp.org/2.10/en/gimp-filter-convolution-matrix.html).
</summary>
<param name="source">The source bitmap.</param>
<param name="targetInfo">This specifies the target size and other information required to create the surface.</param>
<param name="isAntialias">This enables anti-aliasing on the SKPaint instance.</param>
<param name="isDither">This enables dithering on the SKPaint instance.</param>
<returns>The resized image.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.EncodeImage(System.String,System.DateTime,System.String,System.Boolean,System.Nullable{MediaBrowser.Model.Drawing.ImageOrientation},System.Int32,MediaBrowser.Controller.Drawing.ImageProcessingOptions,MediaBrowser.Model.Drawing.ImageFormat)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.CreateImageCollage(MediaBrowser.Controller.Drawing.ImageCollageOptions,System.String)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.CreateSplashscreen(System.Collections.Generic.IReadOnlyList{System.String},System.Collections.Generic.IReadOnlyList{System.String})">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.CreateTrickplayTile(MediaBrowser.Controller.Drawing.ImageCollageOptions,System.Int32,System.Int32,System.Nullable{System.Int32})">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaEncoder.GetFontForCharacter(System.String)">
<summary>
Return the typeface that contains the glyph for the given character.
</summary>
<param name="c">The text character.</param>
<returns>The typeface contains the character.</returns>
</member>
<member name="T:Jellyfin.Drawing.Skia.SkiaExtensions">
<summary>
The SkiaSharp extensions.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaExtensions.DrawBitmap(SkiaSharp.SKCanvas,SkiaSharp.SKBitmap,SkiaSharp.SKRect,SkiaSharp.SKSamplingOptions,SkiaSharp.SKPaint)">
<summary>
Draws an SKBitmap on the canvas with specified SkSamplingOptions.
</summary>
<param name="canvas">The SKCanvas to draw on.</param>
<param name="bitmap">The SKBitmap to draw.</param>
<param name="dest">The destination SKRect.</param>
<param name="options">The SKSamplingOptions to use for rendering.</param>
<param name="paint">Optional SKPaint to apply additional effects or styles.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaExtensions.DrawBitmap(SkiaSharp.SKCanvas,SkiaSharp.SKBitmap,System.Single,System.Single,SkiaSharp.SKSamplingOptions,SkiaSharp.SKPaint)">
<summary>
Draws an SKBitmap on the canvas at the specified coordinates with the given SkSamplingOptions.
</summary>
<param name="canvas">The SKCanvas to draw on.</param>
<param name="bitmap">The SKBitmap to draw.</param>
<param name="x">The x-coordinate where the bitmap will be drawn.</param>
<param name="y">The y-coordinate where the bitmap will be drawn.</param>
<param name="options">The SKSamplingOptions to use for rendering.</param>
<param name="paint">Optional SKPaint to apply additional effects or styles.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaExtensions.DrawBitmap(SkiaSharp.SKCanvas,SkiaSharp.SKBitmap,SkiaSharp.SKRect,SkiaSharp.SKRect,SkiaSharp.SKSamplingOptions,SkiaSharp.SKPaint)">
<summary>
Draws an SKBitmap on the canvas using a specified source rectangle, destination rectangle,
and optional paint, with the given SkSamplingOptions.
</summary>
<param name="canvas">The SKCanvas to draw on.</param>
<param name="bitmap">The SKBitmap to draw.</param>
<param name="source">
The source SKRect defining the portion of the bitmap to draw.
</param>
<param name="dest">
The destination SKRect defining the area on the canvas where the bitmap will be drawn.
</param>
<param name="options">The SKSamplingOptions to use for rendering.</param>
<param name="paint">Optional SKPaint to apply additional effects or styles.</param>
</member>
<member name="T:Jellyfin.Drawing.Skia.SkiaHelper">
<summary>
Class containing helper methods for working with SkiaSharp.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.SkiaHelper.GetNextValidImage(Jellyfin.Drawing.Skia.SkiaEncoder,System.Collections.Generic.IReadOnlyList{System.String},System.Int32,System.Int32@)">
<summary>
Gets the next valid image as a bitmap.
</summary>
<param name="skiaEncoder">The current skia encoder.</param>
<param name="paths">The list of image paths.</param>
<param name="currentIndex">The current checked index.</param>
<param name="newIndex">The new index.</param>
<returns>A valid bitmap, or null if no bitmap exists after <c>currentIndex</c>.</returns>
</member>
<member name="T:Jellyfin.Drawing.Skia.SplashscreenBuilder">
<summary>
Used to build the splashscreen.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.SplashscreenBuilder.#ctor(Jellyfin.Drawing.Skia.SkiaEncoder,Microsoft.Extensions.Logging.ILogger)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Drawing.Skia.SplashscreenBuilder"/> class.
</summary>
<param name="skiaEncoder">The SkiaEncoder.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.SplashscreenBuilder.GenerateSplash(System.Collections.Generic.IReadOnlyList{System.String},System.Collections.Generic.IReadOnlyList{System.String},System.String)">
<summary>
Generate a splashscreen.
</summary>
<param name="posters">The poster paths.</param>
<param name="backdrops">The landscape paths.</param>
<param name="outputPath">The output path.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.SplashscreenBuilder.GenerateCollage(System.Collections.Generic.IReadOnlyList{System.String},System.Collections.Generic.IReadOnlyList{System.String})">
<summary>
Generates a collage of posters and landscape pictures.
</summary>
<param name="posters">The poster paths.</param>
<param name="backdrops">The landscape paths.</param>
<returns>The created collage as a bitmap.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.SplashscreenBuilder.Transform3D(SkiaSharp.SKBitmap)">
<summary>
Transform the collage in 3D space.
</summary>
<param name="input">The bitmap to transform.</param>
<returns>The transformed image.</returns>
</member>
<member name="T:Jellyfin.Drawing.Skia.StripCollageBuilder">
<summary>
Used to build collages of multiple images arranged in vertical strips.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.#ctor(Jellyfin.Drawing.Skia.SkiaEncoder)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Drawing.Skia.StripCollageBuilder"/> class.
</summary>
<param name="skiaEncoder">The encoder to use for building collages.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.IsRtlTextRegex">
<remarks>
Pattern:<br/>
<code>\p{IsArabic}|\p{IsArmenian}|\p{IsHebrew}|\p{IsSyriac}|\p{IsThaana}</code><br/>
Explanation:<br/>
<code>
○ Match a character in the set [\u0530-\u074F\u0780-\u07BF].<br/>
</code>
</remarks>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.GetEncodedFormat(System.String)">
<summary>
Check which format an image has been encoded with using its filename extension.
</summary>
<param name="outputPath">The path to the image to get the format for.</param>
<returns>The image format.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.BuildSquareCollage(System.Collections.Generic.IReadOnlyList{System.String},System.String,System.Int32,System.Int32)">
<summary>
Create a square collage.
</summary>
<param name="paths">The paths of the images to use in the collage.</param>
<param name="outputPath">The path at which to place the resulting collage image.</param>
<param name="width">The desired width of the collage.</param>
<param name="height">The desired height of the collage.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.BuildThumbCollage(System.Collections.Generic.IReadOnlyList{System.String},System.String,System.Int32,System.Int32,System.String)">
<summary>
Create a thumb collage.
</summary>
<param name="paths">The paths of the images to use in the collage.</param>
<param name="outputPath">The path at which to place the resulting image.</param>
<param name="width">The desired width of the collage.</param>
<param name="height">The desired height of the collage.</param>
<param name="libraryName">The name of the library to draw on the collage.</param>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.MeasureAndDrawText(SkiaSharp.SKCanvas,System.Single,System.Single,System.String,SkiaSharp.SKPaint,SkiaSharp.SKFont,SkiaSharp.SKTextAlign)">
<summary>
Draw shaped text with given SKPaint.
</summary>
<param name="canvas">If not null, draw text to this canvas, otherwise only measure the text width.</param>
<param name="x">x position of the canvas to draw text.</param>
<param name="y">y position of the canvas to draw text.</param>
<param name="text">The text to draw.</param>
<param name="textPaint">The SKPaint to style the text.</param>
<param name="textFont">The SKFont to style the text.</param>
<param name="alignment">The alignment of the text. Default aligns to left.</param>
<returns>The width of the text.</returns>
</member>
<member name="M:Jellyfin.Drawing.Skia.StripCollageBuilder.DrawText(SkiaSharp.SKCanvas,System.Single,System.Single,System.String,SkiaSharp.SKPaint,SkiaSharp.SKFont,System.Boolean)">
<summary>
Draw shaped text with given SKPaint, search defined type faces to render as many texts as possible.
</summary>
<param name="canvas">If not null, draw text to this canvas, otherwise only measure the text width.</param>
<param name="x">x position of the canvas to draw text.</param>
<param name="y">y position of the canvas to draw text.</param>
<param name="text">The text to draw.</param>
<param name="textPaint">The SKPaint to style the text.</param>
<param name="textFont">The SKFont to style the text.</param>
<param name="isRtl">If true, render from right to left.</param>
<returns>The width of the text.</returns>
</member>
<member name="T:Jellyfin.Drawing.Skia.UnplayedCountIndicator">
<summary>
Static helper class for drawing unplayed count indicators.
</summary>
</member>
<member name="F:Jellyfin.Drawing.Skia.UnplayedCountIndicator.OffsetFromTopRightCorner">
<summary>
The x-offset used when drawing an unplayed count indicator.
</summary>
</member>
<member name="M:Jellyfin.Drawing.Skia.UnplayedCountIndicator.DrawUnplayedCountIndicator(SkiaSharp.SKCanvas,MediaBrowser.Model.Drawing.ImageDimensions,System.Int32)">
<summary>
Draw an unplayed count indicator in the top right corner of a canvas.
</summary>
<param name="canvas">The canvas to draw the indicator on.</param>
<param name="imageSize">
The dimensions of the image to draw the indicator on. The width is used to determine the x-position of the
indicator.
</param>
<param name="count">The number to draw in the indicator.</param>
</member>
<member name="T:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0">
<summary>Custom <see cref="T:System.Text.RegularExpressions.Regex"/>-derived type for the IsRtlTextRegex method.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.Instance">
<summary>Cached, thread-safe singleton instance.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.#ctor">
<summary>Initializes the instance.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.RunnerFactory">
<summary>Provides a factory for creating <see cref="T:System.Text.RegularExpressions.RegexRunner"/> instances to be used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.RunnerFactory.CreateInstance">
<summary>Creates an instance of a <see cref="T:System.Text.RegularExpressions.RegexRunner"/> used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.RunnerFactory.Runner">
<summary>Provides the runner that contains the custom logic implementing the specified regular expression.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.RunnerFactory.Runner.Scan(System.ReadOnlySpan{System.Char})">
<summary>Scan the <paramref name="inputSpan"/> starting from base.runtextstart for the next match.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
</member>
<member name="M:System.Text.RegularExpressions.Generated.IsRtlTextRegex_0.RunnerFactory.Runner.TryFindNextPossibleStartingPosition(System.ReadOnlySpan{System.Char})">
<summary>Search <paramref name="inputSpan"/> starting from base.runtextpos for the next location a match could possibly start.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
<returns>true if a possible match was found; false if no more matches are possible.</returns>
</member>
<member name="T:System.Text.RegularExpressions.Generated.Utilities">
<summary>Helper methods used by generated <see cref="T:System.Text.RegularExpressions.Regex"/>-derived implementations.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout">
<summary>Default timeout value set in <see cref="T:System.AppContext"/>, or <see cref="F:System.Text.RegularExpressions.Regex.InfiniteMatchTimeout"/> if none was set.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_hasTimeout">
<summary>Whether <see cref="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout"/> is non-infinite.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.Utilities.IndexOfNonAsciiOrAny_1861681229F79B552251C84FF351AFD5B7682E7A5D071E5C5F2BB68943FFB41B(System.ReadOnlySpan{System.Char})">
<summary>Finds the next index of any character that matches a character in the set [\u0530-\u074F\u0780-\u07BF].</summary>
</member>
</members>
</doc>
+143
View File
@@ -0,0 +1,143 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.Drawing</name>
</assembly>
<members>
<member name="T:Jellyfin.Drawing.ImageProcessor">
<summary>
Class ImageProcessor.
</summary>
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.#ctor(Microsoft.Extensions.Logging.ILogger{Jellyfin.Drawing.ImageProcessor},MediaBrowser.Controller.IServerApplicationPaths,MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Drawing.IImageEncoder,MediaBrowser.Controller.Configuration.IServerConfigurationManager)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Drawing.ImageProcessor"/> class.
</summary>
<param name="logger">The logger.</param>
<param name="appPaths">The server application paths.</param>
<param name="fileSystem">The filesystem.</param>
<param name="imageEncoder">The image encoder.</param>
<param name="config">The configuration.</param>
</member>
<member name="P:Jellyfin.Drawing.ImageProcessor.SupportedInputFormats">
<inheritdoc />
</member>
<member name="P:Jellyfin.Drawing.ImageProcessor.SupportsImageCollageCreation">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetSupportedImageOutputFormats">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.ProcessImage(MediaBrowser.Controller.Drawing.ImageProcessingOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetCacheFilePath(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Nullable{System.Int32},System.Nullable{System.Int32},System.Nullable{System.Int32},System.Nullable{System.Int32},System.Int32,System.DateTime,MediaBrowser.Model.Drawing.ImageFormat,System.Double,System.Nullable{System.Int32},System.Nullable{System.Int32},System.String,System.String)">
<summary>
Gets the cache file path based on a set of parameters.
</summary>
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageDimensions(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Entities.ItemImageInfo)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageDimensions(System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageBlurHash(System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageBlurHash(System.String,MediaBrowser.Model.Drawing.ImageDimensions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(System.String,System.DateTime)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Entities.ItemImageInfo)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(MediaBrowser.Model.Dto.BaseItemDto,MediaBrowser.Controller.Entities.ItemImageInfo)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(MediaBrowser.Model.Dto.BaseItemDto,MediaBrowser.Model.Entities.ChapterInfo)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Model.Entities.ChapterInfo)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetImageCacheTag(Jellyfin.Database.Implementations.Entities.User)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetCachePath(System.String,System.String,System.String)">
<summary>
Gets the cache path.
</summary>
<param name="path">The path.</param>
<param name="uniqueName">Name of the unique.</param>
<param name="fileExtension">The file extension.</param>
<returns>System.String.</returns>
<exception cref="T:System.ArgumentNullException">
path
or
uniqueName
or
fileExtension.
</exception>
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.GetCachePath(System.ReadOnlySpan{System.Char},System.ReadOnlySpan{System.Char})">
<summary>
Gets the cache path.
</summary>
<param name="path">The path.</param>
<param name="filename">The filename.</param>
<returns>System.String.</returns>
<exception cref="T:System.ArgumentNullException">
path
or
filename.
</exception>
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.CreateImageCollage(MediaBrowser.Controller.Drawing.ImageCollageOptions,System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.ImageProcessor.Dispose">
<inheritdoc />
</member>
<member name="T:Jellyfin.Drawing.NullImageEncoder">
<summary>
A fallback implementation of <see cref="T:MediaBrowser.Controller.Drawing.IImageEncoder" />.
</summary>
</member>
<member name="P:Jellyfin.Drawing.NullImageEncoder.SupportedInputFormats">
<inheritdoc />
</member>
<member name="P:Jellyfin.Drawing.NullImageEncoder.SupportedOutputFormats">
<inheritdoc />
</member>
<member name="P:Jellyfin.Drawing.NullImageEncoder.Name">
<inheritdoc />
</member>
<member name="P:Jellyfin.Drawing.NullImageEncoder.SupportsImageCollageCreation">
<inheritdoc />
</member>
<member name="P:Jellyfin.Drawing.NullImageEncoder.SupportsImageEncoding">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.GetImageSize(System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.EncodeImage(System.String,System.DateTime,System.String,System.Boolean,System.Nullable{MediaBrowser.Model.Drawing.ImageOrientation},System.Int32,MediaBrowser.Controller.Drawing.ImageProcessingOptions,MediaBrowser.Model.Drawing.ImageFormat)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.CreateImageCollage(MediaBrowser.Controller.Drawing.ImageCollageOptions,System.String)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.CreateSplashscreen(System.Collections.Generic.IReadOnlyList{System.String},System.Collections.Generic.IReadOnlyList{System.String})">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.CreateTrickplayTile(MediaBrowser.Controller.Drawing.ImageCollageOptions,System.Int32,System.Int32,System.Nullable{System.Int32})">
<inheritdoc />
</member>
<member name="M:Jellyfin.Drawing.NullImageEncoder.GetImageBlurHash(System.Int32,System.Int32,System.String)">
<inheritdoc />
</member>
</members>
</doc>
+716
View File
@@ -0,0 +1,716 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.Extensions</name>
</assembly>
<members>
<member name="T:Jellyfin.Extensions.CopyToExtensions">
<summary>
Provides <c>CopyTo</c> extensions methods for <see cref="T:System.Collections.Generic.IReadOnlyList`1" />.
</summary>
</member>
<member name="M:Jellyfin.Extensions.CopyToExtensions.CopyTo``1(System.Collections.Generic.IReadOnlyList{``0},System.Collections.Generic.IList{``0},System.Int32)">
<summary>
Copies all the elements of the current collection to the specified list
starting at the specified destination array index. The index is specified as a 32-bit integer.
</summary>
<param name="source">The current collection that is the source of the elements.</param>
<param name="destination">The list that is the destination of the elements copied from the current collection.</param>
<param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
<typeparam name="T">The type of the array.</typeparam>
</member>
<member name="T:Jellyfin.Extensions.DictionaryExtensions">
<summary>
Static extensions for the <see cref="T:System.Collections.Generic.IReadOnlyDictionary`2"/> interface.
</summary>
</member>
<member name="M:Jellyfin.Extensions.DictionaryExtensions.GetFirstNotNullNorWhiteSpaceValue(System.Collections.Generic.IReadOnlyDictionary{System.String,System.String},System.String,System.String,System.String,System.String)">
<summary>
Gets a string from a string dictionary, checking all keys sequentially,
stopping at the first key that returns a result that's neither null nor blank.
</summary>
<param name="dictionary">The dictionary.</param>
<param name="key1">The first checked key.</param>
<param name="key2">The second checked key.</param>
<param name="key3">The third checked key.</param>
<param name="key4">The fourth checked key.</param>
<returns>System.String.</returns>
</member>
<member name="T:Jellyfin.Extensions.EnumerableExtensions">
<summary>
Static extensions for the <see cref="T:System.Collections.Generic.IEnumerable`1"/> interface.
</summary>
</member>
<member name="M:Jellyfin.Extensions.EnumerableExtensions.Contains(System.Collections.Generic.IEnumerable{System.String},System.ReadOnlySpan{System.Char},System.StringComparison)">
<summary>
Determines whether the value is contained in the source collection.
</summary>
<param name="source">An instance of the <see cref="T:System.Collections.Generic.IEnumerable`1"/> interface.</param>
<param name="value">The value to look for in the collection.</param>
<param name="stringComparison">The string comparison.</param>
<returns>A value indicating whether the value is contained in the collection.</returns>
<exception cref="T:System.ArgumentNullException">The source is null.</exception>
</member>
<member name="M:Jellyfin.Extensions.EnumerableExtensions.SingleItemAsEnumerable``1(``0)">
<summary>
Gets an IEnumerable from a single item.
</summary>
<param name="item">The item to return.</param>
<typeparam name="T">The type of item.</typeparam>
<returns>The IEnumerable{T}.</returns>
</member>
<member name="M:Jellyfin.Extensions.EnumerableExtensions.GetUniqueFlags``1(``0)">
<summary>
Gets an IEnumerable consisting of all flags of an enum.
</summary>
<param name="flags">The flags enum.</param>
<typeparam name="T">The type of item.</typeparam>
<returns>The IEnumerable{Enum}.</returns>
</member>
<member name="T:Jellyfin.Extensions.FileHelper">
<summary>
Provides helper functions for <see cref="T:System.IO.File" />.
</summary>
</member>
<member name="M:Jellyfin.Extensions.FileHelper.CreateEmpty(System.String)">
<summary>
Creates, or truncates a file in the specified path.
</summary>
<param name="path">The path and name of the file to create.</param>
</member>
<member name="T:Jellyfin.Extensions.FormattingStreamWriter">
<summary>
A custom StreamWriter which supports setting a IFormatProvider.
</summary>
</member>
<member name="M:Jellyfin.Extensions.FormattingStreamWriter.#ctor(System.IO.Stream,System.IFormatProvider)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.FormattingStreamWriter"/> class.
</summary>
<param name="stream">The stream to write to.</param>
<param name="formatProvider">The format provider to use.</param>
</member>
<member name="M:Jellyfin.Extensions.FormattingStreamWriter.#ctor(System.String,System.IFormatProvider)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.FormattingStreamWriter"/> class.
</summary>
<param name="path">The complete file path to write to.</param>
<param name="formatProvider">The format provider to use.</param>
</member>
<member name="P:Jellyfin.Extensions.FormattingStreamWriter.FormatProvider">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.GuidExtensions">
<summary>
Guid specific extensions.
</summary>
</member>
<member name="M:Jellyfin.Extensions.GuidExtensions.IsEmpty(System.Guid)">
<summary>
Determine whether the guid is default.
</summary>
<param name="guid">The guid.</param>
<returns>Whether the guid is the default value.</returns>
</member>
<member name="M:Jellyfin.Extensions.GuidExtensions.IsNullOrEmpty(System.Nullable{System.Guid})">
<summary>
Determine whether the guid is null or default.
</summary>
<param name="guid">The guid.</param>
<returns>Whether the guid is null or the default valueF.</returns>
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonBoolNumberConverter">
<summary>
Converts a number to a boolean.
This is needed for HDHomerun.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonBoolNumberConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonBoolNumberConverter.Write(System.Text.Json.Utf8JsonWriter,System.Boolean,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonBoolStringConverter">
<summary>
Converts a string to a boolean.
This is needed for FFprobe.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonBoolStringConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonBoolStringConverter.Write(System.Text.Json.Utf8JsonWriter,System.Boolean,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverter`1">
<summary>
Convert comma delimited string to collection of type.
</summary>
<typeparam name="T">Type to convert to.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverter`1.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverter`1"/> class.
</summary>
</member>
<member name="P:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverter`1.Delimiter">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverterFactory">
<summary>
Json comma delimited collection converter factory.
</summary>
<remarks>
This must be applied as an attribute, adding to the JsonConverter list causes stack overflow.
</remarks>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverterFactory.CanConvert(System.Type)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonCommaDelimitedCollectionConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonDateTimeConverter">
<summary>
Legacy DateTime converter.
Milliseconds aren't output if zero by default.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDateTimeConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDateTimeConverter.Write(System.Text.Json.Utf8JsonWriter,System.DateTime,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverter`1">
<summary>
Json unknown enum converter.
</summary>
<typeparam name="T">The type of enum.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverter`1.#ctor(System.Text.Json.Serialization.JsonConverter{`0})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverter`1"/> class.
</summary>
<param name="baseConverter">The base json converter.</param>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverter`1.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverter`1.Write(System.Text.Json.Utf8JsonWriter,`0,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverterFactory">
<summary>
Utilizes the JsonStringEnumConverter and sets a default value if not provided.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverterFactory.CanConvert(System.Type)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDefaultStringEnumConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1">
<summary>
Convert delimited string to array of type.
</summary>
<typeparam name="T">Type to convert to.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1"/> class.
</summary>
</member>
<member name="P:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1.Delimiter">
<summary>
Gets the array delimiter.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonDelimitedCollectionConverter`1.Write(System.Text.Json.Utf8JsonWriter,System.Collections.Generic.IReadOnlyCollection{`0},System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverter`1">
<summary>
Enum flag to json array converter.
</summary>
<typeparam name="T">The type of enum.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverter`1.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverter`1.Write(System.Text.Json.Utf8JsonWriter,`0,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverterFactory">
<summary>
Json flag enum converter factory.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverterFactory.CanConvert(System.Type)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonFlagEnumConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonGuidConverter">
<summary>
Converts a GUID object or value to/from JSON.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonGuidConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonGuidConverter.Write(System.Text.Json.Utf8JsonWriter,System.Guid,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonNullableGuidConverter">
<summary>
Converts a GUID object or value to/from JSON.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableGuidConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableGuidConverter.Write(System.Text.Json.Utf8JsonWriter,System.Nullable{System.Guid},System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverter`1">
<summary>
Converts a nullable struct or value to/from JSON.
Required - some clients send an empty string.
</summary>
<typeparam name="TStruct">The struct type.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverter`1.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverter`1.Write(System.Text.Json.Utf8JsonWriter,System.Nullable{`0},System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverterFactory">
<summary>
Json nullable struct converter factory.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverterFactory.CanConvert(System.Type)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonNullableStructConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverter`1">
<summary>
Convert Pipe delimited string to array of type.
</summary>
<typeparam name="T">Type to convert to.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverter`1.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverter`1"/> class.
</summary>
</member>
<member name="P:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverter`1.Delimiter">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverterFactory">
<summary>
Json Pipe delimited collection converter factory.
</summary>
<remarks>
This must be applied as an attribute, adding to the JsonConverter list causes stack overflow.
</remarks>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverterFactory.CanConvert(System.Type)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonPipeDelimitedCollectionConverterFactory.CreateConverter(System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonStringConverter">
<summary>
Converter to allow the serializer to read strings.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonStringConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonStringConverter.Write(System.Text.Json.Utf8JsonWriter,System.String,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.Converters.JsonVersionConverter">
<summary>
Converts a Version object or value to/from JSON.
</summary>
<remarks>
Required to send <see cref="T:System.Version"/> as a string instead of an object.
</remarks>
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonVersionConverter.Read(System.Text.Json.Utf8JsonReader@,System.Type,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="M:Jellyfin.Extensions.Json.Converters.JsonVersionConverter.Write(System.Text.Json.Utf8JsonWriter,System.Version,System.Text.Json.JsonSerializerOptions)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Extensions.Json.JsonDefaults">
<summary>
Helper class for having compatible JSON throughout the codebase.
</summary>
</member>
<member name="F:Jellyfin.Extensions.Json.JsonDefaults.PascalCaseMediaType">
<summary>
Pascal case json profile media type.
</summary>
</member>
<member name="F:Jellyfin.Extensions.Json.JsonDefaults.CamelCaseMediaType">
<summary>
Camel case json profile media type.
</summary>
</member>
<member name="F:Jellyfin.Extensions.Json.JsonDefaults._jsonSerializerOptions">
<summary>
When changing these options, update
Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
-> AddJellyfinApi
-> AddJsonOptions.
</summary>
</member>
<member name="P:Jellyfin.Extensions.Json.JsonDefaults.Options">
<summary>
Gets the default <see cref="T:System.Text.Json.JsonSerializerOptions" /> options.
</summary>
<remarks>
The return value must not be modified.
If the defaults must be modified the author must use the copy constructor.
</remarks>
<returns>The default <see cref="T:System.Text.Json.JsonSerializerOptions" /> options.</returns>
</member>
<member name="P:Jellyfin.Extensions.Json.JsonDefaults.CamelCaseOptions">
<summary>
Gets camelCase json options.
</summary>
<remarks>
The return value must not be modified.
If the defaults must be modified the author must use the copy constructor.
</remarks>
<returns>The camelCase <see cref="T:System.Text.Json.JsonSerializerOptions" /> options.</returns>
</member>
<member name="P:Jellyfin.Extensions.Json.JsonDefaults.PascalCaseOptions">
<summary>
Gets PascalCase json options.
</summary>
<remarks>
The return value must not be modified.
If the defaults must be modified the author must use the copy constructor.
</remarks>
<returns>The PascalCase <see cref="T:System.Text.Json.JsonSerializerOptions" /> options.</returns>
</member>
<member name="T:Jellyfin.Extensions.Json.Utf8JsonExtensions">
<summary>
Extensions for Utf8JsonReader and Utf8JsonWriter.
</summary>
</member>
<member name="M:Jellyfin.Extensions.Json.Utf8JsonExtensions.IsEmptyString(System.Text.Json.Utf8JsonReader)">
<summary>
Determines if the reader contains an empty string.
</summary>
<param name="reader">The reader.</param>
<returns>Whether the reader contains an empty string.</returns>
</member>
<member name="M:Jellyfin.Extensions.Json.Utf8JsonExtensions.IsNull(System.Text.Json.Utf8JsonReader)">
<summary>
Determines if the reader contains a null value.
</summary>
<param name="reader">The reader.</param>
<returns>Whether the reader contains null.</returns>
</member>
<member name="T:Jellyfin.Extensions.ReadOnlyListExtension">
<summary>
Static extensions for the <see cref="T:System.Collections.Generic.IReadOnlyList`1"/> interface.
</summary>
</member>
<member name="M:Jellyfin.Extensions.ReadOnlyListExtension.IndexOf``1(System.Collections.Generic.IReadOnlyList{``0},``0)">
<summary>
Finds the index of the desired item.
</summary>
<param name="source">The source list.</param>
<param name="value">The value to fine.</param>
<typeparam name="T">The type of item to find.</typeparam>
<returns>Index if found, else -1.</returns>
</member>
<member name="M:Jellyfin.Extensions.ReadOnlyListExtension.FindIndex``1(System.Collections.Generic.IReadOnlyList{``0},System.Predicate{``0})">
<summary>
Finds the index of the predicate.
</summary>
<param name="source">The source list.</param>
<param name="match">The value to find.</param>
<typeparam name="T">The type of item to find.</typeparam>
<returns>Index if found, else -1.</returns>
</member>
<member name="M:Jellyfin.Extensions.ReadOnlyListExtension.FirstOrDefault``1(System.Collections.Generic.IReadOnlyList{``0})">
<summary>
Get the first or default item from a list.
</summary>
<param name="source">The source list.</param>
<typeparam name="T">The type of item.</typeparam>
<returns>The first item or default if list is empty.</returns>
</member>
<member name="T:Jellyfin.Extensions.ShuffleExtensions">
<summary>
Provides <c>Shuffle</c> extensions methods for <see cref="T:System.Collections.Generic.IList`1" />.
</summary>
</member>
<member name="M:Jellyfin.Extensions.ShuffleExtensions.Shuffle``1(System.Collections.Generic.IList{``0})">
<summary>
Shuffles the items in a list.
</summary>
<param name="list">The list that should get shuffled.</param>
<typeparam name="T">The type.</typeparam>
</member>
<member name="M:Jellyfin.Extensions.ShuffleExtensions.Shuffle``1(System.Collections.Generic.IList{``0},System.Random)">
<summary>
Shuffles the items in a list.
</summary>
<param name="list">The list that should get shuffled.</param>
<param name="rng">The random number generator to use.</param>
<typeparam name="T">The type.</typeparam>
</member>
<member name="T:Jellyfin.Extensions.SplitStringExtensions">
<summary>
Extension class for splitting lines without unnecessary allocations.
</summary>
</member>
<member name="M:Jellyfin.Extensions.SplitStringExtensions.SpanSplit(System.String,System.Char)">
<summary>
Creates a new string split enumerator.
</summary>
<param name="str">The string to split.</param>
<param name="separator">The separator to split on.</param>
<returns>The enumerator struct.</returns>
</member>
<member name="M:Jellyfin.Extensions.SplitStringExtensions.Split(System.ReadOnlySpan{System.Char},System.Char)">
<summary>
Creates a new span split enumerator.
</summary>
<param name="str">The span to split.</param>
<param name="separator">The separator to split on.</param>
<returns>The enumerator struct.</returns>
</member>
<member name="T:Jellyfin.Extensions.SplitStringExtensions.Enumerator">
<summary>
Provides an enumerator for the substrings separated by the separator.
</summary>
</member>
<member name="M:Jellyfin.Extensions.SplitStringExtensions.Enumerator.#ctor(System.ReadOnlySpan{System.Char},System.Char)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Extensions.SplitStringExtensions.Enumerator"/> struct.
</summary>
<param name="str">The span to split.</param>
<param name="separator">The separator to split on.</param>
</member>
<member name="P:Jellyfin.Extensions.SplitStringExtensions.Enumerator.Current">
<summary>
Gets a reference to the item at the current position of the enumerator.
</summary>
</member>
<member name="M:Jellyfin.Extensions.SplitStringExtensions.Enumerator.GetEnumerator">
<summary>
Returns <c>this</c>.
</summary>
<returns><c>this</c>.</returns>
</member>
<member name="M:Jellyfin.Extensions.SplitStringExtensions.Enumerator.MoveNext">
<summary>
Advances the enumerator to the next item.
</summary>
<returns><c>true</c> if there is a next element; otherwise <c>false</c>.</returns>
</member>
<member name="T:Jellyfin.Extensions.StreamExtensions">
<summary>
Class BaseExtensions.
</summary>
</member>
<member name="M:Jellyfin.Extensions.StreamExtensions.ReadAllLines(System.IO.Stream)">
<summary>
Reads all lines in the <see cref="T:System.IO.Stream" />.
</summary>
<param name="stream">The <see cref="T:System.IO.Stream" /> to read from.</param>
<returns>All lines in the stream.</returns>
</member>
<member name="M:Jellyfin.Extensions.StreamExtensions.ReadAllLines(System.IO.Stream,System.Text.Encoding)">
<summary>
Reads all lines in the <see cref="T:System.IO.Stream" />.
</summary>
<param name="stream">The <see cref="T:System.IO.Stream" /> to read from.</param>
<param name="encoding">The character encoding to use.</param>
<returns>All lines in the stream.</returns>
</member>
<member name="M:Jellyfin.Extensions.StreamExtensions.ReadAllLines(System.IO.TextReader)">
<summary>
Reads all lines in the <see cref="T:System.IO.TextReader" />.
</summary>
<param name="reader">The <see cref="T:System.IO.TextReader" /> to read from.</param>
<returns>All lines in the stream.</returns>
</member>
<member name="M:Jellyfin.Extensions.StreamExtensions.ReadAllLinesAsync(System.IO.TextReader,System.Threading.CancellationToken)">
<summary>
Reads all lines in the <see cref="T:System.IO.TextReader" />.
</summary>
<param name="reader">The <see cref="T:System.IO.TextReader" /> to read from.</param>
<param name="cancellationToken">The token to monitor for cancellation requests.</param>
<returns>All lines in the stream.</returns>
</member>
<member name="T:Jellyfin.Extensions.StringBuilderExtensions">
<summary>
Extension methods for the <see cref="T:System.Text.StringBuilder"/> class.
</summary>
</member>
<member name="M:Jellyfin.Extensions.StringBuilderExtensions.AppendJoinInSingleQuotes(System.Text.StringBuilder,System.Char,System.Collections.Generic.IReadOnlyList{System.String})">
<summary>
Concatenates and appends the members of a collection in single quotes using the specified delimiter.
</summary>
<param name="builder">The string builder.</param>
<param name="delimiter">The character delimiter.</param>
<param name="values">The collection of strings to concatenate.</param>
<returns>The updated string builder.</returns>
</member>
<member name="T:Jellyfin.Extensions.StringExtensions">
<summary>
Provides extensions methods for <see cref="T:System.String" />.
</summary>
<summary>
Checks whether or not the specified string has diacritics in it.
</summary>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.RemoveDiacritics(System.String)">
<summary>
Removes the diacritics character from the strings.
</summary>
<param name="text">The string to act on.</param>
<returns>The string without diacritics character.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.HasDiacritics(System.String)">
<summary>
Checks whether or not the specified string has diacritics in it.
</summary>
<param name="text">The string to check.</param>
<returns>True if the string has diacritics, false otherwise.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.Count(System.ReadOnlySpan{System.Char},System.Char)">
<summary>
Counts the number of occurrences of [needle] in the string.
</summary>
<param name="value">The haystack to search in.</param>
<param name="needle">The character to search for.</param>
<returns>The number of occurrences of the [needle] character.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.LeftPart(System.ReadOnlySpan{System.Char},System.Char)">
<summary>
Returns the part on the left of the <c>needle</c>.
</summary>
<param name="haystack">The string to seek.</param>
<param name="needle">The needle to find.</param>
<returns>The part left of the <paramref name="needle" />.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.RightPart(System.ReadOnlySpan{System.Char},System.Char)">
<summary>
Returns the part on the right of the <c>needle</c>.
</summary>
<param name="haystack">The string to seek.</param>
<param name="needle">The needle to find.</param>
<returns>The part right of the <paramref name="needle" />.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.Transliterated(System.String)">
<summary>
Returns a transliterated string which only contain ascii characters.
</summary>
<param name="text">The string to act on.</param>
<returns>The transliterated string.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.Trimmed(System.Collections.Generic.IEnumerable{System.String})">
<summary>
Ensures all strings are non-null and trimmed of leading an trailing blanks.
</summary>
<param name="values">The enumerable of strings to trim.</param>
<returns>The enumeration of trimmed strings.</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.TruncateAtNull(System.String)">
<summary>
Truncates a string at the first null character ('\0').
</summary>
<param name="text">The input string.</param>
<returns>
The substring up to (but not including) the first null character,
or the original string if no null character is present.
</returns>
</member>
<member name="M:Jellyfin.Extensions.StringExtensions.NonConformingUnicodeRegex">
<remarks>
Pattern:<br/>
<code>([\uD800-\uDBFF](?![\uDC00-\uDFFF]))|((?&lt;![\uD800-\uDBFF])[\uDC00-\uDFFF])|()</code><br/>
Explanation:<br/>
<code>
○ Match with 3 alternative expressions, atomically.<br/>
○ 1st capture group.<br/>
○ Match a character in the set [\uD800-\uDBFF].<br/>
○ Zero-width negative lookahead.<br/>
○ Match a character in the set [\uDC00-\uDFFF].<br/>
○ 2nd capture group.<br/>
○ Zero-width negative lookbehind.<br/>
○ Match a character in the set [\uD800-\uDBFF] right-to-left.<br/>
○ Match a character in the set [\uDC00-\uDFFF].<br/>
○ 3rd capture group.<br/>
○ Match ''.<br/>
</code>
</remarks>
</member>
<member name="T:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0">
<summary>Custom <see cref="T:System.Text.RegularExpressions.Regex"/>-derived type for the NonConformingUnicodeRegex method.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.Instance">
<summary>Cached, thread-safe singleton instance.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.#ctor">
<summary>Initializes the instance.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory">
<summary>Provides a factory for creating <see cref="T:System.Text.RegularExpressions.RegexRunner"/> instances to be used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory.CreateInstance">
<summary>Creates an instance of a <see cref="T:System.Text.RegularExpressions.RegexRunner"/> used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory.Runner">
<summary>Provides the runner that contains the custom logic implementing the specified regular expression.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory.Runner.Scan(System.ReadOnlySpan{System.Char})">
<summary>Scan the <paramref name="inputSpan"/> starting from base.runtextstart for the next match.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
</member>
<member name="M:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory.Runner.TryFindNextPossibleStartingPosition(System.ReadOnlySpan{System.Char})">
<summary>Search <paramref name="inputSpan"/> starting from base.runtextpos for the next location a match could possibly start.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
<returns>true if a possible match was found; false if no more matches are possible.</returns>
</member>
<member name="M:System.Text.RegularExpressions.Generated.NonConformingUnicodeRegex_0.RunnerFactory.Runner.TryMatchAtCurrentPosition(System.ReadOnlySpan{System.Char})">
<summary>Determine whether <paramref name="inputSpan"/> at base.runtextpos is a match for the regular expression.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
<returns>true if the regular expression matches at the current position; otherwise, false.</returns>
</member>
<member name="T:System.Text.RegularExpressions.Generated.Utilities">
<summary>Helper methods used by generated <see cref="T:System.Text.RegularExpressions.Regex"/>-derived implementations.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout">
<summary>Default timeout value set in <see cref="T:System.AppContext"/>, or <see cref="F:System.Text.RegularExpressions.Regex.InfiniteMatchTimeout"/> if none was set.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_hasTimeout">
<summary>Whether <see cref="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout"/> is non-infinite.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.Utilities.IndexOfNonAsciiOrAny_AEA8CFA2B5BC71ABC904D402F4CC4D3E0211E0681824955E81054C16A76CB030(System.ReadOnlySpan{System.Char})">
<summary>Finds the next index of any character that matches a character in the set [\uD800-\uDFFF\uFFFD].</summary>
</member>
</members>
</doc>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,224 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.MediaEncoding.Hls</name>
</assembly>
<members>
<member name="T:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator.#ctor(MediaBrowser.Controller.Persistence.IKeyframeRepository,Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor,Microsoft.Extensions.Logging.ILogger{Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator"/> class.
</summary>
<param name="keyframeRepository">An instance of the <see cref="T:MediaBrowser.Controller.Persistence.IKeyframeRepository"/> interface.</param>
<param name="keyframeExtractor">An instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor"/> interface.</param>
<param name="logger">An instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator.IsMetadataBased">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator.TryExtractKeyframes(System.Guid,System.String,Jellyfin.MediaEncoding.Keyframes.KeyframeData@)">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator.LogKeyframeExtractionFailed(Microsoft.Extensions.Logging.ILogger,System.String)">
<summary>
<para><b>Message:</b> Failed to extract keyframes using {ExtractorName}</para>
<para><b>Level:</b> Debug</para>
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator.LogKeyframeExtractionSucceeded(Microsoft.Extensions.Logging.ILogger,System.String)">
<summary>
<para><b>Message:</b> Successfully extracted keyframes using {ExtractorName}</para>
<para><b>Level:</b> Debug</para>
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Extensions.MediaEncodingHlsServiceCollectionExtensions">
<summary>
Extensions for the <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> interface.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extensions.MediaEncodingHlsServiceCollectionExtensions.AddHlsPlaylistGenerator(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
Adds the hls playlist generators to the <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/>.
</summary>
<param name="serviceCollection">An instance of the <see cref="T:Microsoft.Extensions.DependencyInjection.IServiceCollection"/> interface.</param>
<returns>The updated service collection.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor.#ctor(MediaBrowser.Controller.MediaEncoding.IMediaEncoder,Emby.Naming.Common.NamingOptions,Microsoft.Extensions.Logging.ILogger{Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor"/> class.
</summary>
<param name="mediaEncoder">An instance of the <see cref="T:MediaBrowser.Controller.MediaEncoding.IMediaEncoder"/> interface.</param>
<param name="namingOptions">An instance of <see cref="T:Emby.Naming.Common.NamingOptions"/>.</param>
<param name="logger">An instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor.IsMetadataBased">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor.TryExtractKeyframes(System.Guid,System.String,Jellyfin.MediaEncoding.Keyframes.KeyframeData@)">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.FfProbeKeyframeExtractor.LogKeyframeExtractionError(Microsoft.Extensions.Logging.ILogger,System.String,System.Exception)">
<summary>
<para><b>Message:</b> Extracting keyframes from {FilePath} using ffprobe failed</para>
<para><b>Level:</b> Error</para>
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor">
<summary>
Keyframe extractor.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor.IsMetadataBased">
<summary>
Gets a value indicating whether the extractor is based on container metadata.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor.TryExtractKeyframes(System.Guid,System.String,Jellyfin.MediaEncoding.Keyframes.KeyframeData@)">
<summary>
Attempt to extract keyframes.
</summary>
<param name="itemId">The item id.</param>
<param name="filePath">The path to the file.</param>
<param name="keyframeData">The keyframes.</param>
<returns>A value indicating whether the keyframe extraction was successful.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor.#ctor(Microsoft.Extensions.Logging.ILogger{Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor"/> class.
</summary>
<param name="logger">An instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor.IsMetadataBased">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor.TryExtractKeyframes(System.Guid,System.String,Jellyfin.MediaEncoding.Keyframes.KeyframeData@)">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Extractors.MatroskaKeyframeExtractor.LogKeyframeExtractionError(Microsoft.Extensions.Logging.ILogger,System.String,System.Exception)">
<summary>
<para><b>Message:</b> Extracting keyframes from {FilePath} using matroska metadata failed</para>
<para><b>Level:</b> Error</para>
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest">
<summary>
Request class for the <see cref="M:Jellyfin.MediaEncoding.Hls.Playlist.IDynamicHlsPlaylistGenerator.CreateMainPlaylist(Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest)"/> method.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.#ctor(System.Nullable{System.Guid},System.String,System.Int32,System.Int64,System.String,System.String,System.String,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest"/> class.
</summary>
<param name="mediaSourceId">The media source id.</param>
<param name="filePath">The absolute file path to the file.</param>
<param name="desiredSegmentLengthMs">The desired segment length in milliseconds.</param>
<param name="totalRuntimeTicks">The total duration of the file in ticks.</param>
<param name="segmentContainer">The desired segment container eg. "ts".</param>
<param name="endpointPrefix">The URI prefix for the relative URL in the playlist.</param>
<param name="queryString">The desired query string to append (must start with ?).</param>
<param name="isRemuxingVideo">Whether the video is being remuxed.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.MediaSourceId">
<summary>
Gets the media source id.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.FilePath">
<summary>
Gets the file path.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.DesiredSegmentLengthMs">
<summary>
Gets the desired segment length in milliseconds.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.TotalRuntimeTicks">
<summary>
Gets the total runtime in ticks.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.SegmentContainer">
<summary>
Gets the segment container.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.EndpointPrefix">
<summary>
Gets the endpoint prefix for the URL.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.QueryString">
<summary>
Gets the query string.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest.IsRemuxingVideo">
<summary>
Gets a value indicating whether the video is being remuxed.
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Playlist.DynamicHlsPlaylistGenerator">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Playlist.DynamicHlsPlaylistGenerator.#ctor(MediaBrowser.Controller.Configuration.IServerConfigurationManager,System.Collections.Generic.IEnumerable{Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Playlist.DynamicHlsPlaylistGenerator"/> class.
</summary>
<param name="serverConfigurationManager">An instance of the see <see cref="T:MediaBrowser.Controller.Configuration.IServerConfigurationManager"/> interface.</param>
<param name="extractors">An instance of <see cref="T:System.Collections.Generic.IEnumerable`1"/>.</param>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Playlist.DynamicHlsPlaylistGenerator.CreateMainPlaylist(Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest)">
<inheritdoc />
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.Playlist.IDynamicHlsPlaylistGenerator">
<summary>
Generator for dynamic HLS playlists where the segment lengths aren't known in advance.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.Playlist.IDynamicHlsPlaylistGenerator.CreateMainPlaylist(Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest)">
<summary>
Creates the main playlist containing the main video or audio stream.
</summary>
<param name="request">An instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.Playlist.CreateMainPlaylistRequest"/> class.</param>
<returns>The playlist as a formatted string.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.#ctor(MediaBrowser.Model.Globalization.ILocalizationManager,MediaBrowser.Controller.Library.ILibraryManager,System.Collections.Generic.IEnumerable{Jellyfin.MediaEncoding.Hls.Extractors.IKeyframeExtractor})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask"/> class.
</summary>
<param name="localizationManager">An instance of the <see cref="T:MediaBrowser.Model.Globalization.ILocalizationManager"/> interface.</param>
<param name="libraryManager">An instance of the <see cref="T:MediaBrowser.Controller.Library.ILibraryManager"/> interface.</param>
<param name="keyframeExtractors">The keyframe extractors.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.Name">
<inheritdoc />
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.Key">
<inheritdoc />
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.Description">
<inheritdoc />
</member>
<member name="P:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.Category">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.ExecuteAsync(System.IProgress{System.Double},System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:Jellyfin.MediaEncoding.Hls.ScheduledTasks.KeyframeExtractionScheduledTask.GetDefaultTriggers">
<inheritdoc />
</member>
</members>
</doc>
@@ -0,0 +1,260 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.MediaEncoding.Keyframes</name>
</assembly>
<members>
<member name="T:Jellyfin.MediaEncoding.Keyframes.FfProbe.FfProbeKeyframeExtractor">
<summary>
FfProbe based keyframe extractor.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.FfProbe.FfProbeKeyframeExtractor.GetKeyframeData(System.String,System.String)">
<summary>
Extracts the keyframes using the ffprobe executable at the specified path.
</summary>
<param name="ffProbePath">The path to the ffprobe executable.</param>
<param name="filePath">The file path.</param>
<returns>An instance of <see cref="T:Jellyfin.MediaEncoding.Keyframes.KeyframeData"/>.</returns>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.FfProbe.FfProbeKeyframeExtractor.ParseStream(System.IO.StreamReader)">
<summary>
Parses the keyframe data from the FFProbe stream.
</summary>
<param name="reader">The stream reader containing FFProbe output.</param>
<returns>The parsed keyframe data.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.FfTool.FfToolKeyframeExtractor">
<summary>
FfTool based keyframe extractor.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.FfTool.FfToolKeyframeExtractor.GetKeyframeData(System.String,System.String)">
<summary>
Extracts the keyframes using the fftool executable at the specified path.
</summary>
<param name="ffToolPath">The path to the fftool executable.</param>
<param name="filePath">The file path.</param>
<returns>An instance of <see cref="T:Jellyfin.MediaEncoding.Keyframes.KeyframeData"/>.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.KeyframeData">
<summary>
Keyframe information for a specific file.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.KeyframeData.#ctor(System.Int64,System.Collections.Generic.IReadOnlyList{System.Int64})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Keyframes.KeyframeData"/> class.
</summary>
<param name="totalDuration">The total duration of the video stream in ticks.</param>
<param name="keyframeTicks">The video keyframes in ticks.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.KeyframeData.TotalDuration">
<summary>
Gets the total duration of the stream in ticks.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.KeyframeData.KeyframeTicks">
<summary>
Gets the keyframes in ticks.
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions">
<summary>
Extension methods for the <see cref="T:NEbml.Core.EbmlReader"/> class.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions.FindElement(NEbml.Core.EbmlReader,System.UInt64)">
<summary>
Traverses the current container to find the element with <paramref name="identifier"/> identifier.
</summary>
<param name="reader">An instance of <see cref="T:NEbml.Core.EbmlReader"/>.</param>
<param name="identifier">The element identifier.</param>
<returns>A value indicating whether the element was found.</returns>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions.ReadUIntFromBinary(NEbml.Core.EbmlReader)">
<summary>
Reads the current position in the file as an unsigned integer converted from binary.
</summary>
<param name="reader">An instance of <see cref="T:NEbml.Core.EbmlReader"/>.</param>
<returns>The unsigned integer.</returns>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions.ReadSeekHead(NEbml.Core.EbmlReader)">
<summary>
Reads from the start of the file to retrieve the SeekHead segment.
</summary>
<param name="reader">An instance of <see cref="T:NEbml.Core.EbmlReader"/>.</param>
<returns>Instance of <see cref="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead"/>.</returns>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions.ReadInfo(NEbml.Core.EbmlReader,System.Int64)">
<summary>
Reads from SegmentContainer to retrieve the Info segment.
</summary>
<param name="reader">An instance of <see cref="T:NEbml.Core.EbmlReader"/>.</param>
<param name="position">The position of the info segment relative to the Segment container.</param>
<returns>Instance of <see cref="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info"/>.</returns>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Extensions.EbmlReaderExtensions.FindFirstTrackNumberByType(NEbml.Core.EbmlReader,System.Int64,System.UInt64)">
<summary>
Enters the Tracks segment and reads all tracks to find the specified type.
</summary>
<param name="reader">Instance of <see cref="T:NEbml.Core.EbmlReader"/>.</param>
<param name="tracksPosition">The relative position of the tracks segment.</param>
<param name="type">The track type identifier.</param>
<returns>The first track number with the specified type.</returns>
<exception cref="T:System.InvalidOperationException">Stream type is not found.</exception>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants">
<summary>
Constants for the Matroska identifiers.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.SegmentContainer">
<summary>
The Matroska segment container identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.SeekHead">
<summary>
The Matroska seek head identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.Seek">
<summary>
The Matroska seek identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.Info">
<summary>
The Matroska info identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TimestampScale">
<summary>
The Matroska timestamp scale identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.Duration">
<summary>
The Matroska duration identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.Tracks">
<summary>
The Matroska tracks identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TrackEntry">
<summary>
The Matroska track entry identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TrackNumber">
<summary>
The Matroska track number identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TrackType">
<summary>
The Matroska track type identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TrackTypeVideo">
<summary>
The Matroska video track type value.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.TrackTypeSubtitle">
<summary>
The Matroska subtitle track type value.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.Cues">
<summary>
The Matroska cues identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.CueTime">
<summary>
The Matroska cue time identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.CuePoint">
<summary>
The Matroska cue point identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.CueTrackPositions">
<summary>
The Matroska cue track positions identifier.
</summary>
</member>
<member name="F:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaConstants.CuePointTrackNumber">
<summary>
The Matroska cue point track number identifier.
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaKeyframeExtractor">
<summary>
The keyframe extractor for the matroska container.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaKeyframeExtractor.GetKeyframeData(System.String)">
<summary>
Extracts the keyframes in ticks (scaled using the container timestamp scale) from the matroska container.
</summary>
<param name="filePath">The file path.</param>
<returns>An instance of <see cref="T:Jellyfin.MediaEncoding.Keyframes.KeyframeData"/>.</returns>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info">
<summary>
The matroska Info segment.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info.#ctor(System.Int64,System.Nullable{System.Double})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info"/> class.
</summary>
<param name="timestampScale">The timestamp scale in nanoseconds.</param>
<param name="duration">The duration of the entire file.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info.TimestampScale">
<summary>
Gets the timestamp scale in nanoseconds.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.Info.Duration">
<summary>
Gets the total duration of the file.
</summary>
</member>
<member name="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead">
<summary>
The matroska SeekHead segment. All positions are relative to the Segment container.
</summary>
</member>
<member name="M:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead.#ctor(System.Int64,System.Int64,System.Int64)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead"/> class.
</summary>
<param name="infoPosition">The relative file position of the info segment.</param>
<param name="tracksPosition">The relative file position of the tracks segment.</param>
<param name="cuesPosition">The relative file position of the cues segment.</param>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead.InfoPosition">
<summary>
Gets relative file position of the info segment.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead.TracksPosition">
<summary>
Gets the relative file position of the tracks segment.
</summary>
</member>
<member name="P:Jellyfin.MediaEncoding.Keyframes.Matroska.Models.SeekHead.CuesPosition">
<summary>
Gets the relative file position of the cues segment.
</summary>
</member>
</members>
</doc>
+305
View File
@@ -0,0 +1,305 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Jellyfin.Networking</name>
</assembly>
<members>
<member name="T:Jellyfin.Networking.AutoDiscoveryHost">
<summary>
<see cref="T:Microsoft.Extensions.Hosting.BackgroundService"/> responsible for responding to auto-discovery messages.
</summary>
</member>
<member name="F:Jellyfin.Networking.AutoDiscoveryHost.PortNumber">
<summary>
The port to listen on for auto-discovery messages.
</summary>
</member>
<member name="M:Jellyfin.Networking.AutoDiscoveryHost.#ctor(Microsoft.Extensions.Logging.ILogger{Jellyfin.Networking.AutoDiscoveryHost},MediaBrowser.Controller.IServerApplicationHost,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Common.Net.INetworkManager)">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Networking.AutoDiscoveryHost" /> class.
</summary>
<param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/>.</param>
<param name="appHost">The <see cref="T:MediaBrowser.Controller.IServerApplicationHost"/>.</param>
<param name="configurationManager">The <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/>.</param>
<param name="networkManager">The <see cref="T:MediaBrowser.Common.Net.INetworkManager"/>.</param>
</member>
<member name="M:Jellyfin.Networking.AutoDiscoveryHost.ExecuteAsync(System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="T:Jellyfin.Networking.HappyEyeballs.HttpClientExtension">
<summary>
Defines the <see cref="T:Jellyfin.Networking.HappyEyeballs.HttpClientExtension"/> class.
Implementation taken from https://github.com/ppy/osu-framework/pull/4191 .
</summary>
</member>
<member name="P:Jellyfin.Networking.HappyEyeballs.HttpClientExtension.UseIPv6">
<summary>
Gets or sets a value indicating whether the client should use IPv6.
</summary>
</member>
<member name="M:Jellyfin.Networking.HappyEyeballs.HttpClientExtension.OnConnect(System.Net.Http.SocketsHttpConnectionContext,System.Threading.CancellationToken)">
<summary>
Implements the httpclient callback method.
</summary>
<param name="context">The <see cref="T:System.Net.Http.SocketsHttpConnectionContext"/> instance.</param>
<param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> instance.</param>
<returns>The http steam.</returns>
</member>
<member name="T:Jellyfin.Networking.Manager.NetworkManager">
<summary>
Class to take care of network interface management.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._initLock">
<summary>
Threading lock for network properties.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._publishedServerUrls">
<summary>
Holds the published server URLs and the IPs to use them on.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._eventfire">
<summary>
Used to stop "event-racing conditions".
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._interfaces">
<summary>
Dictionary containing interface addresses and their subnets.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._lanSubnets">
<summary>
Unfiltered user defined LAN subnets (<see cref="P:MediaBrowser.Common.Net.NetworkConfiguration.LocalNetworkSubnets"/>)
or internal interface network subnets if undefined by user.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._excludedSubnets">
<summary>
User defined list of subnets to excluded from the LAN.
</summary>
</member>
<member name="F:Jellyfin.Networking.Manager.NetworkManager._disposed">
<summary>
True if this object is disposed.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.#ctor(MediaBrowser.Common.Configuration.IConfigurationManager,Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{Jellyfin.Networking.Manager.NetworkManager})">
<summary>
Initializes a new instance of the <see cref="T:Jellyfin.Networking.Manager.NetworkManager"/> class.
</summary>
<param name="configurationManager">The <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> instance.</param>
<param name="startupConfig">The <see cref="T:Microsoft.Extensions.Configuration.IConfiguration"/> instance holding startup parameters.</param>
<param name="logger">Logger to use for messages.</param>
</member>
<member name="E:Jellyfin.Networking.Manager.NetworkManager.NetworkChanged">
<summary>
Event triggered on network changes.
</summary>
</member>
<member name="P:Jellyfin.Networking.Manager.NetworkManager.MockNetworkSettings">
<summary>
Gets or sets a value indicating whether testing is taking place.
</summary>
</member>
<member name="P:Jellyfin.Networking.Manager.NetworkManager.IsIPv4Enabled">
<summary>
Gets a value indicating whether IPv4 is enabled.
</summary>
</member>
<member name="P:Jellyfin.Networking.Manager.NetworkManager.IsIPv6Enabled">
<summary>
Gets a value indicating whether IP6 is enabled.
</summary>
</member>
<member name="P:Jellyfin.Networking.Manager.NetworkManager.TrustAllIPv6Interfaces">
<summary>
Gets a value indicating whether is all IPv6 interfaces are trusted as internal.
</summary>
</member>
<member name="P:Jellyfin.Networking.Manager.NetworkManager.PublishedServerUrls">
<summary>
Gets the Published server override list.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.Dispose">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.OnNetworkAvailabilityChanged(System.Object,System.Net.NetworkInformation.NetworkAvailabilityEventArgs)">
<summary>
Handler for network change events.
</summary>
<param name="sender">Sender.</param>
<param name="e">A <see cref="T:System.Net.NetworkInformation.NetworkAvailabilityEventArgs"/> containing network availability information.</param>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.OnNetworkAddressChanged(System.Object,System.EventArgs)">
<summary>
Handler for network change events.
</summary>
<param name="sender">Sender.</param>
<param name="e">An <see cref="T:System.EventArgs"/>.</param>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.HandleNetworkChange">
<summary>
Triggers our event, and re-loads interface information.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.OnNetworkChange">
<summary>
Waits for 2 seconds before re-initialising the settings, as typically these events fire multiple times in succession.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.InitializeInterfaces">
<summary>
Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetInterfacesCore(Microsoft.Extensions.Logging.ILogger,System.Boolean,System.Boolean)">
<summary>
Generate a list of all the interface ip addresses and submasks where that are in the active/unknown state.
</summary>
<param name="logger">The logger.</param>
<param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
<param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
<returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.InitializeLan(MediaBrowser.Common.Net.NetworkConfiguration)">
<summary>
Initializes internal LAN cache.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.EnforceBindSettings(MediaBrowser.Common.Net.NetworkConfiguration)">
<summary>
Enforce bind addresses and exclusions on available interfaces.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.FilterBindSettings(MediaBrowser.Common.Net.NetworkConfiguration,System.Collections.Generic.IList{MediaBrowser.Model.Net.IPData},System.Boolean,System.Boolean)">
<summary>
Filters a list of bind addresses and exclusions on available interfaces.
</summary>
<param name="config">The network config to be filtered by.</param>
<param name="interfaces">A list of possible interfaces to be filtered.</param>
<param name="isIPv4Enabled">If true evaluates IPV4 type ip addresses.</param>
<param name="isIPv6Enabled">If true evaluates IPV6 type ip addresses.</param>
<returns>A list of all locally known up addresses and submasks that are to be considered usable.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.InitializeRemote(MediaBrowser.Common.Net.NetworkConfiguration)">
<summary>
Initializes the remote address values.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.InitializeOverrides(MediaBrowser.Common.Net.NetworkConfiguration)">
<summary>
Parses the user defined overrides into the dictionary object.
Overrides are the equivalent of localised publishedServerUrl, enabling
different addresses to be advertised over different subnets.
format is subnet=ipaddress|host|uri
when subnet = 0.0.0.0, any external address matches.
</summary>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.UpdateSettings(System.Object)">
<summary>
Reloads all settings and re-Initializes the instance.
</summary>
<param name="configuration">The <see cref="T:MediaBrowser.Common.Net.NetworkConfiguration"/> to use.</param>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.Dispose(System.Boolean)">
<summary>
Protected implementation of Dispose pattern.
</summary>
<param name="disposing"><c>True</c> to dispose the managed state.</param>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.TryParseInterface(System.String,System.Collections.Generic.IReadOnlyList{MediaBrowser.Model.Net.IPData}@)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.ShouldAllowServerAccess(System.Net.IPAddress)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetLoopbacks">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetAllBindInterfaces(System.Boolean)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetAllBindInterfaces(System.Boolean,MediaBrowser.Common.Configuration.IConfigurationManager,System.Collections.Generic.IReadOnlyList{MediaBrowser.Model.Net.IPData},System.Boolean,System.Boolean)">
<summary>
Reads the jellyfin configuration of the configuration manager and produces a list of interfaces that should be bound.
</summary>
<param name="individualInterfaces">Defines that only known interfaces should be used.</param>
<param name="configurationManager">The ConfigurationManager.</param>
<param name="knownInterfaces">The known interfaces that gets returned if possible or instructed.</param>
<param name="readIpv4">Include IPV4 type interfaces.</param>
<param name="readIpv6">Include IPV6 type interfaces.</param>
<returns>A list of ip address of which jellyfin should bind to.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetBindAddress(System.String,System.Nullable{System.Int32}@)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetBindAddress(Microsoft.AspNetCore.Http.HttpRequest,System.Nullable{System.Int32}@)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetBindAddress(System.Net.IPAddress,System.Nullable{System.Int32}@,System.Boolean)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.GetInternalBindAddresses">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.IsInLocalNetwork(System.String)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.IsLinkLocalAddress(System.Net.IPAddress)">
<summary>
Get if the IPAddress is Link-local.
</summary>
<param name="address">The IP Address.</param>
<returns>Bool indicates if the address is link-local.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.IsInLocalNetwork(System.Net.IPAddress)">
<inheritdoc/>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.CheckIfLanAndNotExcluded(System.Net.IPAddress)">
<summary>
Check if the address is in the LAN and not excluded.
</summary>
<param name="address">The IP address to check. The caller should make sure this is not an IPv4MappedToIPv6 address.</param>
<returns>Boolean indicates whether the address is in LAN.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.MatchesPublishedServerUrl(System.Net.IPAddress,System.Boolean,System.String@)">
<summary>
Attempts to match the source against the published server URL overrides.
</summary>
<param name="source">IP source address to use.</param>
<param name="isInExternalSubnet">True if the source is in an external subnet.</param>
<param name="bindPreference">The published server URL that matches the source address.</param>
<returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.MatchesBindInterface(System.Net.IPAddress,System.Boolean,System.String@)">
<summary>
Attempts to match the source against the user defined bind interfaces.
</summary>
<param name="source">IP source address to use.</param>
<param name="isInExternalSubnet">True if the source is in the external subnet.</param>
<param name="result">The result, if a match is found.</param>
<returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
</member>
<member name="M:Jellyfin.Networking.Manager.NetworkManager.MatchesExternalInterface(System.Net.IPAddress,System.String@)">
<summary>
Attempts to match the source against external interfaces.
</summary>
<param name="source">IP source address to use.</param>
<param name="result">The result, if a match is found.</param>
<returns><c>true</c> if a match is found, <c>false</c> otherwise.</returns>
</member>
<member name="T:Jellyfin.Networking.Udp.SocketFactory">
<summary>
Factory class to create different kinds of sockets.
</summary>
</member>
<member name="M:Jellyfin.Networking.Udp.SocketFactory.CreateUdpBroadcastSocket(System.Int32)">
<inheritdoc />
</member>
</members>
</doc>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,500 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>MediaBrowser.LocalMetadata</name>
</assembly>
<members>
<member name="T:MediaBrowser.LocalMetadata.BaseXmlProvider`1">
<summary>
The BaseXmlProvider.
</summary>
<typeparam name="T">Type of provider.</typeparam>
</member>
<member name="M:MediaBrowser.LocalMetadata.BaseXmlProvider`1.#ctor(MediaBrowser.Model.IO.IFileSystem)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.BaseXmlProvider`1"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.BaseXmlProvider`1.Name">
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.BaseXmlProvider`1.Order">
After Nfo
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.BaseXmlProvider`1.FileSystem">
<summary>
Gets the IFileSystem.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.BaseXmlProvider`1.GetMetadata(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService,System.Threading.CancellationToken)">
<summary>
Gets metadata for item.
</summary>
<param name="info">The item info.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
<param name="cancellationToken">The cancellation token.</param>
<returns>The metadata for item.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.BaseXmlProvider`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Threading.CancellationToken)">
<summary>
Get metadata from path.
</summary>
<param name="result">Resulting metadata.</param>
<param name="path">The path.</param>
<param name="cancellationToken">The cancellation token.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.BaseXmlProvider`1.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Get metadata from xml file.
</summary>
<param name="info">Item inf.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
<returns>The file system metadata.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.BaseXmlProvider`1.HasChanged(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider">
<summary>
Collection folder local image provider.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider.#ctor(MediaBrowser.Model.IO.IFileSystem)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider.Name">
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider.Order">
Run after LocalImageProvider
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider.Supports(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.CollectionFolderLocalImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Images.EpisodeLocalImageProvider">
<summary>
Episode local image provider.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.EpisodeLocalImageProvider.Name">
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.EpisodeLocalImageProvider.Order">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.EpisodeLocalImageProvider.Supports(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.EpisodeLocalImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider">
<summary>
Internal metadata folder image provider.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider.#ctor(MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider.Order">
Make sure this is last so that all other locations are scanned first
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider.Name">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider.Supports(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.InternalMetadataFolderImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Images.LocalImageProvider">
<summary>
Local image provider.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.LocalImageProvider.#ctor(MediaBrowser.Model.IO.IFileSystem)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Images.LocalImageProvider"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.LocalImageProvider.Name">
<inheritdoc />
</member>
<member name="P:MediaBrowser.LocalMetadata.Images.LocalImageProvider.Order">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.LocalImageProvider.Supports(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.LocalImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.LocalImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,System.String,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Get images for item.
</summary>
<param name="item">The item.</param>
<param name="path">The images path.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
<returns>The local image info.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Images.LocalImageProvider.GetImages(MediaBrowser.Controller.Entities.BaseItem,System.Collections.Generic.IEnumerable{System.String},MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Get images for item from multiple paths.
</summary>
<param name="item">The item.</param>
<param name="paths">The image paths.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
<returns>The local image info.</returns>
</member>
<member name="T:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1">
<summary>
Provides a base class for parsing metadata xml.
</summary>
<typeparam name="T">Type of item xml parser.</typeparam>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser{`0}},MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1" /> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.Logger">
<summary>
Gets the logger.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.ProviderManager">
<summary>
Gets the provider manager.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Threading.CancellationToken)">
<summary>
Fetches metadata for an item from one xml file.
</summary>
<param name="item">The item.</param>
<param name="metadataFile">The metadata file.</param>
<param name="cancellationToken">The cancellation token.</param>
<exception cref="T:System.ArgumentNullException">Item is null.</exception>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Xml.XmlReaderSettings,System.Text.Encoding,System.Threading.CancellationToken)">
<summary>
Fetches the specified item.
</summary>
<param name="item">The item.</param>
<param name="metadataFile">The metadata file.</param>
<param name="settings">The settings.</param>
<param name="encoding">The encoding.</param>
<param name="cancellationToken">The cancellation token.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{`0})">
<summary>
Fetches metadata from one Xml Element.
</summary>
<param name="reader">The reader.</param>
<param name="itemResult">The item result.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.FetchFromTaglinesNode(System.Xml.XmlReader,`0)">
<summary>
Fetches from taglines node.
</summary>
<param name="reader">The reader.</param>
<param name="item">The item.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.FetchFromGenresNode(System.Xml.XmlReader,`0)">
<summary>
Fetches from genres node.
</summary>
<param name="reader">The reader.</param>
<param name="item">The item.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.FetchDataFromPersonsNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{`0})">
<summary>
Fetches the data from persons node.
</summary>
<param name="reader">The reader.</param>
<param name="item">The item.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.FetchFromStudiosNode(System.Xml.XmlReader,`0)">
<summary>
Fetches from studios node.
</summary>
<param name="reader">The reader.</param>
<param name="item">The item.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.GetLinkedChild(System.Xml.XmlReader)">
<summary>
Get linked child.
</summary>
<param name="reader">The xml reader.</param>
<returns>The linked child.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BaseItemXmlParser`1.GetShare(System.Xml.XmlReader)">
<summary>
Get share.
</summary>
<param name="reader">The xml reader.</param>
<returns>The share.</returns>
</member>
<member name="T:MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser">
<summary>
The box set xml parser.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser},MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.Movies.BoxSet})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser">
<summary>
Playlist xml parser.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser},MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Playlists.Playlist})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Providers.BoxSetXmlProvider">
<summary>
Class BoxSetXmlProvider.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.BoxSetXmlProvider.#ctor(MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Parsers.BoxSetXmlParser},MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Providers.BoxSetXmlProvider"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.BoxSetXmlProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.Movies.BoxSet},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.BoxSetXmlProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Providers.PlaylistXmlProvider">
<summary>
Playlist xml provider.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.PlaylistXmlProvider.#ctor(MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Parsers.PlaylistXmlParser},MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Providers.PlaylistXmlProvider"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.PlaylistXmlProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Playlists.Playlist},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Providers.PlaylistXmlProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Savers.BaseXmlSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="configurationManager">Instance of the <see cref="T:MediaBrowser.Controller.Configuration.IServerConfigurationManager"/> interface.</param>
<param name="libraryManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.ILibraryManager"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="P:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.FileSystem">
<summary>
Gets the file system.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.ConfigurationManager">
<summary>
Gets the configuration manager.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.LibraryManager">
<summary>
Gets the library manager.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.Logger">
<summary>
Gets the logger.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.Name">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.GetSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<summary>
Gets the save path.
</summary>
<param name="item">The item.</param>
<returns>System.String.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<summary>
Gets the name of the root element.
</summary>
<param name="item">The item.</param>
<returns>System.String.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<summary>
Determines whether [is enabled for] [the specified item].
</summary>
<param name="item">The item.</param>
<param name="updateType">Type of the update.</param>
<returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.SaveAsync(MediaBrowser.Controller.Entities.BaseItem,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.WriteCustomElementsAsync(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<summary>
Write custom elements.
</summary>
<param name="item">The item.</param>
<param name="writer">The xml writer.</param>
<returns>The task object representing the asynchronous operation.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.AddCommonNodesAsync(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<summary>
Adds the common nodes.
</summary>
<param name="item">The item.</param>
<param name="writer">The xml writer.</param>
<returns>The task object representing the asynchronous operation.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.AddSharesAsync(MediaBrowser.Model.Entities.IHasShares,System.Xml.XmlWriter)">
<summary>
Add shares.
</summary>
<param name="item">The item.</param>
<param name="writer">The xml writer.</param>
<returns>The task object representing the asynchronous operation.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.AddMediaInfo``1(``0,System.Xml.XmlWriter)">
<summary>
Appends the media info.
</summary>
<param name="item">The item.</param>
<param name="writer">The xml writer.</param>
<typeparam name="T">Type of item.</typeparam>
<returns>The task object representing the asynchronous operation.</returns>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BaseXmlSaver.AddLinkedChildren(MediaBrowser.Controller.Entities.Folder,System.Xml.XmlWriter,System.String,System.String)">
<summary>
ADd linked children.
</summary>
<param name="item">The item.</param>
<param name="writer">The xml writer.</param>
<param name="pluralNodeName">The plural node name.</param>
<param name="singularNodeName">The singular node name.</param>
<returns>The task object representing the asynchronous operation.</returns>
</member>
<member name="T:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver">
<summary>
Box set xml saver.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="configurationManager">Instance of the <see cref="T:MediaBrowser.Controller.Configuration.IServerConfigurationManager"/> interface.</param>
<param name="libraryManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.ILibraryManager"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver.WriteCustomElementsAsync(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.BoxSetXmlSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver">
<summary>
Playlist xml saver.
</summary>
</member>
<member name="F:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.DefaultPlaylistFilename">
<summary>
The default file name to use when creating a new playlist.
</summary>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver"/> class.
</summary>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="configurationManager">Instance of the <see cref="T:MediaBrowser.Controller.Configuration.IServerConfigurationManager"/> interface.</param>
<param name="libraryManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.ILibraryManager"/> interface.</param>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.WriteCustomElementsAsync(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.LocalMetadata.Savers.PlaylistXmlSaver.GetSavePath(System.String)">
<summary>
Get the save path.
</summary>
<param name="itemPath">The item path.</param>
<returns>The save path.</returns>
</member>
<member name="T:MediaBrowser.LocalMetadata.XmlProviderUtils">
<summary>
The xml provider utils.
</summary>
</member>
<member name="P:MediaBrowser.LocalMetadata.XmlProviderUtils.Name">
<summary>
Gets the name.
</summary>
</member>
</members>
</doc>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+710
View File
@@ -0,0 +1,710 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>MediaBrowser.XbmcMetadata</name>
</assembly>
<members>
<member name="M:MediaBrowser.XbmcMetadata.Configuration.NfoConfigurationFactory.GetConfigurations">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.NfoUserDataSaver">
<summary>
<see cref="T:Microsoft.Extensions.Hosting.IHostedService"/> responsible for updating NFO files' user data.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.NfoUserDataSaver.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.NfoUserDataSaver},MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IProviderManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.NfoUserDataSaver"/> class.
</summary>
<param name="logger">The <see cref="T:Microsoft.Extensions.Logging.ILogger"/>.</param>
<param name="config">The <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/>.</param>
<param name="userDataManager">The <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/>.</param>
<param name="providerManager">The <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/>.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.NfoUserDataSaver.StartAsync(System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.NfoUserDataSaver.StopAsync(System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1">
<summary>
The BaseNfoParser class.
</summary>
<typeparam name="T">The type.</typeparam>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1" /> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.Logger">
<summary>
Gets the logger.
</summary>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.ProviderManager">
<summary>
Gets the provider manager.
</summary>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.SupportsUrlAfterClosingXmlTag">
<summary>
Gets a value indicating whether URLs after a closing XML tag are supported.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Threading.CancellationToken)">
<summary>
Fetches metadata for an item from one xml file.
</summary>
<param name="item">The <see cref="T:MediaBrowser.Controller.Providers.MetadataResult`1"/>.</param>
<param name="metadataFile">The metadata file.</param>
<param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/>.</param>
<exception cref="T:System.ArgumentNullException"><c>item</c> is <c>null</c>.</exception>
<exception cref="T:System.ArgumentException"><c>metadataFile</c> is <c>null</c> or empty.</exception>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Xml.XmlReaderSettings,System.Threading.CancellationToken)">
<summary>
Fetches the specified item.
</summary>
<param name="item">The <see cref="T:MediaBrowser.Controller.Providers.MetadataResult`1"/>.</param>
<param name="metadataFile">The metadata file.</param>
<param name="settings">The <see cref="T:System.Xml.XmlReaderSettings"/>.</param>
<param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/>.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.ParseProviderLinks(`0,System.ReadOnlySpan{System.Char})">
<summary>
Parses a XML tag to a provider id.
</summary>
<param name="item">The item.</param>
<param name="xml">The xml tag.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{`0})">
<summary>
Fetches metadata from an XML node.
</summary>
<param name="reader">The <see cref="T:System.Xml.XmlReader"/>.</param>
<param name="itemResult">The <see cref="T:MediaBrowser.Controller.Providers.MetadataResult`1"/>.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.BaseNfoParser`1.GetImageType(System.String)">
<summary>
Parses the <see cref="T:MediaBrowser.Model.Entities.ImageType"/> from the NFO aspect property.
</summary>
<param name="aspect">The NFO aspect property.</param>
<returns>The <see cref="T:MediaBrowser.Model.Entities.ImageType"/>.</returns>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser">
<summary>
Nfo parser for episodes.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Episode},System.String,System.Xml.XmlReaderSettings,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Episode})">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.EpisodeNfoParser.ReadEpisodeDetailsFromXml(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Episode},System.String,System.Xml.XmlReaderSettings,System.Threading.CancellationToken)">
<summary>
Reads the episode details from the given xml and saves the result in the provided result item.
</summary>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.MovieNfoParser">
<summary>
Nfo parser for movies.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.MovieNfoParser.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.MovieNfoParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.DirectoryService"/> interface.</param>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.MovieNfoParser.SupportsUrlAfterClosingXmlTag">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.MovieNfoParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.Video})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.SeasonNfoParser">
<summary>
Nfo parser for seasons.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeasonNfoParser.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.SeasonNfoParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.DirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeasonNfoParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Season})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoParser">
<summary>
Nfo parser for series.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoParser.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoParser.SupportsUrlAfterClosingXmlTag">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Series})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoSeasonParser">
<summary>
NFO parser for seasons based on series NFO.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoSeasonParser.#ctor(Microsoft.Extensions.Logging.ILogger,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoSeasonParser"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="P:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoSeasonParser.SupportsUrlAfterClosingXmlTag">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Parsers.SeriesNfoSeasonParser.FetchDataFromXmlNode(System.Xml.XmlReader,MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Season})">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider">
<summary>
Nfo provider for albums.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.Audio.MusicAlbum},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.AlbumNfoProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider">
<summary>
Nfo provider for artists.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider.#ctor(MediaBrowser.Model.IO.IFileSystem,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider},MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.Audio.MusicArtist},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.ArtistNfoProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="P:MediaBrowser.XbmcMetadata.Providers.BaseNfoProvider`1.Name">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.BaseNfoProvider`1.GetMetadata(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.BaseNfoProvider`1.HasChanged(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.BaseVideoNfoProvider`1.Fetch(MediaBrowser.Controller.Providers.MetadataResult{`0},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.BaseVideoNfoProvider`1.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider">
<summary>
Nfo provider for episodes.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Episode},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.EpisodeNfoProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.MovieNfoProvider">
<summary>
Nfo provider for movies.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.MovieNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.MovieNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.MovieNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.MusicVideoNfoProvider">
<summary>
Nfo provider for music videos.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.MusicVideoNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.MusicVideoNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.MusicVideoNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider">
<summary>
Nfo provider for seasons.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Season},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeasonNfoProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider">
<summary>
Nfo provider for series.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Series},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider">
<summary>
NFO provider for seasons based on series NFO.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService,MediaBrowser.Controller.Library.ILibraryManager)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
<param name="libraryManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.ILibraryManager"/> interface.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider.Fetch(MediaBrowser.Controller.Providers.MetadataResult{MediaBrowser.Controller.Entities.TV.Season},System.String,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.SeriesNfoSeasonProvider.GetXmlFile(MediaBrowser.Controller.Providers.ItemInfo,MediaBrowser.Controller.Providers.IDirectoryService)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Providers.VideoNfoProvider">
<summary>
Nfo provider for videos.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Providers.VideoNfoProvider.#ctor(Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Providers.VideoNfoProvider},MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Common.Configuration.IConfigurationManager,MediaBrowser.Controller.Providers.IProviderManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Providers.IDirectoryService)">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Providers.VideoNfoProvider"/> class.
</summary>
<param name="logger">Instance of the <see cref="T:Microsoft.Extensions.Logging.ILogger`1"/> interface.</param>
<param name="fileSystem">Instance of the <see cref="T:MediaBrowser.Model.IO.IFileSystem"/> interface.</param>
<param name="config">Instance of the <see cref="T:MediaBrowser.Common.Configuration.IConfigurationManager"/> interface.</param>
<param name="providerManager">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IProviderManager"/> interface.</param>
<param name="userManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserManager"/> interface.</param>
<param name="userDataManager">Instance of the <see cref="T:MediaBrowser.Controller.Library.IUserDataManager"/> interface.</param>
<param name="directoryService">Instance of the <see cref="T:MediaBrowser.Controller.Providers.IDirectoryService"/> interface.</param>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver">
<summary>
Nfo saver for albums.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.AlbumNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver">
<summary>
Nfo saver for artist.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.ArtistNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="P:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.Name">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.InvalidXMLCharsRegexRegex">
<remarks>
Pattern:<br/>
<code>(?&lt;![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]</code><br/>
Explanation:<br/>
<code>
○ Match with 3 alternative expressions, atomically.<br/>
○ Match a sequence of expressions.<br/>
○ Zero-width negative lookbehind.<br/>
○ Match a character in the set [\uD800-\uDBFF] right-to-left.<br/>
○ Match a character in the set [\uDC00-\uDFFF].<br/>
○ Match a sequence of expressions.<br/>
○ Match a character in the set [\uD800-\uDBFF].<br/>
○ Zero-width negative lookahead.<br/>
○ Match a character in the set [\uDC00-\uDFFF].<br/>
○ Match a character in the set [^\t\n\r -~\u00A0-\uFEFE\uFF00-\uFFFD].<br/>
</code>
</remarks>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.GetSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<summary>
Gets the save path.
</summary>
<param name="item">The item.</param>
<returns><see cref="T:System.String" />.</returns>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<summary>
Gets the name of the root element.
</summary>
<param name="item">The item.</param>
<returns><see cref="T:System.String" />.</returns>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.SaveAsync(MediaBrowser.Controller.Entities.BaseItem,System.Threading.CancellationToken)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.AddCommonNodes(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Configuration.IServerConfigurationManager)">
<summary>
Adds the common nodes.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.BaseNfoSaver.GetOutputTrailerUrl(System.String)">
<summary>
Gets the output trailer URL.
</summary>
<param name="url">The URL.</param>
<returns>System.String.</returns>
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver">
<summary>
Nfo saver for episodes.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.EpisodeNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver">
<summary>
Nfo saver for movies.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.MovieNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver">
<summary>
Nfo saver for seasons.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeasonNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver">
<summary>
Nfo saver for series.
</summary>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.#ctor(MediaBrowser.Model.IO.IFileSystem,MediaBrowser.Controller.Configuration.IServerConfigurationManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.IUserDataManager,Microsoft.Extensions.Logging.ILogger{MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver})">
<summary>
Initializes a new instance of the <see cref="T:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver"/> class.
</summary>
<param name="fileSystem">The file system.</param>
<param name="configurationManager">the server configuration manager.</param>
<param name="libraryManager">The library manager.</param>
<param name="userManager">The user manager.</param>
<param name="userDataManager">The user data manager.</param>
<param name="logger">The logger.</param>
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.GetLocalSavePath(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.GetRootElementName(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.IsEnabledFor(MediaBrowser.Controller.Entities.BaseItem,MediaBrowser.Controller.Library.ItemUpdateType)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.WriteCustomElements(MediaBrowser.Controller.Entities.BaseItem,System.Xml.XmlWriter)">
<inheritdoc />
</member>
<member name="M:MediaBrowser.XbmcMetadata.Savers.SeriesNfoSaver.GetTagsUsed(MediaBrowser.Controller.Entities.BaseItem)">
<inheritdoc />
</member>
<member name="T:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0">
<summary>Custom <see cref="T:System.Text.RegularExpressions.Regex"/>-derived type for the InvalidXMLCharsRegexRegex method.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.Instance">
<summary>Cached, thread-safe singleton instance.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.#ctor">
<summary>Initializes the instance.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory">
<summary>Provides a factory for creating <see cref="T:System.Text.RegularExpressions.RegexRunner"/> instances to be used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory.CreateInstance">
<summary>Creates an instance of a <see cref="T:System.Text.RegularExpressions.RegexRunner"/> used by methods on <see cref="T:System.Text.RegularExpressions.Regex"/>.</summary>
</member>
<member name="T:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory.Runner">
<summary>Provides the runner that contains the custom logic implementing the specified regular expression.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory.Runner.Scan(System.ReadOnlySpan{System.Char})">
<summary>Scan the <paramref name="inputSpan"/> starting from base.runtextstart for the next match.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
</member>
<member name="M:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory.Runner.TryFindNextPossibleStartingPosition(System.ReadOnlySpan{System.Char})">
<summary>Search <paramref name="inputSpan"/> starting from base.runtextpos for the next location a match could possibly start.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
<returns>true if a possible match was found; false if no more matches are possible.</returns>
</member>
<member name="M:System.Text.RegularExpressions.Generated.InvalidXMLCharsRegexRegex_0.RunnerFactory.Runner.TryMatchAtCurrentPosition(System.ReadOnlySpan{System.Char})">
<summary>Determine whether <paramref name="inputSpan"/> at base.runtextpos is a match for the regular expression.</summary>
<param name="inputSpan">The text being scanned by the regular expression.</param>
<returns>true if the regular expression matches at the current position; otherwise, false.</returns>
</member>
<member name="T:System.Text.RegularExpressions.Generated.Utilities">
<summary>Helper methods used by generated <see cref="T:System.Text.RegularExpressions.Regex"/>-derived implementations.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout">
<summary>Default timeout value set in <see cref="T:System.AppContext"/>, or <see cref="F:System.Text.RegularExpressions.Regex.InfiniteMatchTimeout"/> if none was set.</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_hasTimeout">
<summary>Whether <see cref="F:System.Text.RegularExpressions.Generated.Utilities.s_defaultTimeout"/> is non-infinite.</summary>
</member>
<member name="M:System.Text.RegularExpressions.Generated.Utilities.IndexOfNonAsciiOrAny_054D88A1CA74F303E737A5D6CB3A2FC78EF8B4B74FED9DBE7BF41353919E123B(System.ReadOnlySpan{System.Char})">
<summary>Finds the next index of any character that matches a character in the set [^\t\n\r -~\u00A0-\uD7FF\uE000-\uFEFE\uFF00-\uFFFD].</summary>
</member>
<member name="F:System.Text.RegularExpressions.Generated.Utilities.s_ascii_260000FFFFFFFFFFFFFFFFFFFFFF7F">
<summary>Supports searching for characters in or not in "\t\n\r !\"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".</summary>
</member>
</members>
</doc>
@@ -0,0 +1,235 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>
{{#IF isInReportingMode}}
{{/IF}}
Jellyfin Startup
</title>
<style>
* {
font-family: sans-serif;
}
.flex-row {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: normal;
}
.flex-col {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: normal;
}
header {
height: 5rem;
width: 100%;
}
header svg {
height: 3rem;
width: 9rem;
margin-right: 1rem;
}
/* ol.action-list {
list-style-type: none;
position: relative;
} */
ol.action-list * {
font-family: monospace;
font-weight: 300;
font-size: clamp(18px, 100vw / var(--width), 20px);
font-feature-settings: 'onum', 'pnum';
line-height: 1.8;
-webkit-text-size-adjust: none;
}
/*
ol.action-list li {
padding-top: .5rem;
}
ol.action-list li::before {
position: absolute;
left: -0.8em;
font-size: 1.1em;
} */
/* Attribution as heavily inspired by: https://iamkate.com/code/tree-views/ */
.action-list {
--spacing: 1.4rem;
--radius: 14px;
}
.action-list li {
display: block;
position: relative;
padding-left: calc(2 * var(--spacing) - var(--radius) - 1px);
}
.action-list ul {
margin-left: calc(var(--radius) - var(--spacing));
padding-left: 0;
}
.action-list ul li {
border-left: 2px solid #ddd;
}
.action-list ul li:last-child {
border-color: transparent;
}
.action-list ul li::before {
content: '';
display: block;
position: absolute;
top: calc(var(--spacing) / -2);
left: -2px;
width: calc(var(--spacing) + 2px);
height: calc(var(--spacing) + 1px);
border: solid #ddd;
border-width: 0 0 2px 2px;
}
.action-list summary {
display: block;
cursor: pointer;
}
.action-list summary::marker,
.action-list summary::-webkit-details-marker {
display: none;
}
.action-list summary:focus {
outline: none;
}
.action-list summary:focus-visible {
outline: 1px dotted #000;
}
.action-list li::after,
.action-list summary::before {
content: '';
display: block;
position: absolute;
top: calc(var(--spacing) / 2 - var(--radius) + 4px);
left: calc(var(--spacing) - var(--radius) - -5px);
}
.action-list summary::before {
z-index: 1;
/* background: #696 url('expand-collapse.svg') 0 0; */
}
.action-list details[open]>summary::before {
background-position: calc(-2 * var(--radius)) 0;
}
.action-list li.danger-item::after,
.action-list li.danger-strong-item::after {
content: '❌';
}
ol.action-list li span.danger-strong-item {
text-decoration-style: solid;
text-decoration-color: red;
text-decoration-line: underline;
}
ol.action-list li.warn-item::after {
content: '⚠️';
}
ol.action-list li.success-item::after {
content: '✅';
}
ol.action-list li.info-item::after {
content: '🔹';
}
/* End Attribution */
</style>
</head>
<body>
<div>
<header class="flex-row">
{{^IF isInReportingMode}}
<p>Jellyfin Server {{version}} still starting. Please wait.</p>
{{#ELSE}}
<p>Jellyfin Server has encountered an error and was not able to start.</p>
{{/ELSE}}
{{/IF}}
{{#IF localNetworkRequest}}
<p style="margin-left: 1rem;">You can download the current log file <a href='/startup/logger'
target="_blank">here</a>.</p>
{{/IF}}
</header>
{{#DECLARE LogEntry |--}}
{{#LET children = Children}}
<li class="{{FormatLogLevel(children).ToString()}}-item">
{{--| #IF children.Count > 0}}
<details open>
<summary>{{DateOfCreation}} - {{Content}}</summary>
<ul class="action-list">
{{--| #EACH children.Reverse() |-}}
{{#IMPORT 'LogEntry'}}
{{--| /EACH |-}}
</ul>
</details>
{{--| #ELSE |-}}
<span class="{{FormatLogLevel(children).ToString()}}-item">{{DateOfCreation}} - {{Content}}</span>
{{--| /ELSE |--}}
{{--| /IF |-}}
</li>
{{--| /DECLARE}}
{{#IF localNetworkRequest}}
<div class="flex-col">
<ol class="action-list">
{{#FOREACH log IN logs.Reverse()}}
{{#IMPORT 'LogEntry' #WITH log}}
{{/FOREACH}}
</ol>
</div>
{{#ELSE}}
{{#IF networkManagerReady}}
<p>Please visit this page from your local network to view detailed startup logs.</p>
{{#ELSE}}
<p>Initializing network settings. Please wait.</p>
{{/ELSE}}
{{/IF}}
{{/ELSE}}
{{/IF}}
</div>
</body>
{{^IF isInReportingMode}}
<script>
setTimeout(() => {
window.location.reload();
}, {{ retryValue.TotalMilliseconds }});
</script>
{{/IF}}
</html>
Binary file not shown.
@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Publish","Endpoints":[]}
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,393 @@
#!/bin/bash
#
# SYNOPSIS
# Automated weekly performance monitoring for Jellyfin PostgreSQL database
#
# DESCRIPTION
# This script:
# 1. Runs comprehensive diagnostics
# 2. Analyzes slow queries
# 3. Tracks index usage over time
# 4. Generates weekly performance reports
# 5. Optionally runs VACUUM ANALYZE on tables with high dead tuples
# 6. Can be scheduled via cron
#
# USAGE
# ./Weekly-Performance-Monitor.sh [OPTIONS]
#
# OPTIONS
# -o, --output-directory DIR Directory to save reports (default: reports/weekly)
# -e, --email-report If specified, emails the report (requires email configuration)
# -c, --compare-with-previous Compare current stats with previous week
# -a, --auto-vacuum-analyze Enable auto vacuum analyze
# --dead-tuple-threshold NUM Minimum dead tuples to trigger vacuum (default: 100)
# --dead-tuple-percent-threshold NUM Minimum dead tuple percentage to trigger vacuum (default: 0.02)
#
# EXAMPLE
# ./Weekly-Performance-Monitor.sh
# ./Weekly-Performance-Monitor.sh -o "/reports/jellyfin" -c
# ./Weekly-Performance-Monitor.sh -a --dead-tuple-threshold 10000 --dead-tuple-percent-threshold 20
#
# CRON JOB EXAMPLE
# # Run every Monday at 6am
# 0 6 * * 1 /path/to/Weekly-Performance-Monitor.sh -a
#
# ============================================================================
# Default Configuration
# ============================================================================
OUTPUT_DIRECTORY="reports/weekly"
EMAIL_REPORT=false
COMPARE_WITH_PREVIOUS=false
AUTO_VACUUM_ANALYZE=false
DEAD_TUPLE_THRESHOLD=100
DEAD_TUPLE_PERCENT_THRESHOLD=0.02
# ============================================================================
# Helper Functions
# ============================================================================
# Color definitions
CYAN='\033[0;36m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
GRAY='\033[0;90m'
NC='\033[0m' # No Color
write_color_output() {
local message="$1"
local color="$2"
echo -e "${color}${message}${NC}"
}
get_report_timestamp() {
date +"%Y-%m-%d_%H%M%S"
}
get_week_number() {
date +%V
}
# ============================================================================
# Script Argument Parsing
# ============================================================================
while [[ "$#" -gt 0 ]]; do
case $1 in
-o|--output-directory) OUTPUT_DIRECTORY="$2"; shift ;;
-e|--email-report) EMAIL_REPORT=true ;;
-c|--compare-with-previous) COMPARE_WITH_PREVIOUS=true ;;
-a|--auto-vacuum-analyze) AUTO_VACUUM_ANALYZE=true ;;
--dead-tuple-threshold) DEAD_TUPLE_THRESHOLD="$2"; shift ;;
--dead-tuple-percent-threshold) DEAD_TUPLE_PERCENT_THRESHOLD="$2"; shift ;;
*) write_color_output "Unknown parameter passed: $1" "$RED"; exit 1 ;;
esac
shift
done
# ============================================================================
# Configuration
# ============================================================================
set -e # Exit on error
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Load database configuration
source "./db-config.sh"
# ============================================================================
# Functions
# ============================================================================
test_database_connection() {
write_color_output "\nTesting database connection..." "$CYAN"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "SELECT version();" > /dev/null 2>&1; then
write_color_output "[OK] Database connection successful" "$GREEN"
return 0
else
write_color_output "[ERROR] Database connection failed" "$RED"
return 1
fi
}
invoke_diagnostic_report() {
local output_file="$1"
write_color_output "\nRunning comprehensive diagnostics..." "$CYAN"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$PROJECT_ROOT/sql/diagnostics.sql" > "$output_file" 2>&1; then
write_color_output "[OK] Diagnostics completed: $output_file" "$GREEN"
else
write_color_output "[WARN] Diagnostics completed with warnings" "$YELLOW"
fi
}
invoke_query_analysis() {
local output_file="$1"
write_color_output "\nAnalyzing slow queries..." "$CYAN"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$PROJECT_ROOT/sql/query-analysis.sql" > "$output_file" 2>&1; then
write_color_output "[OK] Query analysis completed: $output_file" "$GREEN"
else
write_color_output "[WARN] Query analysis completed with warnings" "$YELLOW"
fi
}
get_index_usage_stats() {
local output_file="$1"
write_color_output "\nGathering index usage statistics..." "$CYAN"
local query="
SELECT
schemaname,
relname AS tablename,
indexrelname,
idx_scan,
idx_tup_read,
idx_tup_fetch,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
ORDER BY schemaname, relname, idx_scan DESC;
"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "$query" > "$output_file" 2>&1; then
write_color_output "[OK] Index stats gathered: $output_file" "$GREEN"
else
write_color_output "[WARN] Index stats completed with warnings" "$YELLOW"
fi
}
get_table_size_stats() {
local output_file="$1"
write_color_output "\nGathering table size statistics..." "$CYAN"
local query="
SELECT
schemaname,
relname,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) AS total_size,
pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) AS table_size,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname) - pg_relation_size(schemaname||'.'||relname)) AS index_size,
n_live_tup AS row_count,
n_dead_tup AS dead_rows,
ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
ORDER BY pg_total_relation_size(schemaname||'.'||relname) DESC;
"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "$query" > "$output_file" 2>&1; then
write_color_output "[OK] Table size stats gathered: $output_file" "$GREEN"
else
write_color_output "[WARN] Table size stats completed with warnings" "$YELLOW"
fi
}
invoke_conditional_vacuum_analyze() {
local min_dead_tuples="$1"
local min_dead_percent="$2"
write_color_output "\nChecking for tables that need VACUUM ANALYZE..." "$CYAN"
write_color_output "Thresholds: dead tuples >= $min_dead_tuples and dead percent >= $min_dead_percent" "$GRAY"
local table_query="
SELECT
format('%I.%I', schemaname, relname) AS qualified_table,
n_dead_tup,
ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
AND n_live_tup > 0
AND n_dead_tup >= $min_dead_tuples
AND (100.0 * n_dead_tup / NULLIF(n_live_tup, 0)) >= $min_dead_percent
ORDER BY n_dead_tup DESC;
"
tables=$(PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -t -A -F "|" -c "$table_query")
if [ -z "$tables" ]; then
write_color_output "[OK] No tables exceeded dead tuple thresholds" "$GREEN"
return
fi
write_color_output "Found $(echo "$tables" | wc -l) table(s) requiring VACUUM ANALYZE" "$YELLOW"
while IFS= read -r line; do
qualified_table=$(echo "$line" | cut -d'|' -f1)
dead_tuples=$(echo "$line" | cut -d'|' -f2)
dead_percent=$(echo "$line" | cut -d'|' -f3)
write_color_output "Running VACUUM ANALYZE on $qualified_table (dead tuples: $dead_tuples, dead percent: $dead_percent)" "$CYAN"
if PGPASSWORD=$DB_PASS $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "VACUUM (ANALYZE, VERBOSE) $qualified_table;"; then
write_color_output "[OK] VACUUM ANALYZE completed for $qualified_table" "$GREEN"
else
write_color_output "[ERROR] VACUUM ANALYZE failed for $qualified_table" "$RED"
fi
done <<< "$tables"
}
compare_with_previous() {
local current_report="$1"
local output_dir="$2"
write_color_output "\nComparing with previous week's report..." "$CYAN"
previous_report=$(find "$output_dir" -name "diagnostics_*.txt" ! -name "$(basename "$current_report")" -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)
if [ -z "$previous_report" ]; then
write_color_output "[WARN] No previous reports found for comparison" "$YELLOW"
return
fi
write_color_output "Comparing with: $(basename "$previous_report")" "$CYAN"
# TODO: Add detailed comparison logic
write_color_output "[OK] Comparison data available" "$GREEN"
write_color_output " Current: $current_report" "$GRAY"
write_color_output " Previous: $previous_report" "$GRAY"
}
new_summary_report() {
local output_dir="$1"
local timestamp="$2"
write_color_output "\nGenerating summary report..." "$CYAN"
local summary_file="$output_dir/summary_$timestamp.txt"
cat > "$summary_file" <<EOF
================================================================================
JELLYFIN DATABASE WEEKLY PERFORMANCE SUMMARY
================================================================================
Report Date: $(date +"%Y-%m-%d %H:%M:%S")
Database: $DB_NAME at $DB_HOST
Week Number: $(get_week_number)
================================================================================
REPORTS GENERATED:
* diagnostics_$timestamp.txt : Full diagnostic report
* query_analysis_$timestamp.txt : Slow query analysis
* index_usage_$timestamp.txt : Index usage statistics
* table_sizes_$timestamp.txt : Table size and bloat info
* summary_$timestamp.txt : This summary
================================================================================
QUICK HEALTH CHECK:
================================================================================
To view critical issues check diagnostics file for:
* Section 5: TABLES WITH HIGH SEQUENTIAL SCANS
* Section 8: UNUSED OR RARELY USED INDEXES
* Section 10: SLOWEST QUERIES
To identify slow queries check query_analysis file for:
* Section 2: TOP 20 SLOWEST QUERIES (by max execution time)
* Section 5: BASEITEMS TABLE QUERIES ANALYSIS
================================================================================
RECOMMENDED ACTIONS:
================================================================================
* Review slow queries taking longer than 10 seconds
* Check if new indexes are being utilized
* Look for tables with many sequential scans
* Monitor cache hit ratio (should be above 95 percent)
* Check for tables with high dead tuples percentage (need VACUUM)
================================================================================
AUTOMATION SETUP:
================================================================================
To schedule this script weekly, add the following to your crontab:
0 6 * * 1 /path/to/$(basename "$0")
================================================================================
NEXT REPORT: $(date -d "+7 days" +"%Y-%m-%d")
================================================================================
EOF
write_color_output "[OK] Summary generated: $summary_file" "$GREEN"
}
# ============================================================================
# Main Execution
# ============================================================================
write_color_output "\n${CYAN}========================================${NC}"
write_color_output "Jellyfin Database Weekly Monitor" "$CYAN"
write_color_output "========================================${NC}"
write_color_output "Database: $DB_NAME" "$YELLOW"
write_color_output "Host: $DB_HOST" "$YELLOW"
write_color_output "Week: $(get_week_number)" "$YELLOW"
write_color_output "Date: $(date +'%Y-%m-%d %H:%M:%S')" "$YELLOW"
write_color_output "========================================\n${NC}"
# Create output directory
report_dir="./$OUTPUT_DIRECTORY"
mkdir -p "$report_dir"
write_color_output "Created report directory: $report_dir" "$GREEN"
# Generate timestamp for this run
timestamp=$(get_report_timestamp)
# Test connection
if ! test_database_connection; then
write_color_output "\n[ERROR] Cannot connect to database. Exiting." "$RED"
exit 1
fi
# Run diagnostics
diagnostics_file="$report_dir/diagnostics_$timestamp.txt"
invoke_diagnostic_report "$diagnostics_file"
# Run query analysis
query_analysis_file="$report_dir/query_analysis_$timestamp.txt"
invoke_query_analysis "$query_analysis_file"
# Gather index usage stats
index_usage_file="$report_dir/index_usage_$timestamp.txt"
get_index_usage_stats "$index_usage_file"
# Gather table size stats
table_sizes_file="$report_dir/table_sizes_$timestamp.txt"
get_table_size_stats "$table_sizes_file"
# Optionally vacuum/analyze tables
if [ "$AUTO_VACUUM_ANALYZE" = true ]; then
invoke_conditional_vacuum_analyze "$DEAD_TUPLE_THRESHOLD" "$DEAD_TUPLE_PERCENT_THRESHOLD"
fi
# Compare with previous week
if [ "$COMPARE_WITH_PREVIOUS" = true ]; then
compare_with_previous "$diagnostics_file" "$report_dir"
fi
# Generate summary
new_summary_report "$report_dir" "$timestamp"
# Cleanup old reports (keep last 12 weeks)
write_color_output "\nCleaning up old reports (keeping last 12 weeks)..." "$CYAN"
find "$report_dir" -type f -mtime +84 -delete
write_color_output "[OK] Cleanup complete" "$GREEN"
write_color_output "\n${GREEN}========================================${NC}"
write_color_output "[OK] WEEKLY MONITORING COMPLETE" "$GREEN"
write_color_output "========================================${NC}"
write_color_output "Reports saved to: $report_dir" "$YELLOW"
write_color_output "Summary: summary_$timestamp.txt" "$YELLOW"
write_color_output "\nNext steps:" "$CYAN"
write_color_output "1. Review summary_$timestamp.txt" "$NC"
write_color_output "2. Check for critical issues in diagnostics report" "$NC"
write_color_output "3. Analyze slow queries in query_analysis report" "$NC"
write_color_output "========================================\n${NC}"
# Email report if requested
if [ "$EMAIL_REPORT" = true ]; then
write_color_output "[WARN] Email functionality not yet implemented" "$YELLOW"
write_color_output " Configure a mail client like 'mail' or 'sendmail'" "$GRAY"
fi
exit 0
+78
View File
@@ -0,0 +1,78 @@
#!/bin/bash
# Database Configuration
# Edit this file to change which database commands run against
# Database connection settings
PSQL_PATH="/usr/bin/psql"
DB_USER="jellyfin"
DB_NAME="jellyfin_test2" # ← Change this to switch databases
DB_HOST="192.168.129.253"
DB_PORT="6432"
# Export for use in other scripts
export PSQL_PATH
export DB_USER
export DB_NAME
export DB_HOST
export DB_PORT
# Helper function to run psql commands
invoke_psql() {
local query=""
local file=""
local output_file=""
while [[ $# -gt 0 ]]; do
case "$1" in
-q|--query)
query="$2"
shift 2
;;
-f|--file)
file="$2"
shift 2
;;
-o|--output-file)
output_file="$2"
shift 2
;;
*)
echo "Unknown option: $1"
return 1
;;
esac
done
local base_cmd="$PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME"
local cmd=""
if [ -n "$file" ]; then
cmd="$base_cmd -f \"$file\""
elif [ -n "$query" ]; then
cmd="$base_cmd -c \"$query\""
else
echo "Must provide either --query or --file parameter"
return 1
fi
if [ -n "$output_file" ]; then
cmd+=" > \"$output_file\""
fi
echo -e "\e[36mConnecting to: $DB_NAME@$DB_HOST as $DB_USER\e[0m"
eval "$cmd"
}
# Display current configuration
echo -e "\e[36m========================================\e[0m"
echo -e "\e[36mDatabase Configuration Loaded\e[0m"
echo -e "\e[36m========================================\e[0m"
echo -e "\e[33mDatabase: $DB_NAME\e[0m"
echo -e "\e[33mUser: $DB_USER\e[0m"
echo -e "\e[33mHost: $DB_HOST\e[0m"
echo -e "\e[33mPort: $DB_PORT\e[0m"
echo -e "\e[36m========================================\e[0m"
echo ""
echo -e "\e[90mTo change database, edit: db-config.sh\e[0m"
echo -e "\e[90mThen run: source db-config.sh\e[0m"
echo ""
@@ -0,0 +1,30 @@
#!/bin/bash
# Linux script to rebuild the Jellyfin solution properly
# This script cleans and rebuilds in the correct order to resolve CS0006 metadata errors
echo -e "\e[36mStarting Jellyfin solution rebuild...\e[0m"
# Step 1: Clean the solution
echo -e "\n\e[33m[1/3] Cleaning solution...\e[0m"
dotnet clean Jellyfin.sln --configuration Debug
# Remove obj and bin directories to ensure clean state
echo -e "\n\e[33m[2/3] Removing build artifacts...\e[0m"
find . -name bin -type d -exec rm -rf {} +
find . -name obj -type d -exec rm -rf {} +
# Step 3: Restore NuGet packages
echo -e "\n\e[33m[3/3] Restoring NuGet packages...\e[0m"
dotnet restore Jellyfin.sln
# Step 4: Build the solution
echo -e "\n\e[33m[4/4] Building solution...\e[0m"
dotnet build Jellyfin.sln --configuration Debug --no-restore
echo -e "\n\e[32mBuild complete!\e[0m"
echo -e "\e[36mIf you still see errors, try building specific projects in this order:\e[0m"
echo -e "\e[90m 1. Jellyfin.Database.Implementations\e[0m"
echo -e "\e[90m 2. Jellyfin.Database.Providers.Postgres\e[0m"
echo -e "\e[90m 3. Jellyfin.Server.Implementations\e[0m"
echo -e "\e[90m 4. Emby.Server.Implementations\e[0m"
echo -e "\e[90m 5. Jellyfin.Server\e[0m"
@@ -0,0 +1,227 @@
# Jellyfin PostgreSQL Database Setup (.NET 11 Preview)
## Overview
Due to .NET 11 being a preview release, EF Core migrations cannot run automatically at startup. Database schema is created using SQL scripts.
**✨ NEW: Automatic Schema Initialization**
If the database is **empty** (no `library` schema found), Jellyfin will **automatically execute** `sql/schema_init/create_database_schema.sql` at startup!
---
## 🚀 Automatic Setup (Recommended)
### For Fresh Database - Zero Configuration!
1. **Create an empty PostgreSQL database**:
```sh
psql -U postgres -c "CREATE DATABASE jellyfin OWNER jellyfin;"
```
2. **Just start Jellyfin**:
```sh
dotnet run --project Jellyfin.Server
```
**That's it!** Jellyfin will:
- ✅ Detect the database is empty (no `library` schema)
- ✅ Automatically load `sql/schema_init/create_database_schema.sql`
- ✅ Execute the full schema creation script
- ✅ Initialize all tables, schemas, and base indexes
- ✅ Continue with normal startup
**What you'll see in logs**:
```
[INF] Database is empty (library schema not found). Attempting to initialize from SQL script...
[INF] Found schema script at: D:\Jellyfin\sql\schema_init\create_database_schema.sql
[INF] Executing create_database_schema.sql to initialize database...
[INF] Successfully initialized database from SQL script
[INF] Database schema verification complete.
```
---
## 🔧 Manual Setup (Alternative)
If you prefer manual control or the automatic setup fails:
## 🔧 Manual Setup (Alternative)
If you prefer manual control or the automatic setup fails:
### Step 1: Create Database and Schema
Run the main schema creation script:
```sh
psql -U jellyfin -d postgres -f sql/schema_init/create_database_schema.sql
```
This creates:
- All database schemas (library, activitylog, authentication, etc.)
- All tables with proper columns and data types
- Primary keys and foreign keys
- Base indexes
---
### Step 2: Apply Supplementary Performance Indexes
After the main schema is created, apply the performance indexes:
```sh
psql -U jellyfin -d jellyfin -f sql/indexes/apply-supplementary-indexes-migration.sql
```
This creates 5 additional performance indexes for:
- Library browsing
- Folder hierarchy
- Recently added content
- Genre/tag filtering
- Episode deduplication
---
### Step 3: Apply ActivityLog Index Fix
If the ActivityLogs table exists, apply the final index:
```sh
psql -U jellyfin -d jellyfin -f sql/fix-activitylog-index.sql
```
This creates the 6th performance index for user activity queries.
---
## 📋 What Changed for .NET 11 Preview
### Automatic SQL Script Execution ✨
**New Feature**: Empty database auto-initialization!
When Jellyfin starts:
1. ✅ Connects to PostgreSQL database
2. ✅ Checks if `library` schema exists
3. ✅ **If empty**: Automatically runs `sql/schema_init/create_database_schema.sql`
4. ✅ **If not empty**: Skips initialization and proceeds normally
5. ⚠️ Logs warning about any pending migrations (doesn't apply them)
6. ✅ Continues with normal startup
### In Code (`PostgresDatabaseProvider.cs`)
**Disabled**:
- ❌ `context.Database.EnsureCreatedAsync()` - Bypasses migration tracking
- ❌ `context.Database.MigrateAsync()` - Not compatible with .NET 11 preview
**Added**:
- ✅ Empty database detection (checks for `library` schema)
- ✅ Automatic SQL script execution from `sql/schema_init/create_database_schema.sql`
- ✅ Proper error handling and logging
- ✅ 10-minute timeout for large schema creation
---
## 🔄 Upgrading Existing Database
If you already have a Jellyfin database and are upgrading:
### Option 1: Apply Only Missing Indexes (Recommended)
If your database structure is already correct, just add the performance indexes:
```sh
# Apply supplementary indexes
psql -U jellyfin -d jellyfin -f sql/indexes/apply-supplementary-indexes-migration.sql
# Apply ActivityLog index fix
psql -U jellyfin -d jellyfin -f sql/fix-activitylog-index.sql
```
### Option 2: Full Schema Recreation (Data Loss!)
**⚠️ WARNING: This will delete all data!**
Only do this for a fresh start:
```sh
# Drop existing database
psql -U jellyfin -d postgres -c "DROP DATABASE IF EXISTS jellyfin;"
# Recreate from schema
psql -U jellyfin -d postgres -f sql/schema_init/create_database_schema.sql
psql -U jellyfin -d jellyfin -f sql/indexes/apply-supplementary-indexes-migration.sql
psql -U jellyfin -d jellyfin -f sql/fix-activitylog-index.sql
```
---
## 📁 SQL Script Locations
```
D:\Projects\pgsql-jellyfin\
├── sql/
│ ├── schema_init/
│ │ ├── create_database_schema.sql ← Main database schema
│ │ └── 10_create_supplementary_indexes.sql ← (EF migration file)
│ ├── indexes/
│ │ └── apply-supplementary-indexes-migration.sql ← Performance indexes
│ ├── diagnostics/
│ │ ├── diagnostics.sql
│ │ ├── monitor-query-performance.sql
│ │ └── query-analysis.sql
│ ├── fix-activitylog-index.sql ← ActivityLog index fix
│ └── README-DATABASE-SETUP.md ← This file
```
---
## ✅ Verification
After running the scripts, verify everything is set up correctly:
```sql
-- Check schemas exist
SELECT schema_name FROM information_schema.schemata
WHERE schema_name IN ('library', 'activitylog', 'authentication', 'displaypreferences');
-- Check migration history
SELECT * FROM library."__EFMigrationsHistory" ORDER BY "MigrationId";
-- Check indexes
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE indexname LIKE 'idx_%'
ORDER BY tablename, indexname;
```
Expected:
- ✅ 4+ schemas
- ✅ 3+ migrations in history
- ✅ 6+ custom performance indexes
---
## 🎯 When .NET 11 Goes GA
When .NET 11 is officially released (no longer preview):
1. Uncomment the migration code in `PostgresDatabaseProvider.cs`
2. Remove the warning log statements
3. EF Core migrations will work automatically again
---
## 📞 Need Help?
If you encounter issues:
1. **Check logs**: Look for database connection errors
2. **Verify credentials**: Ensure PostgreSQL user has proper permissions
3. **Check schema**: Run verification queries above
4. **Manual fixes**: All scripts use `IF NOT EXISTS` - safe to re-run
---
**Created**: 2026-03-07
**Commit**: `0911146` - EF migrations disabled for .NET 11 preview
@@ -0,0 +1,40 @@
@echo off
REM Quick script to add ALL performance indexes (base + supplementary)
echo ========================================
echo Add ALL Performance Indexes
echo ========================================
echo.
echo This will create:
echo - 18 base performance indexes
echo - 5 supplementary indexes
echo Total: 23 indexes
echo.
echo NOTE: This uses the database configured in db-config.ps1
echo Current default: jellyfin_testsdata
echo.
echo This may take 10-30 minutes...
echo.
REM Use PowerShell to load config and run
powershell -Command ". .\db-config.ps1; & $PSQL_PATH -U $DB_USER -d $DB_NAME -f sql\all_performance_indexes.sql"
if %ERRORLEVEL% EQU 0 (
echo.
echo ========================================
echo Success! All 23 indexes added.
echo ========================================
echo.
echo Performance improvement: 70-90 percent faster!
echo.
echo Next: Restart Jellyfin
echo.
) else (
echo.
echo ========================================
echo Failed to add indexes
echo ========================================
echo.
)
pause
@@ -0,0 +1,37 @@
@echo off
REM Add missing base performance indexes to Jellyfin database
echo ========================================
echo Add Base Performance Indexes
echo ========================================
echo.
echo These indexes were missing from InitialCreate
echo Adding 18 essential performance indexes...
echo.
"C:\Program Files\PostgreSQL\18\bin\psql.exe" -U jellyfin -d jellyfin -f sql\add_base_performance_indexes.sql
if %ERRORLEVEL% EQU 0 (
echo.
echo ========================================
echo Success! Base indexes added.
echo ========================================
echo.
echo Indexes created:
echo - 9 on BaseItems
echo - 2 on BaseItemProviders
echo - 2 on MediaStreamInfos
echo - 2 on PeopleBaseItemMap
echo - 3 on UserData
echo.
echo Next: Restart Jellyfin
echo.
) else (
echo.
echo ========================================
echo Failed to add indexes
echo ========================================
echo.
)
pause
@@ -0,0 +1,38 @@
@echo off
REM Quick script to add supplementary indexes to jellyfin_testsdata
echo ========================================
echo Add Supplementary Indexes
echo ========================================
echo.
echo Connecting to jellyfin_testsdata database...
echo.
REM Apply the indexes
"C:\Program Files\PostgreSQL\18\bin\psql.exe" -U jellyfin -d jellyfin -f sql\schema_init\10_create_supplementary_indexes.sql
if %ERRORLEVEL% EQU 0 (
echo.
echo ========================================
echo Success! Indexes added.
echo ========================================
echo.
echo Next steps:
echo 1. Restart Jellyfin
echo 2. Monitor performance
echo.
) else (
echo.
echo ========================================
echo Failed to add indexes
echo ========================================
echo.
echo Troubleshooting:
echo - Ensure PostgreSQL is running
echo - Check database name is correct
echo - Verify credentials
echo.
)
pause
@@ -0,0 +1,20 @@
# Add Supplementary Indexes - Quick Command
Write-Host "Connecting to jellyfin_testsdata and adding supplementary indexes..." -ForegroundColor Cyan
Write-Host ""
# Quick and simple - just run the SQL script
psql -U jellyfin -d jellyfin_testsdata -f sql\schema_init\10_create_supplementary_indexes.sql
if ($LASTEXITCODE -eq 0) {
Write-Host ""
Write-Host "✓ Success! Supplementary indexes added." -ForegroundColor Green
Write-Host ""
Write-Host "Next: Restart Jellyfin to see performance improvements" -ForegroundColor Yellow
} else {
Write-Host ""
Write-Host "❌ Failed. Check that:" -ForegroundColor Red
Write-Host " - PostgreSQL is running" -ForegroundColor Gray
Write-Host " - Database 'jellyfin_testsdata' exists" -ForegroundColor Gray
Write-Host " - You have the correct password" -ForegroundColor Gray
}
@@ -0,0 +1,175 @@
# Apply Supplementary Indexes Migration to PostgreSQL Database
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Apply Supplementary Indexes Migration" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Configuration
$connectionString = "Host=localhost;Port=5432;Database=jellyfin_testsdata;Username=jellyfin;Password=YOUR_PASSWORD"
$migrationName = "AddSupplementaryIndexes"
Write-Host "Step 1: Build the solution" -ForegroundColor Yellow
Write-Host "This ensures the migration is compiled..." -ForegroundColor Gray
dotnet build src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres\Jellyfin.Database.Providers.Postgres.csproj
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Build failed. Please fix compilation errors." -ForegroundColor Red
exit 1
}
Write-Host "✓ Build successful" -ForegroundColor Green
Write-Host ""
Write-Host "Step 2: Check current database state" -ForegroundColor Yellow
Write-Host "Connecting to jellyfin_testsdata..." -ForegroundColor Gray
# Test database connection
try {
$testQuery = @"
SELECT EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'library'
AND table_name = 'BaseItems'
) as table_exists;
"@
$result = psql -U jellyfin -d jellyfin_testsdata -t -c $testQuery 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ Database connection successful" -ForegroundColor Green
} else {
Write-Host "❌ Database connection failed" -ForegroundColor Red
Write-Host "Error: $result" -ForegroundColor Red
Write-Host ""
Write-Host "Please ensure:" -ForegroundColor Yellow
Write-Host " 1. PostgreSQL is running" -ForegroundColor Gray
Write-Host " 2. Database 'jellyfin_testsdata' exists" -ForegroundColor Gray
Write-Host " 3. Credentials are correct" -ForegroundColor Gray
exit 1
}
} catch {
Write-Host "❌ Failed to test database connection: $_" -ForegroundColor Red
exit 1
}
Write-Host ""
Write-Host "Step 3: Check for existing indexes" -ForegroundColor Yellow
Write-Host "Checking which supplementary indexes already exist..." -ForegroundColor Gray
$checkIndexQuery = @"
SELECT
indexname,
CASE WHEN indexname IS NOT NULL THEN 'EXISTS' ELSE 'MISSING' END as status
FROM (
VALUES
('idx_baseitems_type_isvirtualitem_topparentid'),
('idx_baseitems_topparentid_isfolder'),
('idx_baseitems_datecreated_filtered'),
('idx_itemvaluesmap_itemvalueid_itemid'),
('idx_activitylogs_userid_datecreated')
) AS wanted(indexname)
LEFT JOIN pg_indexes ON pg_indexes.indexname = wanted.indexname
ORDER BY wanted.indexname;
"@
$indexStatus = psql -U jellyfin -d jellyfin_testsdata -c $checkIndexQuery
Write-Host $indexStatus
Write-Host ""
Write-Host "Step 4: Apply migration using EF Core" -ForegroundColor Yellow
Write-Host "This will create any missing indexes..." -ForegroundColor Gray
# Option 1: Use dotnet ef (if you have it installed)
Write-Host ""
Write-Host "Choose migration method:" -ForegroundColor Cyan
Write-Host "1. Use dotnet ef database update (requires dotnet-ef tool)" -ForegroundColor White
Write-Host "2. Use SQL script generation" -ForegroundColor White
Write-Host "3. Apply SQL directly (recommended)" -ForegroundColor White
Write-Host ""
$choice = Read-Host "Enter your choice (1, 2, or 3)"
switch ($choice) {
"1" {
Write-Host "Applying migration using dotnet ef..." -ForegroundColor Yellow
$env:ConnectionStrings__DefaultConnection = $connectionString
dotnet ef database update --project src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres\Jellyfin.Database.Providers.Postgres.csproj
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ Migration applied successfully" -ForegroundColor Green
} else {
Write-Host "❌ Migration failed" -ForegroundColor Red
}
}
"2" {
Write-Host "Generating SQL script..." -ForegroundColor Yellow
dotnet ef migrations script --project src\Jellyfin.Database\Jellyfin.Database.Providers.Postgres\Jellyfin.Database.Providers.Postgres.csproj --output sql\migration_supplementary_indexes.sql
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ SQL script generated at: sql\migration_supplementary_indexes.sql" -ForegroundColor Green
Write-Host ""
Write-Host "To apply, run:" -ForegroundColor Yellow
Write-Host "psql -U jellyfin -d jellyfin_testsdata -f sql\migration_supplementary_indexes.sql" -ForegroundColor White
} else {
Write-Host "❌ Script generation failed" -ForegroundColor Red
}
}
"3" {
Write-Host "Applying SQL directly..." -ForegroundColor Yellow
# Apply the SQL from the schema_init script
psql -U jellyfin -d jellyfin_testsdata -f sql\schema_init\10_create_supplementary_indexes.sql
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ Indexes created successfully" -ForegroundColor Green
} else {
Write-Host "❌ Index creation failed" -ForegroundColor Red
}
}
default {
Write-Host "Invalid choice. Exiting." -ForegroundColor Red
exit 1
}
}
Write-Host ""
Write-Host "Step 5: Verify indexes were created" -ForegroundColor Yellow
$verifyQuery = @"
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) as size
FROM pg_indexes
JOIN pg_stat_user_indexes USING (schemaname, tablename, indexname)
WHERE indexname IN (
'idx_baseitems_type_isvirtualitem_topparentid',
'idx_baseitems_topparentid_isfolder',
'idx_baseitems_datecreated_filtered',
'idx_itemvaluesmap_itemvalueid_itemid',
'idx_activitylogs_userid_datecreated'
)
ORDER BY schemaname, tablename, indexname;
"@
$verification = psql -U jellyfin -d jellyfin_testsdata -c $verifyQuery
Write-Host $verification
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Migration Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host "1. Restart Jellyfin to clear connection pools" -ForegroundColor White
Write-Host "2. Monitor index usage with: psql -U jellyfin -d jellyfin_testsdata -f sql\diagnostics.sql" -ForegroundColor White
Write-Host "3. Check performance improvements" -ForegroundColor White
Write-Host ""
@@ -0,0 +1,388 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Converts C# files from block-scoped to file-scoped namespaces and fixes System namespace conflicts.
.DESCRIPTION
This script converts all C# files in MediaBrowser.Model from block-scoped namespaces
to file-scoped namespaces, and replaces System using directives with global::System
to resolve conflicts with MediaBrowser.Model.System namespace.
.PARAMETER Path
The root path to search for C# files. Defaults to "MediaBrowser.Model"
.PARAMETER DryRun
If specified, shows what would be changed without actually modifying files
.PARAMETER Verbose
Shows detailed processing information
.PARAMETER BackupFiles
Creates .bak files before conversion
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -DryRun
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -BackupFiles
.EXAMPLE
.\ConvertToFileScopedNamespaces.ps1 -Path "MediaBrowser.Model\Dlna" -Verbose
#>
param(
[string]$Path = "MediaBrowser.Model",
[switch]$DryRun,
[switch]$BackupFiles
)
$ErrorActionPreference = "Stop"
$VerbosePreference = if ($PSBoundParameters.ContainsKey('Verbose')) { 'Continue' } else { 'SilentlyContinue' }
function Get-IndentString {
param([string]$Line)
if ($Line -match '^(\s+)') {
return $Matches[1]
}
return ""
}
function Get-IndentLevel {
param([string]$IndentString)
# Count spaces (4 spaces = 1 level) or tabs (1 tab = 1 level)
$spaces = ($IndentString.ToCharArray() | Where-Object { $_ -eq ' ' }).Count
$tabs = ($IndentString.ToCharArray() | Where-Object { $_ -eq "`t" }).Count
return [Math]::Max($tabs, [Math]::Floor($spaces / 4))
}
function Remove-OneIndentLevel {
param([string]$Line, [ref]$IndentChar, [ref]$IndentSize)
if ([string]::IsNullOrWhiteSpace($Line)) {
return $Line
}
$indent = Get-IndentString -Line $Line
if ($indent.Length -eq 0) {
return $Line
}
# Detect indent character and size on first indented line
if ($IndentChar.Value -eq $null) {
if ($indent[0] -eq "`t") {
$IndentChar.Value = "`t"
$IndentSize.Value = 1
} else {
$IndentChar.Value = ' '
# Detect indent size (usually 4 spaces)
$IndentSize.Value = 4
if ($indent.Length -ge 2) {
# Try to detect actual indent size
for ($i = 1; $i -le 8; $i++) {
if ($indent.Length % $i -eq 0) {
$IndentSize.Value = $i
break
}
}
}
}
Write-Verbose "Detected indent: '$($IndentChar.Value)' x $($IndentSize.Value)"
}
# Remove one level of indentation
if ($IndentChar.Value -eq "`t") {
if ($Line -match '^\t(.*)') {
return $Matches[1]
}
} else {
$pattern = "^" + (' ' * $IndentSize.Value) + '(.*)'
if ($Line -match $pattern) {
return $Matches[1]
}
}
return $Line
}
function Test-FileScopedNamespace {
param([string]$Content)
# Check if already using file-scoped namespace (semicolon after namespace declaration)
return $Content -match 'namespace\s+[\w\.]+\s*;'
}
function Test-BlockScopedNamespace {
param([string]$Content)
# Check for block-scoped namespace
return $Content -match 'namespace\s+[\w\.]+\s*\r?\n?\s*\{'
}
function Add-GlobalPrefix {
param([string]$Content)
# System namespaces to prefix (ordered from most specific to least)
$systemNamespaces = @(
'System\.Xml\.Serialization',
'System\.ComponentModel\.DataAnnotations',
'System\.ComponentModel',
'System\.Collections\.Generic',
'System\.Collections\.Concurrent',
'System\.Collections',
'System\.Diagnostics\.CodeAnalysis',
'System\.Diagnostics',
'System\.Globalization',
'System\.Linq\.Expressions',
'System\.Linq',
'System\.Text\.Json\.Serialization',
'System\.Text\.Json',
'System\.Text\.RegularExpressions',
'System\.Text',
'System\.Threading\.Tasks',
'System\.Threading',
'System\.Runtime\.Serialization',
'System\.Runtime\.CompilerServices',
'System\.Runtime',
'System\.Net\.Http',
'System\.Net\.Sockets',
'System\.Net\.WebSockets',
'System\.Net\.Mime',
'System\.Net',
'System\.IO\.Compression',
'System\.IO',
'System\.Security',
'System' # Must be last to avoid partial matches
)
foreach ($ns in $systemNamespaces) {
# Don't add global:: if it's already there
# Match: "using System.X" but not "using global::System.X"
$pattern = '(\s+using\s+)(?!global::)(' + $ns + ')(\s|;)'
$replacement = '${1}global::${2}${3}'
$Content = $Content -replace $pattern, $replacement
}
return $Content
}
function Convert-ToFileScopedNamespace {
param(
[string]$FilePath,
[switch]$DryRun,
[switch]$CreateBackup
)
$content = Get-Content -Path $FilePath -Raw
$originalContent = $content
$modified = $false
# Check current state
$hasFileScopedNS = Test-FileScopedNamespace -Content $content
$hasBlockScopedNS = Test-BlockScopedNamespace -Content $content
if ($hasFileScopedNS) {
Write-Verbose " Already using file-scoped namespace"
# But still add global:: prefixes if needed
$contentWithGlobal = Add-GlobalPrefix -Content $content
if ($contentWithGlobal -ne $content) {
$content = $contentWithGlobal
$modified = $true
Write-Host " [UPDATED] Added global:: prefixes" -ForegroundColor Yellow
} else {
Write-Host " [SKIP] Already correct" -ForegroundColor Gray
return $false
}
}
elseif (-not $hasBlockScopedNS) {
Write-Host " [SKIP] No namespace found" -ForegroundColor Gray
return $false
}
else {
Write-Verbose " Converting block-scoped to file-scoped namespace"
# Extract namespace name and line
if ($content -match 'namespace\s+([\w\.]+)\s*\r?\n?\s*\{') {
$namespaceName = $Matches[1]
Write-Verbose " Namespace: $namespaceName"
} else {
Write-Warning " Could not parse namespace"
return $false
}
# Step 1: Convert namespace declaration to file-scoped
$content = $content -replace '(namespace\s+[\w\.]+)\s*\r?\n?\s*\{', '$1;'
$modified = $true
# Step 2: Add global:: prefixes to System using directives
$content = Add-GlobalPrefix -Content $content
# Step 3: Parse into lines for indentation removal
$lines = $content -split '\r?\n'
$hasWindowsLineEndings = $originalContent -match '\r\n'
# Find namespace declaration line
$namespaceLineIndex = -1
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match '^namespace\s+[\w\.]+\s*;') {
$namespaceLineIndex = $i
break
}
}
if ($namespaceLineIndex -lt 0) {
Write-Warning " Could not find namespace declaration after conversion"
return $false
}
# Step 4: Remove one level of indentation from all lines after namespace
$indentChar = $null
$indentSize = 4
$indentCharRef = [ref]$indentChar
$indentSizeRef = [ref]$indentSize
for ($i = $namespaceLineIndex + 1; $i -lt $lines.Length; $i++) {
$lines[$i] = Remove-OneIndentLevel -Line $lines[$i] -IndentChar $indentCharRef -IndentSize $indentSizeRef
}
# Step 5: Remove the closing brace of the namespace
# Find the last non-empty, non-whitespace line that's just a closing brace
$closingBraceIndex = -1
for ($i = $lines.Length - 1; $i -gt $namespaceLineIndex; $i--) {
$trimmed = $lines[$i].Trim()
if ($trimmed -eq '}') {
$closingBraceIndex = $i
break
}
elseif ($trimmed -ne '' -and $trimmed -ne '}') {
# Found non-brace content, stop looking
break
}
}
if ($closingBraceIndex -ge 0) {
Write-Verbose " Removing closing brace at line $closingBraceIndex"
$lines = $lines[0..($closingBraceIndex-1)] + $lines[($closingBraceIndex+1)..($lines.Length-1)]
}
# Step 6: Rejoin lines
$lineEnding = if ($hasWindowsLineEndings) { "`r`n" } else { "`n" }
$content = ($lines | Where-Object { $_ -ne $null }) -join $lineEnding
# Step 7: Clean up trailing whitespace and ensure single newline at end
$content = $content.TrimEnd()
$content += $lineEnding
Write-Host " [CONVERTED] Block-scoped → File-scoped" -ForegroundColor Green
}
# Apply changes
if ($modified -and $content -ne $originalContent) {
if ($DryRun) {
Write-Host " [DRY-RUN] Would save changes" -ForegroundColor Cyan
return $true
}
if ($CreateBackup) {
$backupPath = "$FilePath.bak"
Copy-Item -Path $FilePath -Destination $backupPath -Force
Write-Verbose " Created backup: $backupPath"
}
# Write with correct encoding (UTF-8 without BOM for .cs files)
[System.IO.File]::WriteAllText($FilePath, $content, [System.Text.UTF8Encoding]::new($false))
return $true
}
return $false
}
# Main script
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "C# File-Scoped Namespace Converter" -ForegroundColor Cyan
Write-Host "Fixes MediaBrowser.Model.System namespace conflicts" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host
if ($DryRun) {
Write-Host "DRY RUN MODE - No files will be modified" -ForegroundColor Yellow
Write-Host
}
if ($BackupFiles) {
Write-Host "Backup mode enabled - .bak files will be created" -ForegroundColor Yellow
Write-Host
}
if (-not (Test-Path $Path)) {
Write-Error "❌ Path not found: $Path"
exit 1
}
# Find all C# files
$files = Get-ChildItem -Path $Path -Filter "*.cs" -Recurse | Where-Object {
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\bin\\" -and
$_.FullName -notmatch "\\Properties\\AssemblyInfo\.cs$"
}
Write-Host "Found $($files.Count) C# files in $Path" -ForegroundColor Cyan
Write-Host
$convertedCount = 0
$skippedCount = 0
$errorCount = 0
$updatedCount = 0
foreach ($file in $files) {
$relativePath = $file.FullName.Replace((Get-Location).Path, "").TrimStart([char]92, [char]47)
Write-Host "[Processing] $relativePath" -ForegroundColor White
try {
$result = Convert-ToFileScopedNamespace -FilePath $file.FullName -DryRun:$DryRun -CreateBackup:$BackupFiles
if ($result) {
if ($result -eq "updated") {
$updatedCount++
} else {
$convertedCount++
}
} else {
$skippedCount++
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
Write-Verbose $_.ScriptStackTrace
$errorCount++
}
}
Write-Host
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " Total files: $($files.Count)" -ForegroundColor White
Write-Host " Converted: $convertedCount" -ForegroundColor Green
Write-Host " Updated (global::): $updatedCount" -ForegroundColor Yellow
Write-Host " Skipped: $skippedCount" -ForegroundColor Gray
Write-Host " Errors: $errorCount" -ForegroundColor Red
if ($DryRun) {
Write-Host
Write-Host "This was a DRY RUN. Run without -DryRun to apply changes." -ForegroundColor Yellow
} elseif ($convertedCount -gt 0 -or $updatedCount -gt 0) {
Write-Host
Write-Host "Conversion complete! Next steps:" -ForegroundColor Green
Write-Host " 1. Review changes with: git diff" -ForegroundColor White
Write-Host " 2. Build project: dotnet build MediaBrowser.Model\MediaBrowser.Model.csproj" -ForegroundColor White
Write-Host " 3. If successful: git add . && git commit -m `"Convert to file-scoped namespaces`"" -ForegroundColor White
}
Write-Host ("=" * 80) -ForegroundColor Cyan
exit $errorCount
@@ -0,0 +1,299 @@
# Async Migration Analysis Script
# Scans the Jellyfin codebase for synchronous database operations that need conversion
param(
[string]$Path = ".",
[switch]$DetailedReport,
[switch]$ExportCsv
)
Write-Host "🔍 Scanning for synchronous database operations..." -ForegroundColor Cyan
Write-Host ""
# Define patterns to search for
$patterns = @{
"SaveChanges" = @{
Pattern = "\.SaveChanges\s*\(\s*\)"
Replacement = "await .SaveChangesAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous database save operation"
}
"ToList" = @{
Pattern = "\.ToList\s*\(\s*\)"
Replacement = "await .ToListAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous list materialization"
}
"ToArray" = @{
Pattern = "\.ToArray\s*\(\s*\)"
Replacement = "await .ToArrayAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous array materialization"
}
"FirstOrDefault" = @{
Pattern = "\.FirstOrDefault\s*\("
Replacement = 'await .FirstOrDefaultAsync('
Severity = "HIGH"
Description = "Synchronous first element query"
}
"First" = @{
Pattern = "\.First\s*\("
Replacement = 'await .FirstAsync('
Severity = "HIGH"
Description = "Synchronous first element query"
}
"SingleOrDefault" = @{
Pattern = "\.SingleOrDefault\s*\("
Replacement = 'await .SingleOrDefaultAsync('
Severity = "MEDIUM"
Description = "Synchronous single element query"
}
"Single" = @{
Pattern = "\.Single\s*\("
Replacement = 'await .SingleAsync('
Severity = "MEDIUM"
Description = "Synchronous single element query"
}
"Any" = @{
Pattern = "\.Any\s*\("
Replacement = 'await .AnyAsync('
Severity = "MEDIUM"
Description = "Synchronous existence check"
}
"Count" = @{
Pattern = "\.Count\s*\(\s*\)"
Replacement = "await .CountAsync(cancellationToken)"
Severity = "MEDIUM"
Description = "Synchronous count operation"
}
"LongCount" = @{
Pattern = "\.LongCount\s*\("
Replacement = "await .LongCountAsync("
Severity = "MEDIUM"
Description = "Synchronous long count operation"
}
"ExecuteDelete" = @{
Pattern = "\.ExecuteDelete\s*\(\s*\)"
Replacement = "await .ExecuteDeleteAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous bulk delete"
}
"ExecuteUpdate" = @{
Pattern = "\.ExecuteUpdate\s*\("
Replacement = "await .ExecuteUpdateAsync("
Severity = "HIGH"
Description = "Synchronous bulk update"
}
"BeginTransaction" = @{
Pattern = "\.BeginTransaction\s*\(\s*\)"
Replacement = "await .BeginTransactionAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous transaction start"
}
"Commit" = @{
Pattern = "transaction\.Commit\s*\(\s*\)"
Replacement = "await transaction.CommitAsync(cancellationToken)"
Severity = "HIGH"
Description = "Synchronous transaction commit"
}
"Rollback" = @{
Pattern = "transaction\.Rollback\s*\(\s*\)"
Replacement = "await transaction.RollbackAsync(cancellationToken)"
Severity = "MEDIUM"
Description = "Synchronous transaction rollback"
}
"Sum" = @{
Pattern = "\.Sum\s*\("
Replacement = 'await .SumAsync('
Severity = "LOW"
Description = "Synchronous sum aggregation"
}
"Average" = @{
Pattern = "\.Average\s*\("
Replacement = 'await .AverageAsync('
Severity = "LOW"
Description = "Synchronous average aggregation"
}
"Min" = @{
Pattern = "\.Min\s*\("
Replacement = 'await .MinAsync('
Severity = "LOW"
Description = "Synchronous min aggregation"
}
"Max" = @{
Pattern = "\.Max\s*\("
Replacement = 'await .MaxAsync('
Severity = "LOW"
Description = "Synchronous max aggregation"
}
"Load" = @{
Pattern = "\.Load\s*\(\s*\)"
Replacement = 'await .LoadAsync(cancellationToken)'
Severity = "MEDIUM"
Description = "Synchronous navigation property load"
}
}
# Exclude patterns (already async or intentionally sync)
$excludePatterns = @(
"Async\s*\(",
"// ASYNC",
"\.ConfigureAwait",
"AsyncEnumerable"
)
# Get all .cs files, excluding test files and migrations initially
$files = Get-ChildItem -Path $Path -Recurse -Filter "*.cs" |
Where-Object {
$_.FullName -notmatch "\\bin\\" -and
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\Migrations\\" -and
$_.FullName -notmatch "\.Designer\.cs$"
}
$results = @()
$summary = @{}
foreach ($file in $files) {
$content = Get-Content -Path $file.FullName -Raw
foreach ($patternName in $patterns.Keys) {
$patternInfo = $patterns[$patternName]
$pattern = $patternInfo.Pattern
# Find matches
$matches = [regex]::Matches($content, $pattern)
foreach ($match in $matches) {
# Check if this line is already async or should be excluded
$lineStart = $content.Substring(0, $match.Index).LastIndexOf("`n")
if ($lineStart -lt 0) { $lineStart = 0 }
$lineEnd = $content.IndexOf("`n", $match.Index)
if ($lineEnd -lt 0) { $lineEnd = $content.Length }
$line = $content.Substring($lineStart, $lineEnd - $lineStart).Trim()
# Skip if already async or excluded
$shouldExclude = $false
foreach ($excludePattern in $excludePatterns) {
if ($line -match $excludePattern) {
$shouldExclude = $true
break
}
}
if (-not $shouldExclude) {
# Calculate line number
$lineNumber = ($content.Substring(0, $match.Index) -split "`n").Count
$result = [PSCustomObject]@{
File = $file.FullName.Replace($Path, "").TrimStart('\')
LineNumber = $lineNumber
Pattern = $patternName
Severity = $patternInfo.Severity
Description = $patternInfo.Description
MatchedText = $match.Value
Line = $line
Replacement = $patternInfo.Replacement
}
$results += $result
# Update summary
if (-not $summary.ContainsKey($patternName)) {
$summary[$patternName] = 0
}
$summary[$patternName]++
}
}
}
}
# Display results
Write-Host "📊 Analysis Complete!" -ForegroundColor Green
Write-Host ""
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host "SUMMARY" -ForegroundColor Yellow
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host ""
$totalIssues = $results.Count
$highSeverity = ($results | Where-Object { $_.Severity -eq "HIGH" }).Count
$mediumSeverity = ($results | Where-Object { $_.Severity -eq "MEDIUM" }).Count
$lowSeverity = ($results | Where-Object { $_.Severity -eq "LOW" }).Count
Write-Host "Total synchronous operations found: " -NoNewline
Write-Host $totalIssues -ForegroundColor Red
Write-Host ""
Write-Host " 🔴 HIGH severity: " -NoNewline
Write-Host $highSeverity -ForegroundColor Red
Write-Host " 🟡 MEDIUM severity: " -NoNewline
Write-Host $mediumSeverity -ForegroundColor Yellow
Write-Host " 🟢 LOW severity: " -NoNewline
Write-Host $lowSeverity -ForegroundColor Green
Write-Host ""
Write-Host "Breakdown by operation type:" -ForegroundColor Cyan
Write-Host ""
$summary.GetEnumerator() | Sort-Object Value -Descending | ForEach-Object {
$severity = $patterns[$_.Key].Severity
$color = switch ($severity) {
"HIGH" { "Red" }
"MEDIUM" { "Yellow" }
"LOW" { "Green" }
}
Write-Host " $($_.Key): " -NoNewline
Write-Host $_.Value -ForegroundColor $color -NoNewline
Write-Host " ($severity)"
}
Write-Host ""
Write-Host "=" * 80 -ForegroundColor DarkGray
if ($DetailedReport) {
Write-Host ""
Write-Host "DETAILED FINDINGS" -ForegroundColor Yellow
Write-Host "=" * 80 -ForegroundColor DarkGray
Write-Host ""
# Group by file
$resultsByFile = $results | Group-Object -Property File
foreach ($fileGroup in $resultsByFile | Sort-Object Name) {
Write-Host ""
Write-Host "📄 $($fileGroup.Name)" -ForegroundColor Cyan
Write-Host "-" * 80 -ForegroundColor DarkGray
foreach ($item in $fileGroup.Group | Sort-Object LineNumber) {
$severityColor = switch ($item.Severity) {
"HIGH" { "Red" }
"MEDIUM" { "Yellow" }
"LOW" { "Green" }
}
Write-Host " Line $($item.LineNumber): " -NoNewline
Write-Host "$($item.Pattern) " -NoNewline -ForegroundColor $severityColor
Write-Host "- $($item.Description)"
Write-Host " Current: " -NoNewline
Write-Host $item.MatchedText -ForegroundColor Gray
Write-Host " Replacement: " -NoNewline
Write-Host $item.Replacement -ForegroundColor Green
}
}
}
if ($ExportCsv) {
$csvPath = Join-Path $Path "async_migration_report.csv"
$results | Export-Csv -Path $csvPath -NoTypeInformation
Write-Host ""
Write-Host "📁 Report exported to: " -NoNewline
Write-Host $csvPath -ForegroundColor Cyan
}
Write-Host ""
Write-Host "💡 TIP: Run with -DetailedReport for line-by-line analysis" -ForegroundColor DarkGray
Write-Host "💡 TIP: Run with -ExportCsv to export findings to CSV" -ForegroundColor DarkGray
Write-Host ""
# Return results for further processing
return $results
@@ -0,0 +1,79 @@
# Apply ItemValues Optimized Indexes
# These indexes target the critical ItemValues table performance issue
# Note: Creates SQL file first, then executes it to avoid CONCURRENTLY transaction issues
. .\db-config.ps1
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Creating Optimized ItemValues Indexes" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Target: library.ItemValues table" -ForegroundColor Yellow
Write-Host "Problem: 1.3 BILLION rows being scanned sequentially" -ForegroundColor Red
Write-Host "Solution: 3 targeted indexes" -ForegroundColor Green
Write-Host ""
Write-Host "This may take 10-30 minutes..." -ForegroundColor Yellow
Write-Host ""
# Create temporary SQL file (CONCURRENTLY requires running outside transaction)
$tempSqlFile = "temp_itemvalues_indexes.sql"
$sqlContent = @"
-- Optimized indexes for ItemValues table
-- Fixes the 1.3 billion row sequential scan issue
-- Index 1: CleanValue lookups (for genre/tag searches)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_cleanvalue
ON library."ItemValues" ("CleanValue")
WHERE "CleanValue" IS NOT NULL;
-- Index 2: Value lookups (for direct value searches)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_value
ON library."ItemValues" ("Value")
WHERE "Value" IS NOT NULL;
-- Index 3: ItemValueId + CleanValue for joins (ItemValuesMap to ItemValues)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_id_cleanvalue
ON library."ItemValues" ("ItemValueId", "CleanValue");
"@
# Write SQL to temp file WITHOUT BOM (UTF8 without BOM)
[System.IO.File]::WriteAllText((Join-Path $PWD $tempSqlFile), $sqlContent, (New-Object System.Text.UTF8Encoding $false))
Write-Host "Executing index creation script..." -ForegroundColor Cyan
Write-Host ""
# Execute the SQL file (allows CONCURRENTLY to work)
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f $tempSqlFile
if ($LASTEXITCODE -eq 0) {
Write-Host ""
Write-Host "[OK] All 3 indexes created successfully!" -ForegroundColor Green
} else {
Write-Host ""
Write-Host "[FAILED] Some indexes may have failed to create" -ForegroundColor Red
Write-Host "Check the output above for details" -ForegroundColor Yellow
}
# Clean up temp file
Remove-Item $tempSqlFile -ErrorAction SilentlyContinue
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Index Creation Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Verify indexes were created
Write-Host "Verifying indexes..." -ForegroundColor Yellow
$queryVerify = "SELECT indexrelname as indexname, pg_size_pretty(pg_relation_size(indexrelid)) as size FROM pg_stat_user_indexes WHERE schemaname = 'library' AND relname = 'ItemValues' AND indexrelname LIKE 'idx_itemvalues_%' ORDER BY indexrelname;"
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c $queryVerify
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Cyan
Write-Host "1. Use Jellyfin normally - browse, filter by genre and tags" -ForegroundColor Gray
Write-Host "2. Wait 1 week" -ForegroundColor Gray
Write-Host "3. Run diagnostics again using db-quick.ps1" -ForegroundColor Gray
Write-Host "4. Check improvement in ItemValues sequential scans" -ForegroundColor Gray
Write-Host ""
Write-Host "Expected improvement: 70-90 percent faster genre and tag queries!" -ForegroundColor Green
@@ -0,0 +1,205 @@
# Jellyfin Windows Path Fixer
# This script checks and fixes Linux paths in Jellyfin configuration files
param(
[string]$ConfigPath = "C:\ProgramData\jellyfin\config",
[string]$DataPath = "C:\ProgramData\jellyfin\data",
[switch]$DryRun = $false,
[switch]$Force = $false
)
Write-Host "=================================================" -ForegroundColor Cyan
Write-Host "Jellyfin Windows Path Fixer" -ForegroundColor Cyan
Write-Host "=================================================" -ForegroundColor Cyan
Write-Host ""
# Check if config path exists
if (-not (Test-Path $ConfigPath)) {
Write-Host "ERROR: Config path not found: $ConfigPath" -ForegroundColor Red
Write-Host "Please specify the correct path using -ConfigPath parameter" -ForegroundColor Yellow
exit 1
}
Write-Host "Config Path: $ConfigPath" -ForegroundColor Green
Write-Host "Data Path: $DataPath" -ForegroundColor Green
Write-Host ""
$issuesFound = @()
# Function to check XML file for Linux paths
function Test-LinuxPathsInXml {
param(
[string]$FilePath,
[string]$FileName
)
if (-not (Test-Path $FilePath)) {
Write-Host " [SKIP] $FileName - File not found" -ForegroundColor Gray
return $null
}
try {
[xml]$xml = Get-Content $FilePath
$linuxPaths = @()
# Check all text nodes for Linux paths
$xml.SelectNodes("//*") | ForEach-Object {
if ($_.InnerText -match '^(/var/|/usr/|/etc/)') {
$linuxPaths += @{
Element = $_.Name
CurrentValue = $_.InnerText
Line = $_.OuterXml
}
}
}
if ($linuxPaths.Count -gt 0) {
Write-Host " [ISSUE] $FileName - Found $($linuxPaths.Count) Linux path(s)" -ForegroundColor Yellow
return @{
FilePath = $FilePath
FileName = $FileName
LinuxPaths = $linuxPaths
}
} else {
Write-Host " [OK] $FileName - No Linux paths found" -ForegroundColor Green
return $null
}
}
catch {
Write-Host " [ERROR] $FileName - Failed to parse: $_" -ForegroundColor Red
return $null
}
}
# Check configuration files
Write-Host "Checking configuration files..." -ForegroundColor Cyan
Write-Host ""
$systemXmlPath = Join-Path $ConfigPath "system.xml"
$result = Test-LinuxPathsInXml -FilePath $systemXmlPath -FileName "system.xml"
if ($result) {
$issuesFound += $result
foreach ($path in $result.LinuxPaths) {
Write-Host " - $($path.Element): $($path.CurrentValue)" -ForegroundColor Red
}
}
$encodingXmlPath = Join-Path $ConfigPath "encoding.xml"
$result = Test-LinuxPathsInXml -FilePath $encodingXmlPath -FileName "encoding.xml"
if ($result) {
$issuesFound += $result
foreach ($path in $result.LinuxPaths) {
Write-Host " - $($path.Element): $($path.CurrentValue)" -ForegroundColor Red
}
}
$databaseXmlPath = Join-Path $ConfigPath "database.xml"
$result = Test-LinuxPathsInXml -FilePath $databaseXmlPath -FileName "database.xml"
if ($result) {
$issuesFound += $result
foreach ($path in $result.LinuxPaths) {
Write-Host " - $($path.Element): $($path.CurrentValue)" -ForegroundColor Red
}
}
Write-Host ""
Write-Host "=================================================" -ForegroundColor Cyan
# Summary
if ($issuesFound.Count -eq 0) {
Write-Host "No issues found! All paths are correct." -ForegroundColor Green
exit 0
}
Write-Host "Found issues in $($issuesFound.Count) file(s)" -ForegroundColor Yellow
Write-Host ""
# If DryRun, just show what would be done
if ($DryRun) {
Write-Host "DRY RUN MODE - No changes will be made" -ForegroundColor Cyan
Write-Host ""
Write-Host "The following changes would be made:" -ForegroundColor Yellow
foreach ($issue in $issuesFound) {
Write-Host ""
Write-Host "File: $($issue.FileName)" -ForegroundColor Cyan
foreach ($path in $issue.LinuxPaths) {
Write-Host " $($path.Element):" -ForegroundColor White
Write-Host " FROM: $($path.CurrentValue)" -ForegroundColor Red
if ($path.Element -in @("MetadataPath", "TranscodingTempPath")) {
Write-Host " TO: (empty - use default)" -ForegroundColor Green
} else {
$newPath = $path.CurrentValue -replace '^/var/lib/jellyfin', $DataPath -replace '/', '\'
Write-Host " TO: $newPath" -ForegroundColor Green
}
}
}
Write-Host ""
Write-Host "To apply these changes, run the script without -DryRun" -ForegroundColor Yellow
exit 0
}
# Ask for confirmation
if (-not $Force) {
Write-Host ""
Write-Host "WARNING: This will modify your configuration files!" -ForegroundColor Yellow
Write-Host "Backups will be created automatically." -ForegroundColor Yellow
Write-Host ""
$confirm = Read-Host "Do you want to proceed? (yes/no)"
if ($confirm -ne "yes") {
Write-Host "Operation cancelled." -ForegroundColor Red
exit 0
}
}
# Apply fixes
Write-Host ""
Write-Host "Applying fixes..." -ForegroundColor Cyan
Write-Host ""
foreach ($issue in $issuesFound) {
Write-Host "Fixing $($issue.FileName)..." -ForegroundColor Yellow
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$backupPath = "$($issue.FilePath).backup.$timestamp"
Copy-Item $issue.FilePath $backupPath
Write-Host " Backup created: $backupPath" -ForegroundColor Gray
[xml]$xml = Get-Content $issue.FilePath
foreach ($path in $issue.LinuxPaths) {
$element = $null
$xml.SelectNodes("//*") | ForEach-Object {
if ($_.InnerText -eq $path.CurrentValue) {
$element = $_
}
}
if ($element) {
if ($path.Element -in @("MetadataPath", "TranscodingTempPath")) {
$element.InnerText = ""
Write-Host " Cleared $($path.Element)" -ForegroundColor Green
} else {
$newPath = $path.CurrentValue -replace '^/var/lib/jellyfin', $DataPath -replace '/', '\'
$element.InnerText = $newPath
Write-Host " Updated $($path.Element) to: $newPath" -ForegroundColor Green
}
}
}
$xml.Save($issue.FilePath)
Write-Host " Saved $($issue.FileName)" -ForegroundColor Green
Write-Host ""
}
Write-Host "=================================================" -ForegroundColor Cyan
Write-Host "All fixes applied successfully!" -ForegroundColor Green
Write-Host ""
Write-Host "NEXT STEPS:" -ForegroundColor Yellow
Write-Host "1. Restart Jellyfin service/application" -ForegroundColor White
Write-Host "2. Check logs to verify paths are now correct" -ForegroundColor White
$configInfo = "3. If issues persist, review backups in: $ConfigPath"
Write-Host $configInfo -ForegroundColor White
Write-Host ""
@@ -0,0 +1,83 @@
# Fix startup.json WebDir paths
# This script finds and fixes incorrect WebDir paths in startup.json files
Write-Host "=================================================" -ForegroundColor Cyan
Write-Host "Jellyfin startup.json WebDir Fixer" -ForegroundColor Cyan
Write-Host "=================================================" -ForegroundColor Cyan
Write-Host ""
$searchPaths = @(
".",
".\bin\Debug\net11.0",
".\bin\Release\net11.0",
".\bin\Debug\net11.0\config",
".\bin\Release\net11.0\config"
)
$filesFound = 0
$filesFixed = 0
foreach ($path in $searchPaths) {
$jsonPath = Join-Path $path "startup.json"
if (Test-Path $jsonPath) {
$filesFound++
Write-Host "Found: $jsonPath" -ForegroundColor Yellow
try {
$content = Get-Content $jsonPath -Raw
$originalContent = $content
# Check for incorrect WebDir
if ($content -match '"WebDir":\s*"[^"]*C:/ProgramData/jellyfin/wwwroot[^"]*"') {
Write-Host " Status: Has incorrect WebDir path" -ForegroundColor Red
# Create backup
$backupPath = "$jsonPath.backup.$(Get-Date -Format 'yyyyMMdd_HHmmss')"
Copy-Item $jsonPath $backupPath
Write-Host " Backup: $backupPath" -ForegroundColor Gray
# Fix the path
$content = $content -replace '"WebDir":\s*"C:/ProgramData/jellyfin/wwwroot"', '"WebDir": "wwwroot"'
# Also fix any data path if it has /data appended
$content = $content -replace '"WebDir":\s*"C:/ProgramData/jellyfin/data/wwwroot"', '"WebDir": "wwwroot"'
Set-Content -Path $jsonPath -Value $content -NoNewline
$filesFixed++
Write-Host " Result: FIXED!" -ForegroundColor Green
}
elseif ($content -match '"WebDir":\s*"wwwroot"') {
Write-Host " Status: Already correct (relative path)" -ForegroundColor Green
}
else {
Write-Host " Status: Has custom path (not changed)" -ForegroundColor Cyan
}
}
catch {
Write-Host " ERROR: Failed to process - $_" -ForegroundColor Red
}
Write-Host ""
}
}
Write-Host "=================================================" -ForegroundColor Cyan
if ($filesFound -eq 0) {
Write-Host "No startup.json files found." -ForegroundColor Yellow
Write-Host "Run Jellyfin once to create the configuration file." -ForegroundColor White
}
elseif ($filesFixed -eq 0) {
Write-Host "All files are already correct!" -ForegroundColor Green
}
else {
Write-Host "Fixed $filesFixed of $filesFound file(s)" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host "1. Rebuild your solution (if needed)" -ForegroundColor White
Write-Host "2. Run Jellyfin to verify WebDir path is correct" -ForegroundColor White
}
Write-Host ""
@@ -0,0 +1,195 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Fixes StyleCop warnings in C# files.
.DESCRIPTION
Automatically fixes common StyleCop warnings:
- SA1413: Adds trailing commas to multi-line initializers
- SA1515: Adds blank lines before single-line comments
- SA1518: Ensures files end with single newline
.PARAMETER Path
The root path to search for C# files. Defaults to "MediaBrowser.Model"
.PARAMETER DryRun
If specified, shows what would be changed without actually modifying files
.EXAMPLE
.\FixStyleCopWarnings.ps1 -DryRun
#>
param(
[string]$Path = "MediaBrowser.Model",
[switch]$DryRun
)
$ErrorActionPreference = "Stop"
function Fix-TrailingCommas {
param([string]$Content)
$modified = $false
$lines = $content -split '\r?\n'
for ($i = 0; $i -lt $lines.Length - 1; $i++) {
$currentLine = $lines[$i]
$nextLine = $lines[$i + 1]
# Check if current line ends with a property/value without comma
# and next line closes the initializer
if ($currentLine -match '^\s+\w+\s*=\s*.+[^,]\s*$' -and $nextLine -match '^\s*[}\)]') {
$lines[$i] = $currentLine.TrimEnd() + ','
$modified = $true
}
}
if ($modified) {
return ($lines -join "`n")
}
return $Content
}
function Fix-BlankLinesBeforeComments {
param([string]$Content)
$modified = $false
$lines = $content -split '\r?\n'
$newLines = @()
for ($i = 0; $i -lt $lines.Length; $i++) {
$currentLine = $lines[$i]
# Check if this is a single-line comment
if ($currentLine -match '^\s+//\s+\w') {
# Check if previous line exists and is not blank
if ($i -gt 0) {
$prevLine = $lines[$i - 1]
$prevLineIsBlank = [string]::IsNullOrWhiteSpace($prevLine)
$prevLineIsOpenBrace = $prevLine -match '\{\s*$'
$prevLineIsComment = $prevLine -match '^\s*//'
# Add blank line if previous line is not blank, not open brace, not another comment
if (-not $prevLineIsBlank -and -not $prevLineIsOpenBrace -and -not $prevLineIsComment) {
$newLines += ""
$modified = $true
}
}
}
$newLines += $currentLine
}
if ($modified) {
return ($newLines -join "`n")
}
return $Content
}
function Fix-FileEnding {
param([string]$Content)
# Ensure file ends with exactly one newline
$trimmed = $Content.TrimEnd("`r", "`n", " ", "`t")
return $trimmed + "`n"
}
function Fix-StyleCopWarnings {
param(
[string]$FilePath,
[switch]$DryRun
)
$content = Get-Content -Path $FilePath -Raw
$originalContent = $content
# Apply fixes
$content = Fix-TrailingCommas -Content $content
$content = Fix-BlankLinesBeforeComments -Content $content
$content = Fix-FileEnding -Content $content
# Restore Windows line endings if original had them
if ($originalContent -match '\r\n') {
$content = $content -replace '(?<!\r)\n', "`r`n"
}
if ($content -ne $originalContent) {
if ($DryRun) {
Write-Host " [DRY-RUN] Would fix StyleCop warnings" -ForegroundColor Cyan
return $true
}
[System.IO.File]::WriteAllText($FilePath, $content, [System.Text.UTF8Encoding]::new($false))
Write-Host " [FIXED]" -ForegroundColor Green
return $true
}
Write-Host " [SKIP] No warnings to fix" -ForegroundColor Gray
return $false
}
# Main script
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "StyleCop Warning Fixer" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host
if ($DryRun) {
Write-Host "DRY RUN MODE - No files will be modified" -ForegroundColor Yellow
Write-Host
}
if (-not (Test-Path $Path)) {
Write-Error "Path not found: $Path"
exit 1
}
$files = Get-ChildItem -Path $Path -Filter "*.cs" -Recurse | Where-Object {
$_.FullName -notmatch "\\obj\\" -and
$_.FullName -notmatch "\\bin\\"
}
Write-Host "Found $($files.Count) C# files in $Path" -ForegroundColor Cyan
Write-Host
$fixedCount = 0
$skippedCount = 0
$errorCount = 0
foreach ($file in $files) {
$relativePath = $file.FullName.Replace((Get-Location).Path, "").TrimStart([char]92, [char]47)
Write-Host "[Processing] $relativePath" -ForegroundColor White
try {
$fixed = Fix-StyleCopWarnings -FilePath $file.FullName -DryRun:$DryRun
if ($fixed) {
$fixedCount++
} else {
$skippedCount++
}
}
catch {
Write-Host " [ERROR] $_" -ForegroundColor Red
$errorCount++
}
}
Write-Host
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "Summary:" -ForegroundColor Cyan
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " Total files: $($files.Count)" -ForegroundColor White
Write-Host " Fixed: $fixedCount" -ForegroundColor Green
Write-Host " Skipped: $skippedCount" -ForegroundColor Gray
Write-Host " Errors: $errorCount" -ForegroundColor Red
if ($DryRun) {
Write-Host
Write-Host "This was a DRY RUN. Run without -DryRun to apply changes." -ForegroundColor Yellow
}
Write-Host ("=" * 80) -ForegroundColor Cyan
exit $errorCount
@@ -0,0 +1,237 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Sets up automated weekly performance monitoring via Windows Task Scheduler
.DESCRIPTION
Creates a scheduled task to run Weekly-Performance-Monitor.ps1 every Monday at 6 AM.
Can optionally enable conditional VACUUM ANALYZE based on dead tuple thresholds.
.PARAMETER Uninstall
Remove the scheduled task
.PARAMETER TaskTime
Time to run the task (default: 6am)
.PARAMETER DayOfWeek
Day of week to run (default: Monday)
.PARAMETER EnableAutoVacuumAnalyze
Enable conditional VACUUM ANALYZE for tables above dead tuple thresholds
.PARAMETER DeadTupleThreshold
Minimum dead tuples before VACUUM ANALYZE runs (default: 10000)
.PARAMETER DeadTuplePercentThreshold
Minimum dead tuple percentage before VACUUM ANALYZE runs (default: 20)
.EXAMPLE
.\Setup-Weekly-Monitor.ps1
.EXAMPLE
.\Setup-Weekly-Monitor.ps1 -TaskTime "3am" -DayOfWeek Sunday
.EXAMPLE
.\Setup-Weekly-Monitor.ps1 -EnableAutoVacuumAnalyze -DeadTupleThreshold 10000 -DeadTuplePercentThreshold 20
.EXAMPLE
.\Setup-Weekly-Monitor.ps1 -Uninstall
#>
[CmdletBinding()]
param (
[Parameter()]
[switch]$Uninstall,
[Parameter()]
[string]$TaskTime = "6am",
[Parameter()]
[ValidateSet('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')]
[string]$DayOfWeek = 'Monday',
[Parameter()]
[switch]$EnableAutoVacuumAnalyze,
[Parameter()]
[ValidateRange(1, [int]::MaxValue)]
[int]$DeadTupleThreshold = 10000,
[Parameter()]
[ValidateRange(0.01, 100.0)]
[double]$DeadTuplePercentThreshold = 20.0
)
$ErrorActionPreference = "Stop"
$TaskName = "Jellyfin Database Weekly Monitor"
$ScriptRoot = Split-Path -Parent $PSCommandPath
$MonitorScript = Join-Path $ScriptRoot "Weekly-Performance-Monitor.ps1"
# ============================================================================
# Functions
# ============================================================================
function Write-ColorOutput {
param(
[string]$Message,
[ConsoleColor]$Color = 'White'
)
Write-Host $Message -ForegroundColor $Color
}
function Test-IsAdmin {
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
return $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
# ============================================================================
# Main
# ============================================================================
Write-ColorOutput "`n========================================" -Color Cyan
Write-ColorOutput "Jellyfin DB Monitor Setup" -Color Cyan
Write-ColorOutput "========================================`n" -Color Cyan
# Check if running as administrator
if (-not (Test-IsAdmin)) {
Write-ColorOutput "❌ This script must be run as Administrator" -Color Red
Write-ColorOutput " Right-click PowerShell and select 'Run as Administrator'" -Color Yellow
exit 1
}
# Uninstall if requested
if ($Uninstall) {
Write-ColorOutput "Removing scheduled task..." -Color Yellow
$task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($task) {
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
Write-ColorOutput "✅ Scheduled task removed successfully" -Color Green
} else {
Write-ColorOutput "⚠️ Scheduled task not found" -Color Yellow
}
exit 0
}
# Check if monitor script exists
if (-not (Test-Path $MonitorScript)) {
Write-ColorOutput "❌ Monitor script not found: $MonitorScript" -Color Red
exit 1
}
Write-ColorOutput "Configuration:" -Color Cyan
Write-ColorOutput " Task Name: $TaskName" -Color White
Write-ColorOutput " Script: $MonitorScript" -Color White
Write-ColorOutput " Schedule: Every $DayOfWeek at $TaskTime" -Color White
if ($EnableAutoVacuumAnalyze) {
Write-ColorOutput " Auto VACUUM ANALYZE: Enabled (dead tuples >= $DeadTupleThreshold, dead percent >= $DeadTuplePercentThreshold)" -Color White
} else {
Write-ColorOutput " Auto VACUUM ANALYZE: Disabled" -Color White
}
Write-ColorOutput ""
# Remove existing task if present
$existingTask = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-ColorOutput "⚠️ Existing task found. Removing..." -Color Yellow
Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false
}
# Create scheduled task action
Write-ColorOutput "Creating scheduled task..." -Color Cyan
$scriptArguments = "-NoProfile -ExecutionPolicy Bypass -File `"$MonitorScript`""
if ($EnableAutoVacuumAnalyze) {
$scriptArguments += " -AutoVacuumAnalyze -DeadTupleThreshold $DeadTupleThreshold -DeadTuplePercentThreshold $DeadTuplePercentThreshold"
}
$action = New-ScheduledTaskAction `
-Execute "PowerShell.exe" `
-Argument $scriptArguments
# Create trigger
$trigger = New-ScheduledTaskTrigger `
-Weekly `
-DaysOfWeek $DayOfWeek `
-At $TaskTime
# Task settings
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-RunOnlyIfNetworkAvailable `
-ExecutionTimeLimit (New-TimeSpan -Hours 2)
# Task principal (run as current user)
$principal = New-ScheduledTaskPrincipal `
-UserId $env:USERNAME `
-LogonType S4U `
-RunLevel Highest
# Register the task
try {
Register-ScheduledTask `
-TaskName $TaskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description "Automated weekly performance monitoring for Jellyfin PostgreSQL database" | Out-Null
Write-ColorOutput "✅ Scheduled task created successfully!" -Color Green
Write-ColorOutput ""
# Verify task
$task = Get-ScheduledTask -TaskName $TaskName
$nextRun = $task.Triggers[0].StartBoundary
Write-ColorOutput "Task Details:" -Color Cyan
Write-ColorOutput " Status: $($task.State)" -Color White
Write-ColorOutput " Next Run: $nextRun" -Color White
Write-ColorOutput ""
# Test run option
Write-ColorOutput "Would you like to test the task now? (y/n): " -Color Yellow -NoNewline
$response = Read-Host
if ($response -eq 'y' -or $response -eq 'Y') {
Write-ColorOutput "`nRunning test..." -Color Cyan
Start-ScheduledTask -TaskName $TaskName
Start-Sleep -Seconds 2
$taskInfo = Get-ScheduledTask -TaskName $TaskName | Get-ScheduledTaskInfo
Write-ColorOutput "Last Run Time: $($taskInfo.LastRunTime)" -Color White
Write-ColorOutput "Last Result: $($taskInfo.LastTaskResult)" -Color White
if ($taskInfo.LastTaskResult -eq 0) {
Write-ColorOutput "✅ Test run completed successfully!" -Color Green
} else {
Write-ColorOutput "⚠️ Test run completed with code: $($taskInfo.LastTaskResult)" -Color Yellow
}
}
} catch {
Write-ColorOutput "❌ Failed to create scheduled task: $_" -Color Red
exit 1
}
Write-ColorOutput "`n========================================" -Color Green
Write-ColorOutput "Setup Complete!" -Color Green
Write-ColorOutput "========================================" -Color Green
Write-ColorOutput ""
Write-ColorOutput "Monitoring is now automated!" -Color Cyan
Write-ColorOutput ""
Write-ColorOutput "To manage the task:" -Color White
Write-ColorOutput " View: Get-ScheduledTask -TaskName '$TaskName'" -Color Gray
Write-ColorOutput " Run now: Start-ScheduledTask -TaskName '$TaskName'" -Color Gray
Write-ColorOutput " Disable: Disable-ScheduledTask -TaskName '$TaskName'" -Color Gray
Write-ColorOutput " Remove: .\Setup-Weekly-Monitor.ps1 -Uninstall" -Color Gray
Write-ColorOutput ""
Write-ColorOutput "Reports will be saved to: reports\weekly" -Color Yellow
Write-ColorOutput "========================================`n" -Color Green
@@ -0,0 +1,483 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Automated weekly performance monitoring for Jellyfin PostgreSQL database
.DESCRIPTION
This script:
1. Runs comprehensive diagnostics
2. Analyzes slow queries
3. Tracks index usage over time
4. Generates weekly performance reports
5. Optionally runs VACUUM ANALYZE on tables with high dead tuples
6. Can be scheduled via Task Scheduler
.PARAMETER OutputDirectory
Directory to save reports (default: reports/weekly)
.PARAMETER EmailReport
If specified, emails the report (requires email configuration)
.PARAMETER CompareWithPrevious
Compare current stats with previous week
.EXAMPLE
.\Weekly-Performance-Monitor.ps1
.EXAMPLE
.\Weekly-Performance-Monitor.ps1 -OutputDirectory "C:\Reports\Jellyfin" -CompareWithPrevious
.EXAMPLE
.\Weekly-Performance-Monitor.ps1 -AutoVacuumAnalyze -DeadTupleThreshold 10000 -DeadTuplePercentThreshold 20
.EXAMPLE
# Schedule weekly via Task Scheduler
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Path\To\Weekly-Performance-Monitor.ps1"
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 6am
Register-ScheduledTask -TaskName "Jellyfin DB Monitor" -Action $action -Trigger $trigger
.NOTES
Author: Generated for Jellyfin PostgreSQL Project
Date: 2026-03-05
#>
[CmdletBinding()]
param (
[Parameter()]
[string]$OutputDirectory = "reports\weekly",
[Parameter()]
[switch]$EmailReport,
[Parameter()]
[switch]$CompareWithPrevious,
[Parameter()]
[switch]$AutoVacuumAnalyze,
[Parameter()]
[ValidateRange(1, [int]::MaxValue)]
[int]$DeadTupleThreshold = 100,
[Parameter()]
[ValidateRange(0.01, 100.0)]
[double]$DeadTuplePercentThreshold = 0.02
)
# ============================================================================
# Configuration
# ============================================================================
$ErrorActionPreference = "Stop"
$ScriptRoot = Split-Path -Parent $PSCommandPath
$ProjectRoot = Split-Path -Parent $ScriptRoot
# Load database configuration
. "$ProjectRoot\scripts\db-config.ps1"
# ============================================================================
# Functions
# ============================================================================
function Write-ColorOutput {
param(
[string]$Message,
[ConsoleColor]$Color = 'White'
)
Write-Host $Message -ForegroundColor $Color
}
function Get-ReportTimestamp {
return Get-Date -Format "yyyy-MM-dd_HHmmss"
}
function Get-WeekNumber {
$date = Get-Date
$culture = [System.Globalization.CultureInfo]::CurrentCulture
return $culture.Calendar.GetWeekOfYear($date,
[System.Globalization.CalendarWeekRule]::FirstDay,
[DayOfWeek]::Monday)
}
function Test-DatabaseConnection {
Write-ColorOutput "`nTesting database connection..." -Color Cyan
try {
$result = & $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "SELECT version();" 2>&1
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "[OK] Database connection successful" -Color Green
return $true
} else {
Write-ColorOutput "[ERROR] Database connection failed: $result" -Color Red
return $false
}
} catch {
Write-ColorOutput "[ERROR] Database connection error: $_" -Color Red
return $false
}
}
function Invoke-DiagnosticReport {
param([string]$OutputFile)
Write-ColorOutput "`nRunning comprehensive diagnostics..." -Color Cyan
try {
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$ProjectRoot\sql\diagnostics.sql" > $OutputFile 2>&1
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "[OK] Diagnostics completed: $OutputFile" -Color Green
return $true
} else {
Write-ColorOutput "[WARN] Diagnostics completed with warnings" -Color Yellow
return $true
}
} catch {
Write-ColorOutput "[ERROR] Diagnostics failed: $_" -Color Red
return $false
}
}
function Invoke-QueryAnalysis {
param([string]$OutputFile)
Write-ColorOutput "`nAnalyzing slow queries..." -Color Cyan
try {
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$ProjectRoot\sql\query-analysis.sql" > $OutputFile 2>&1
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "[OK] Query analysis completed: $OutputFile" -Color Green
return $true
} else {
Write-ColorOutput "[WARN] Query analysis completed with warnings" -Color Yellow
return $true
}
} catch {
Write-ColorOutput "[ERROR] Query analysis failed: $_" -Color Red
return $false
}
}
function Get-IndexUsageStats {
param([string]$OutputFile)
Write-ColorOutput "`nGathering index usage statistics..." -Color Cyan
$query = @"
SELECT
schemaname,
relname AS tablename,
indexrelname,
idx_scan,
idx_tup_read,
idx_tup_fetch,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
ORDER BY schemaname, relname, idx_scan DESC;
"@
try {
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c $query > $OutputFile 2>&1
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "[OK] Index stats gathered: $OutputFile" -Color Green
return $true
} else {
Write-ColorOutput "[WARN] Index stats completed with warnings" -Color Yellow
return $true
}
} catch {
Write-ColorOutput "[ERROR] Index stats failed: $_" -Color Red
return $false
}
}
function Get-TableSizeStats {
param([string]$OutputFile)
Write-ColorOutput "`nGathering table size statistics..." -Color Cyan
$query = @"
SELECT
schemaname,
relname,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) AS total_size,
pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) AS table_size,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname) - pg_relation_size(schemaname||'.'||relname)) AS index_size,
n_live_tup AS row_count,
n_dead_tup AS dead_rows,
ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
ORDER BY pg_total_relation_size(schemaname||'.'||relname) DESC;
"@
try {
& $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c $query > $OutputFile 2>&1
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "[OK] Table size stats gathered: $OutputFile" -Color Green
return $true
} else {
Write-ColorOutput "[WARN] Table size stats completed with warnings" -Color Yellow
return $true
}
} catch {
Write-ColorOutput "[ERROR] Table size stats failed: $_" -Color Red
return $false
}
}
function Invoke-ConditionalVacuumAnalyze {
param(
[int]$MinDeadTuples,
[double]$MinDeadPercent
)
Write-ColorOutput "`nChecking for tables that need VACUUM ANALYZE..." -Color Cyan
Write-ColorOutput "Thresholds: dead tuples >= $MinDeadTuples and dead percent >= $MinDeadPercent" -Color Gray
$tableQuery = @"
SELECT
format('%I.%I', schemaname, relname) AS qualified_table,
n_dead_tup,
ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
AND n_live_tup > 0
AND n_dead_tup >= $MinDeadTuples
AND (100.0 * n_dead_tup / NULLIF(n_live_tup, 0)) >= $MinDeadPercent
ORDER BY n_dead_tup DESC;
"@
try {
$tables = & $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -t -A -F "|" -c $tableQuery 2>&1
if ($LASTEXITCODE -ne 0) {
Write-ColorOutput "[ERROR] Failed to evaluate dead tuple thresholds: $tables" -Color Red
return $false
}
$tableLines = @($tables | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
if ($tableLines.Count -eq 0) {
Write-ColorOutput "[OK] No tables exceeded dead tuple thresholds" -Color Green
return $true
}
Write-ColorOutput "Found $($tableLines.Count) table(s) requiring VACUUM ANALYZE" -Color Yellow
foreach ($line in $tableLines) {
$parts = $line -split "\|", 3
$qualifiedTable = $parts[0].Trim()
$deadTuples = if ($parts.Count -ge 2) { $parts[1].Trim() } else { "unknown" }
$deadPercent = if ($parts.Count -ge 3) { $parts[2].Trim() } else { "unknown" }
Write-ColorOutput "Running VACUUM ANALYZE on $qualifiedTable (dead tuples: $deadTuples, dead percent: $deadPercent)" -Color Cyan
$vacuumResult = & $PSQL_PATH -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "VACUUM (ANALYZE, VERBOSE) $qualifiedTable;" 2>&1
if ($LASTEXITCODE -ne 0) {
Write-ColorOutput "[ERROR] VACUUM ANALYZE failed for $qualifiedTable : $vacuumResult" -Color Red
return $false
}
Write-ColorOutput "[OK] VACUUM ANALYZE completed for $qualifiedTable" -Color Green
}
return $true
} catch {
Write-ColorOutput "[ERROR] Conditional VACUUM ANALYZE failed: $_" -Color Red
return $false
}
}
function Compare-WithPrevious {
param(
[string]$CurrentReport,
[string]$OutputDirectory
)
Write-ColorOutput "`nComparing with previous week's report..." -Color Cyan
# Find most recent previous report
$previousReports = Get-ChildItem "$OutputDirectory\diagnostics_*.txt" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne (Split-Path $CurrentReport -Leaf) } |
Sort-Object LastWriteTime -Descending
if ($previousReports.Count -eq 0) {
Write-ColorOutput "[WARN] No previous reports found for comparison" -Color Yellow
return
}
$previousReport = $previousReports[0].FullName
Write-ColorOutput "Comparing with: $($previousReports[0].Name)" -Color Cyan
# TODO: Add detailed comparison logic
# For now, just note that both reports exist
Write-ColorOutput "[OK] Comparison data available" -Color Green
Write-ColorOutput " Current: $CurrentReport" -Color Gray
Write-ColorOutput " Previous: $previousReport" -Color Gray
}
function New-SummaryReport {
param(
[string]$OutputDirectory,
[string]$Timestamp
)
Write-ColorOutput "`nGenerating summary report..." -Color Cyan
$summaryFile = Join-Path $OutputDirectory "summary_$Timestamp.txt"
$summary = @"
================================================================================
JELLYFIN DATABASE WEEKLY PERFORMANCE SUMMARY
================================================================================
Report Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Database: $DB_NAME at $DB_HOST
Week Number: $(Get-WeekNumber)
================================================================================
REPORTS GENERATED:
* diagnostics_$Timestamp.txt : Full diagnostic report
* query_analysis_$Timestamp.txt : Slow query analysis
* index_usage_$Timestamp.txt : Index usage statistics
* table_sizes_$Timestamp.txt : Table size and bloat info
* summary_$Timestamp.txt : This summary
================================================================================
QUICK HEALTH CHECK:
================================================================================
To view critical issues check diagnostics file for:
* Section 5: TABLES WITH HIGH SEQUENTIAL SCANS
* Section 8: UNUSED OR RARELY USED INDEXES
* Section 10: SLOWEST QUERIES
To identify slow queries check query_analysis file for:
* Section 2: TOP 20 SLOWEST QUERIES (by max execution time)
* Section 5: BASEITEMS TABLE QUERIES ANALYSIS
================================================================================
RECOMMENDED ACTIONS:
================================================================================
* Review slow queries taking longer than 10 seconds
* Check if new indexes are being utilized
* Look for tables with many sequential scans
* Monitor cache hit ratio (should be above 95 percent)
* Check for tables with high dead tuples percentage (need VACUUM)
================================================================================
AUTOMATION SETUP:
================================================================================
To schedule this script weekly via Task Scheduler run Setup-Weekly-Monitor.ps1
as Administrator. This will create a scheduled task to run every Monday at 6am.
To disable the scheduled task run Setup-Weekly-Monitor.ps1 with -Uninstall flag.
================================================================================
NEXT REPORT: $(Get-Date).AddDays(7).ToString("yyyy-MM-dd")
================================================================================
"@
$summary | Out-File -FilePath $summaryFile -Encoding UTF8
Write-ColorOutput "[OK] Summary generated: $summaryFile" -Color Green
}
# ============================================================================
# Main Execution
# ============================================================================
Write-ColorOutput "`n========================================" -Color Cyan
Write-ColorOutput "Jellyfin Database Weekly Monitor" -Color Cyan
Write-ColorOutput "========================================" -Color Cyan
Write-ColorOutput "Database: $DB_NAME" -Color Yellow
Write-ColorOutput "Host: $DB_HOST" -Color Yellow
Write-ColorOutput "Week: $(Get-WeekNumber)" -Color Yellow
Write-ColorOutput "Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -Color Yellow
Write-ColorOutput "========================================`n" -Color Cyan
# Create output directory
$reportDir = Join-Path $ProjectRoot $OutputDirectory
if (-not (Test-Path $reportDir)) {
New-Item -ItemType Directory -Path $reportDir -Force | Out-Null
Write-ColorOutput "Created report directory: $reportDir" -Color Green
}
# Generate timestamp for this run
$timestamp = Get-ReportTimestamp
# Test connection
if (-not (Test-DatabaseConnection)) {
Write-ColorOutput "`n[ERROR] Cannot connect to database. Exiting." -Color Red
exit 1
}
# Run diagnostics
$diagnosticsFile = Join-Path $reportDir "diagnostics_$timestamp.txt"
$success = Invoke-DiagnosticReport -OutputFile $diagnosticsFile
if (-not $success) {
Write-ColorOutput "`n[WARN] Some reports failed. Check output directory." -Color Yellow
}
# Run query analysis
$queryAnalysisFile = Join-Path $reportDir "query_analysis_$timestamp.txt"
Invoke-QueryAnalysis -OutputFile $queryAnalysisFile | Out-Null
# Gather index usage stats
$indexUsageFile = Join-Path $reportDir "index_usage_$timestamp.txt"
Get-IndexUsageStats -OutputFile $indexUsageFile | Out-Null
# Gather table size stats
$tableSizesFile = Join-Path $reportDir "table_sizes_$timestamp.txt"
Get-TableSizeStats -OutputFile $tableSizesFile | Out-Null
# Optionally vacuum/analyze tables that exceed dead tuple thresholds
if ($AutoVacuumAnalyze) {
$vacuumSuccess = Invoke-ConditionalVacuumAnalyze `
-MinDeadTuples $DeadTupleThreshold `
-MinDeadPercent $DeadTuplePercentThreshold
if (-not $vacuumSuccess) {
Write-ColorOutput "[WARN] Conditional VACUUM ANALYZE encountered errors" -Color Yellow
}
}
# Compare with previous week if requested
if ($CompareWithPrevious) {
Compare-WithPrevious -CurrentReport $diagnosticsFile -OutputDirectory $reportDir
}
# Generate summary
New-SummaryReport -OutputDirectory $reportDir -Timestamp $timestamp
# Cleanup old reports (keep last 12 weeks)
Write-ColorOutput "`nCleaning up old reports (keeping last 12 weeks)..." -Color Cyan
Get-ChildItem $reportDir -File |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-84) } |
Remove-Item -Force
Write-ColorOutput "[OK] Cleanup complete" -Color Green
Write-ColorOutput "`n========================================" -Color Green
Write-ColorOutput "[OK] WEEKLY MONITORING COMPLETE" -Color Green
Write-ColorOutput "========================================" -Color Green
Write-ColorOutput "Reports saved to: $reportDir" -Color Yellow
Write-ColorOutput "Summary: summary_$timestamp.txt" -Color Yellow
Write-ColorOutput "`nNext steps:" -Color Cyan
Write-ColorOutput "1. Review summary_$timestamp.txt" -Color White
Write-ColorOutput "2. Check for critical issues in diagnostics report" -Color White
Write-ColorOutput "3. Analyze slow queries in query_analysis report" -Color White
Write-ColorOutput "========================================`n" -Color Green
# Email report if requested (placeholder)
if ($EmailReport) {
Write-ColorOutput "[WARN] Email functionality not yet implemented" -Color Yellow
Write-ColorOutput " Configure Send-MailMessage with your SMTP settings" -Color Gray
}
exit 0
@@ -0,0 +1,162 @@
# Build Jellyfin Installer
# This script builds the Jellyfin installer using Inno Setup
param(
[Parameter(Mandatory=$false)]
[string]$Version = "11.0.0-PostgreSQL-PREVIEW",
[Parameter(Mandatory=$false)]
[ValidateSet("Release", "Debug")]
[string]$Configuration = "Release",
[Parameter(Mandatory=$false)]
[switch]$SkipBuild
)
Write-Host "`n╔══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ Jellyfin PostgreSQL Installer Builder ║" -ForegroundColor Cyan
Write-Host "╚══════════════════════════════════════════════════════════╝`n" -ForegroundColor Cyan
$ErrorActionPreference = "Stop"
$ScriptDir = $PSScriptRoot
$SolutionRoot = "E:\Projects\pgsql-jellyfin"
$InnoSetupPath = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
# Check prerequisites
Write-Host "Checking prerequisites..." -ForegroundColor Yellow
# Check if Inno Setup is installed
if (-not (Test-Path $InnoSetupPath)) {
Write-Host "❌ Inno Setup 6 not found!" -ForegroundColor Red
Write-Host "Please install it from: https://jrsoftware.org/isinfo.php" -ForegroundColor Yellow
Write-Host "Or run: winget install JRSoftware.InnoSetup" -ForegroundColor Yellow
exit 1
}
Write-Host "✅ Inno Setup found" -ForegroundColor Green
# Check if lib folder exists
if (-not (Test-Path "$SolutionRoot\lib\$Configuration\net11.0")) {
if ($SkipBuild) {
Write-Host "❌ Build folder not found: $SolutionRoot\lib\$Configuration\net11.0" -ForegroundColor Red
exit 1
}
Write-Host "⚠️ Build folder not found, building solution first..." -ForegroundColor Yellow
}
# Build the solution if needed
if (-not $SkipBuild) {
Write-Host "`nBuilding Jellyfin solution..." -ForegroundColor Cyan
Push-Location $SolutionRoot
try {
# Clean previous build
Write-Host "Cleaning previous build..." -ForegroundColor Gray
dotnet clean --configuration $Configuration | Out-Null
# Build solution
Write-Host "Building $Configuration configuration..." -ForegroundColor Gray
$buildOutput = dotnet build Jellyfin.Server --configuration $Configuration 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Build failed!" -ForegroundColor Red
Write-Host $buildOutput
exit 1
}
Write-Host "✅ Build successful" -ForegroundColor Green
# Count DLLs
$dllCount = (Get-ChildItem "$SolutionRoot\lib\$Configuration\net11.0" -Filter "*.dll" -Recurse).Count
Write-Host "📊 Built $dllCount DLL files" -ForegroundColor Gray
}
finally {
Pop-Location
}
}
# Check if installer script exists
$InnoScriptPath = "$SolutionRoot\jellyfin-setup.iss"
if (-not (Test-Path $InnoScriptPath)) {
Write-Host "❌ Installer script not found: $InnoScriptPath" -ForegroundColor Red
Write-Host "Please ensure jellyfin-setup.iss is in the solution root" -ForegroundColor Yellow
exit 1
}
# Check for icon file (optional)
$IconPath = "$SolutionRoot\jellyfin.ico"
if (-not (Test-Path $IconPath)) {
Write-Host "⚠️ Icon file not found: $IconPath" -ForegroundColor Yellow
Write-Host "The installer will use the default Inno Setup icon" -ForegroundColor Gray
}
# Update version in script if needed
Write-Host "`nPreparing installer script..." -ForegroundColor Cyan
$scriptContent = Get-Content $InnoScriptPath -Raw
$scriptContent = $scriptContent -replace 'AppVersion=[\d\.]+', "AppVersion=$Version"
$scriptContent = $scriptContent -replace 'OutputBaseFilename=JellyfinSetup-PostgreSQL-[\d\.]+', "OutputBaseFilename=JellyfinSetup-PostgreSQL-$Version"
Set-Content $InnoScriptPath $scriptContent -NoNewline
Write-Host "✅ Version updated to $Version" -ForegroundColor Green
# Create output directory
$OutputDir = "$SolutionRoot\installer-output"
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
# Build the installer
Write-Host "`nBuilding installer..." -ForegroundColor Cyan
Write-Host "Script: $InnoScriptPath" -ForegroundColor Gray
Write-Host "Output: $OutputDir" -ForegroundColor Gray
Push-Location $SolutionRoot
try {
$compileOutput = & $InnoSetupPath $InnoScriptPath 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ Installer build failed!" -ForegroundColor Red
Write-Host $compileOutput
exit 1
}
Write-Host "✅ Installer built successfully!" -ForegroundColor Green
}
finally {
Pop-Location
}
# Find the installer file
$installerFile = Get-ChildItem $OutputDir -Filter "JellyfinSetup-PostgreSQL-*.exe" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($installerFile) {
$fileSize = [math]::Round($installerFile.Length / 1MB, 2)
Write-Host "`n╔══════════════════════════════════════════════════════════╗" -ForegroundColor Green
Write-Host "║ Installer Build Complete! ✅ ║" -ForegroundColor Green
Write-Host "╚══════════════════════════════════════════════════════════╝`n" -ForegroundColor Green
Write-Host "📦 Installer Details:" -ForegroundColor Cyan
Write-Host " File: $($installerFile.Name)" -ForegroundColor White
Write-Host " Path: $($installerFile.FullName)" -ForegroundColor White
Write-Host " Size: $fileSize MB" -ForegroundColor White
Write-Host " Date: $($installerFile.LastWriteTime)" -ForegroundColor White
Write-Host "`n📋 Next Steps:" -ForegroundColor Cyan
Write-Host " 1. Test the installer on a clean Windows system" -ForegroundColor Gray
Write-Host " 2. Sign the installer (optional): signtool sign /f cert.pfx installer.exe" -ForegroundColor Gray
Write-Host " 3. Upload to GitHub releases or your distribution server" -ForegroundColor Gray
Write-Host "`n🧪 Test Commands:" -ForegroundColor Cyan
Write-Host " # Silent install" -ForegroundColor Gray
Write-Host " $($installerFile.FullName) /VERYSILENT /LOG=install.log" -ForegroundColor DarkGray
Write-Host " # Normal install" -ForegroundColor Gray
Write-Host " $($installerFile.FullName)" -ForegroundColor DarkGray
# Open output folder
Write-Host "`nOpening output folder..." -ForegroundColor Cyan
Start-Process $OutputDir
}
else {
Write-Host "❌ Installer file not found in output directory!" -ForegroundColor Red
exit 1
}
Write-Host "`n✅ All done!`n" -ForegroundColor Green
@@ -0,0 +1,22 @@
# copy-sql-files.ps1
# Run this after publish to copy SQL files
param(
[string]$PublishDir = "Jellyfin.Server\bin\Release\net11.0\publish"
)
Write-Host "Copying SQL files to $PublishDir..." -ForegroundColor Cyan
# Create sql directory
New-Item -ItemType Directory -Path "$PublishDir\sql" -Force | Out-Null
New-Item -ItemType Directory -Path "$PublishDir\sql\schema_init" -Force | Out-Null
# Copy SQL files
Copy-Item "Jellyfin.Server\sql\*.sql" "$PublishDir\sql\" -Force
Copy-Item "Jellyfin.Server\sql\schema_init\*.sql" "$PublishDir\sql\schema_init\" -Force -ErrorAction SilentlyContinue
Write-Host "✓ SQL files copied successfully!" -ForegroundColor Green
# List copied files
Write-Host "`nCopied files:" -ForegroundColor Yellow
Get-ChildItem "$PublishDir\sql" -Recurse -File | Select-Object FullName
@@ -0,0 +1,57 @@
# Database Configuration
# Edit this file to change which database commands run against
# Database connection settings
$PSQL_PATH = "C:\Program Files\PostgreSQL\18\bin\psql.exe"
$DB_USER = "jellyfin"
$DB_NAME = "jellyfin_test2" # ← Change this to switch databases
$DB_HOST = "192.168.129.253"
$DB_PORT = "6432"
# Export for use in other scripts
$global:PSQL_PATH = $PSQL_PATH
$global:DB_USER = $DB_USER
$global:DB_NAME = $DB_NAME
$global:DB_HOST = $DB_HOST
$global:DB_PORT = $DB_PORT
# Helper function to run psql commands
function Invoke-PSQL {
param(
[string]$Query,
[string]$File,
[string]$OutputFile
)
$baseCmd = "& `"$PSQL_PATH`" -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME"
if ($File) {
$cmd = "$baseCmd -f `"$File`""
} elseif ($Query) {
$cmd = "$baseCmd -c `"$Query`""
} else {
Write-Error "Must provide either -Query or -File parameter"
return
}
if ($OutputFile) {
$cmd += " > `"$OutputFile`""
}
Write-Host "Connecting to: $DB_NAME@$DB_HOST as $DB_USER" -ForegroundColor Cyan
Invoke-Expression $cmd
}
# Display current configuration
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Database Configuration Loaded" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Database: $DB_NAME" -ForegroundColor Yellow
Write-Host "User: $DB_USER" -ForegroundColor Yellow
Write-Host "Host: $DB_HOST" -ForegroundColor Yellow
Write-Host "Port: $DB_PORT" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "To change database, edit: db-config.ps1" -ForegroundColor Gray
Write-Host "Then run: . .\db-config.ps1" -ForegroundColor Gray
Write-Host ""
@@ -0,0 +1,66 @@
# Quick Database Command Runner
# Uses settings from db-config.ps1
# Load configuration
. .\db-config.ps1
Write-Host "Quick Database Commands" -ForegroundColor Cyan
Write-Host "Using database: $DB_NAME" -ForegroundColor Yellow
Write-Host ""
# Show menu
Write-Host "Choose an option:" -ForegroundColor Green
Write-Host "1. Run diagnostics (sql\diagnostics.sql)"
Write-Host "2. Check index usage"
Write-Host "3. Check if performance indexes exist"
Write-Host "4. Run all performance indexes script"
Write-Host "5. Custom query"
Write-Host "6. Change database"
Write-Host "7. Exit"
Write-Host ""
$choice = Read-Host "Enter choice (1-7)"
switch ($choice) {
"1" {
Write-Host "Running diagnostics..." -ForegroundColor Yellow
Invoke-PSQL -File "sql\diagnostics.sql" -OutputFile "diagnostics_$(Get-Date -Format 'yyyy-MM-dd_HHmmss').txt"
Write-Host "✓ Results saved to diagnostics_*.txt" -ForegroundColor Green
}
"2" {
Write-Host "Checking index usage..." -ForegroundColor Yellow
$query = "SELECT indexname, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes WHERE schemaname = 'library' AND indexname LIKE 'idx_%' ORDER BY idx_scan DESC;"
Invoke-PSQL -Query $query
}
"3" {
Write-Host "Checking performance indexes..." -ForegroundColor Yellow
$query = "SELECT COUNT(*) as perf_index_count FROM pg_indexes WHERE schemaname = 'library' AND indexname IN ('baseitems_communityrating_idx', 'baseitems_datecreated_idx', 'baseitems_datemodified_idx', 'baseitems_parentid_idx', 'baseitems_premieredate_idx', 'baseitems_productionyear_idx', 'baseitems_seriespresentationuniquekey_idx', 'baseitems_sortname_idx', 'baseitems_topparentid_idx');"
Invoke-PSQL -Query $query
}
"4" {
Write-Host "Running all performance indexes script..." -ForegroundColor Yellow
Invoke-PSQL -File "sql\all_performance_indexes.sql"
Write-Host "✓ Script complete" -ForegroundColor Green
}
"5" {
$query = Read-Host "Enter SQL query"
Invoke-PSQL -Query $query
}
"6" {
Write-Host ""
Write-Host "Current database: $DB_NAME" -ForegroundColor Yellow
Write-Host ""
Write-Host "To change database:" -ForegroundColor Cyan
Write-Host "1. Edit db-config.ps1" -ForegroundColor Gray
Write-Host "2. Change the line: `$DB_NAME = `"jellyfin_testsdata`"" -ForegroundColor Gray
Write-Host "3. Save and run: . .\db-config.ps1" -ForegroundColor Gray
Write-Host ""
}
"7" {
Write-Host "Goodbye!" -ForegroundColor Cyan
exit
}
default {
Write-Host "Invalid choice" -ForegroundColor Red
}
}

Some files were not shown because too many files have changed in this diff Show More