diff --git a/Directory.Packages.props b/Directory.Packages.props
index 5921d3c00c..7295588987 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,14 +9,14 @@
-
-
+
+
-
+
@@ -51,7 +51,7 @@
-
+
@@ -80,7 +80,7 @@
-
+
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 46c128dedc..66b7839f77 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -276,6 +276,13 @@ namespace Emby.Server.Implementations.IO
{
_logger.LogError(ex, "Reading the file at {Path} failed due to a permissions exception.", fileInfo.FullName);
}
+ catch (IOException ex)
+ {
+ // IOException generally means the file is not accessible due to filesystem issues
+ // Catch this exception and mark the file as not exist to ignore it
+ _logger.LogError(ex, "Reading the file at {Path} failed due to an IO Exception. Marking the file as not existing", fileInfo.FullName);
+ result.Exists = false;
+ }
}
}
@@ -590,6 +597,9 @@ namespace Emby.Server.Implementations.IO
///
public virtual IEnumerable GetFileSystemEntries(string path, bool recursive = false)
{
+ // Note: any of unhandled exceptions thrown by this method may cause the caller to believe the whole path is not accessible.
+ // But what causing the exception may be a single file under that path. This could lead to unexpected behavior.
+ // For example, the scanner will remove everything in that path due to unhandled errors.
var directoryInfo = new DirectoryInfo(path);
var enumerationOptions = GetEnumerationOptions(recursive);
diff --git a/Emby.Server.Implementations/Localization/Core/ht.json b/Emby.Server.Implementations/Localization/Core/ht.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ht.json
@@ -0,0 +1 @@
+{}
diff --git a/Emby.Server.Implementations/Localization/Ratings/br.csv b/Emby.Server.Implementations/Localization/Ratings/br.csv
index 5ec1eb2627..f6053c88c7 100644
--- a/Emby.Server.Implementations/Localization/Ratings/br.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/br.csv
@@ -1,8 +1,14 @@
Livre,0
L,0
-ER,9
+AL,0
+ER,10
10,10
+A10,10
12,12
+A12,12
14,14
+A14,14
16,16
+A16,16
18,18
+A18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.csv b/Emby.Server.Implementations/Localization/Ratings/ca.csv
index 336ee28067..41dbda1349 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ca.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ca.csv
@@ -6,8 +6,6 @@ TV-Y7,7
TV-Y7-FV,7
PG,9
TV-PG,9
-PG-13,13
-13+,13
TV-14,14
14A,14
16+,16
diff --git a/Emby.Server.Implementations/Localization/Ratings/es.csv b/Emby.Server.Implementations/Localization/Ratings/es.csv
index 619e948d88..ee58660900 100644
--- a/Emby.Server.Implementations/Localization/Ratings/es.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/es.csv
@@ -1,7 +1,7 @@
A,0
A/fig,0
A/i,0
-A/fig/i,0
+A/i/fig,0
APTA,0
ERI,0
TP,0
diff --git a/Emby.Server.Implementations/Localization/Ratings/gb.csv b/Emby.Server.Implementations/Localization/Ratings/gb.csv
index 75b1c20589..858b9a32dd 100644
--- a/Emby.Server.Implementations/Localization/Ratings/gb.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/gb.csv
@@ -6,10 +6,11 @@ U,0
6+,6
7+,7
PG,8
-9+,9
+9,9
12,12
12+,12
12A,12
+12PG,12
Teen,13
13+,13
14+,14
diff --git a/Emby.Server.Implementations/Localization/Ratings/ie.csv b/Emby.Server.Implementations/Localization/Ratings/ie.csv
index 6ef2e50128..d3c634fc93 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ie.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/ie.csv
@@ -4,6 +4,7 @@ PG,12
12A,12
12PG,12
15,15
+15PG,15
15A,15
16,16
18,18
diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv
index c8f8e93db7..6856a2dbbb 100644
--- a/Emby.Server.Implementations/Localization/Ratings/no.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/no.csv
@@ -6,4 +6,5 @@ A,0
12,12
15,15
18,18
+C,18
Not approved,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.csv b/Emby.Server.Implementations/Localization/Ratings/nz.csv
index f617f0c39d..633da78fe1 100644
--- a/Emby.Server.Implementations/Localization/Ratings/nz.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/nz.csv
@@ -10,6 +10,7 @@ R16,16
RP16,16
GA,18
R18,18
+RP18,18
MA,1000
R,1001
Objectionable,1001
diff --git a/Emby.Server.Implementations/Localization/Ratings/us.csv b/Emby.Server.Implementations/Localization/Ratings/us.csv
index fc91edecd5..9aa5c00eb3 100644
--- a/Emby.Server.Implementations/Localization/Ratings/us.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/us.csv
@@ -48,3 +48,5 @@ TV-MA-LS,17
TV-MA-LV,17
TV-MA-SV,17
TV-MA-LSV,17
+TV-X,18
+TV-AO,18
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index 228413777e..0fd0149904 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -51,20 +51,21 @@ namespace Jellyfin.Api.Auth
}
var role = UserRoles.User;
- if (authorizationInfo.IsApiKey || authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+ if (authorizationInfo.IsApiKey
+ || (authorizationInfo.User?.HasPermission(PermissionKind.IsAdministrator) ?? false))
{
role = UserRoles.Administrator;
}
var claims = new[]
{
- new Claim(ClaimTypes.Name, authorizationInfo.User.Username),
+ new Claim(ClaimTypes.Name, authorizationInfo.User?.Username ?? string.Empty),
new Claim(ClaimTypes.Role, role),
new Claim(InternalClaimTypes.UserId, authorizationInfo.UserId.ToString("N", CultureInfo.InvariantCulture)),
- new Claim(InternalClaimTypes.DeviceId, authorizationInfo.DeviceId),
- new Claim(InternalClaimTypes.Device, authorizationInfo.Device),
- new Claim(InternalClaimTypes.Client, authorizationInfo.Client),
- new Claim(InternalClaimTypes.Version, authorizationInfo.Version),
+ new Claim(InternalClaimTypes.DeviceId, authorizationInfo.DeviceId ?? string.Empty),
+ new Claim(InternalClaimTypes.Device, authorizationInfo.Device ?? string.Empty),
+ new Claim(InternalClaimTypes.Client, authorizationInfo.Client ?? string.Empty),
+ new Claim(InternalClaimTypes.Version, authorizationInfo.Version ?? string.Empty),
new Claim(InternalClaimTypes.Token, authorizationInfo.Token),
new Claim(InternalClaimTypes.IsApiKey, authorizationInfo.IsApiKey.ToString(CultureInfo.InvariantCulture))
};
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 1923393cbc..2601fa3be8 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -235,6 +235,11 @@ public static class StreamingHelpers
state.VideoRequest.MaxHeight = resolution.MaxHeight;
}
}
+
+ if (state.AudioStream is not null && !EncodingHelper.IsCopyCodec(state.OutputAudioCodec) && string.Equals(state.AudioStream.Codec, state.OutputAudioCodec, StringComparison.OrdinalIgnoreCase) && state.OutputAudioBitrate.HasValue)
+ {
+ state.OutputAudioCodec = state.SupportedAudioCodecs.Where(c => !EncodingHelper.LosslessAudioCodecs.Contains(c)).FirstOrDefault(mediaEncoder.CanEncodeToAudioCodec);
+ }
}
var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
index c472abdf06..2ce1e76dec 100644
--- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
@@ -71,7 +71,8 @@ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListenerThe message.
protected override void Start(WebSocketMessageInfo message)
{
- if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+ if (message.Connection.AuthorizationInfo.User is null
+ || !message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
{
throw new AuthenticationException("Only admin users can retrieve the activity log.");
}
diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
index f4031be361..a793c2f39e 100644
--- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
+++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
@@ -80,7 +80,8 @@ public class SessionInfoWebSocketListener : BasePeriodicWebSocketListenerThe message.
protected override void Start(WebSocketMessageInfo message)
{
- if (!message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
+ if (message.Connection.AuthorizationInfo.User is null
+ || !message.Connection.AuthorizationInfo.User.HasPermission(PermissionKind.IsAdministrator))
{
throw new AuthenticationException("Only admin users can subscribe to session information.");
}
diff --git a/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/BaseItemEntity.cs b/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/BaseItemEntity.cs
index 33b2b67413..7600ecebff 100644
--- a/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/BaseItemEntity.cs
+++ b/Jellyfin.Database/Jellyfin.Database.Implementations/Entities/BaseItemEntity.cs
@@ -18,9 +18,9 @@ public class BaseItemEntity
public string? Path { get; set; }
- public DateTime StartDate { get; set; }
+ public DateTime? StartDate { get; set; }
- public DateTime EndDate { get; set; }
+ public DateTime? EndDate { get; set; }
public string? ChannelId { get; set; }
diff --git a/Jellyfin.Database/Jellyfin.Database.Providers.PgSql/Migrations/20250205183152_MakeStartEndDateNullable.Designer.cs b/Jellyfin.Database/Jellyfin.Database.Providers.PgSql/Migrations/20250205183152_MakeStartEndDateNullable.Designer.cs
new file mode 100644
index 0000000000..6e8aba54bf
--- /dev/null
+++ b/Jellyfin.Database/Jellyfin.Database.Providers.PgSql/Migrations/20250205183152_MakeStartEndDateNullable.Designer.cs
@@ -0,0 +1,1623 @@
+//
+using System;
+using Jellyfin.Server.Implementations;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Jellyfin.Database.Providers.PgSql.Migrations
+{
+ [DbContext(typeof(JellyfinDbContext))]
+ [Migration("20250205183152_MakeStartEndDateNullable")]
+ partial class MakeStartEndDateNullable
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.1")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("DayOfWeek")
+ .HasColumnType("integer");
+
+ b.Property("EndHour")
+ .HasColumnType("double precision");
+
+ b.Property("StartHour")
+ .HasColumnType("double precision");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AccessSchedules");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ItemId")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("LogSeverity")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Overview")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("bigint");
+
+ b.Property("ShortOverview")
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DateCreated");
+
+ b.ToTable("ActivityLogs");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AncestorId", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("ParentItemId")
+ .HasColumnType("uuid");
+
+ b.HasKey("ItemId", "ParentItemId");
+
+ b.HasIndex("ParentItemId");
+
+ b.ToTable("AncestorIds");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AttachmentStreamInfo", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("Index")
+ .HasColumnType("integer");
+
+ b.Property("Codec")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CodecTag")
+ .HasColumnType("text");
+
+ b.Property("Comment")
+ .HasColumnType("text");
+
+ b.Property("Filename")
+ .HasColumnType("text");
+
+ b.Property("MimeType")
+ .HasColumnType("text");
+
+ b.HasKey("ItemId", "Index");
+
+ b.ToTable("AttachmentStreamInfos");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Album")
+ .HasColumnType("text");
+
+ b.Property("AlbumArtists")
+ .HasColumnType("text");
+
+ b.Property("Artists")
+ .HasColumnType("text");
+
+ b.Property("Audio")
+ .HasColumnType("integer");
+
+ b.Property("ChannelId")
+ .HasColumnType("text");
+
+ b.Property("CleanName")
+ .HasColumnType("text");
+
+ b.Property("CommunityRating")
+ .HasColumnType("real");
+
+ b.Property("CriticRating")
+ .HasColumnType("real");
+
+ b.Property("CustomRating")
+ .HasColumnType("text");
+
+ b.Property("Data")
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateLastMediaAdded")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateLastRefreshed")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateLastSaved")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateModified")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EndDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EpisodeTitle")
+ .HasColumnType("text");
+
+ b.Property("ExternalId")
+ .HasColumnType("text");
+
+ b.Property("ExternalSeriesId")
+ .HasColumnType("text");
+
+ b.Property("ExternalServiceId")
+ .HasColumnType("text");
+
+ b.Property("ExtraIds")
+ .HasColumnType("text");
+
+ b.Property("ExtraType")
+ .HasColumnType("integer");
+
+ b.Property("ForcedSortName")
+ .HasColumnType("text");
+
+ b.Property("Genres")
+ .HasColumnType("text");
+
+ b.Property("Height")
+ .HasColumnType("integer");
+
+ b.Property("IndexNumber")
+ .HasColumnType("integer");
+
+ b.Property("InheritedParentalRatingValue")
+ .HasColumnType("integer");
+
+ b.Property("IsFolder")
+ .HasColumnType("boolean");
+
+ b.Property("IsInMixedFolder")
+ .HasColumnType("boolean");
+
+ b.Property("IsLocked")
+ .HasColumnType("boolean");
+
+ b.Property("IsMovie")
+ .HasColumnType("boolean");
+
+ b.Property("IsRepeat")
+ .HasColumnType("boolean");
+
+ b.Property("IsSeries")
+ .HasColumnType("boolean");
+
+ b.Property("IsVirtualItem")
+ .HasColumnType("boolean");
+
+ b.Property("LUFS")
+ .HasColumnType("real");
+
+ b.Property("MediaType")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("NormalizationGain")
+ .HasColumnType("real");
+
+ b.Property("OfficialRating")
+ .HasColumnType("text");
+
+ b.Property("OriginalTitle")
+ .HasColumnType("text");
+
+ b.Property("Overview")
+ .HasColumnType("text");
+
+ b.Property("OwnerId")
+ .HasColumnType("text");
+
+ b.Property("ParentId")
+ .HasColumnType("uuid");
+
+ b.Property("ParentIndexNumber")
+ .HasColumnType("integer");
+
+ b.Property("Path")
+ .HasColumnType("text");
+
+ b.Property("PreferredMetadataCountryCode")
+ .HasColumnType("text");
+
+ b.Property("PreferredMetadataLanguage")
+ .HasColumnType("text");
+
+ b.Property("PremiereDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PresentationUniqueKey")
+ .HasColumnType("text");
+
+ b.Property("PrimaryVersionId")
+ .HasColumnType("text");
+
+ b.Property("ProductionLocations")
+ .HasColumnType("text");
+
+ b.Property("ProductionYear")
+ .HasColumnType("integer");
+
+ b.Property("RunTimeTicks")
+ .HasColumnType("bigint");
+
+ b.Property("SeasonId")
+ .HasColumnType("uuid");
+
+ b.Property("SeasonName")
+ .HasColumnType("text");
+
+ b.Property("SeriesId")
+ .HasColumnType("uuid");
+
+ b.Property("SeriesName")
+ .HasColumnType("text");
+
+ b.Property("SeriesPresentationUniqueKey")
+ .HasColumnType("text");
+
+ b.Property("ShowId")
+ .HasColumnType("text");
+
+ b.Property("Size")
+ .HasColumnType("bigint");
+
+ b.Property("SortName")
+ .HasColumnType("text");
+
+ b.Property("StartDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Studios")
+ .HasColumnType("text");
+
+ b.Property("Tagline")
+ .HasColumnType("text");
+
+ b.Property("Tags")
+ .HasColumnType("text");
+
+ b.Property("TopParentId")
+ .HasColumnType("uuid");
+
+ b.Property("TotalBitrate")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UnratedType")
+ .HasColumnType("text");
+
+ b.Property("Width")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.HasIndex("Path");
+
+ b.HasIndex("PresentationUniqueKey");
+
+ b.HasIndex("TopParentId", "Id");
+
+ b.HasIndex("Type", "TopParentId", "Id");
+
+ b.HasIndex("Type", "TopParentId", "PresentationUniqueKey");
+
+ b.HasIndex("Type", "TopParentId", "StartDate");
+
+ b.HasIndex("Id", "Type", "IsFolder", "IsVirtualItem");
+
+ b.HasIndex("MediaType", "TopParentId", "IsVirtualItem", "PresentationUniqueKey");
+
+ b.HasIndex("Type", "SeriesPresentationUniqueKey", "IsFolder", "IsVirtualItem");
+
+ b.HasIndex("Type", "SeriesPresentationUniqueKey", "PresentationUniqueKey", "SortName");
+
+ b.HasIndex("IsFolder", "TopParentId", "IsVirtualItem", "PresentationUniqueKey", "DateCreated");
+
+ b.HasIndex("Type", "TopParentId", "IsVirtualItem", "PresentationUniqueKey", "DateCreated");
+
+ b.ToTable("BaseItems");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemImageInfo", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Blurhash")
+ .HasColumnType("bytea");
+
+ b.Property("DateModified")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Height")
+ .HasColumnType("integer");
+
+ b.Property("ImageType")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Width")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ItemId");
+
+ b.ToTable("BaseItemImageInfos");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemMetadataField", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id", "ItemId");
+
+ b.HasIndex("ItemId");
+
+ b.ToTable("BaseItemMetadataFields");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemProvider", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("ProviderId")
+ .HasColumnType("text");
+
+ b.Property("ProviderValue")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("ItemId", "ProviderId");
+
+ b.HasIndex("ProviderId", "ProviderValue", "ItemId");
+
+ b.ToTable("BaseItemProviders");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.BaseItemTrailerType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id", "ItemId");
+
+ b.HasIndex("ItemId");
+
+ b.ToTable("BaseItemTrailerTypes");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("ChapterIndex")
+ .HasColumnType("integer");
+
+ b.Property("ImageDateModified")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImagePath")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .HasColumnType("text");
+
+ b.Property("StartPositionTicks")
+ .HasColumnType("bigint");
+
+ b.HasKey("ItemId", "ChapterIndex");
+
+ b.ToTable("Chapters");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client", "Key")
+ .IsUnique();
+
+ b.ToTable("CustomItemDisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChromecastVersion")
+ .HasColumnType("integer");
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("DashboardTheme")
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("EnableNextVideoInfoOverlay")
+ .HasColumnType("boolean");
+
+ b.Property("IndexBy")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("ScrollDirection")
+ .HasColumnType("integer");
+
+ b.Property("ShowBackdrop")
+ .HasColumnType("boolean");
+
+ b.Property("ShowSidebar")
+ .HasColumnType("boolean");
+
+ b.Property("SkipBackwardLength")
+ .HasColumnType("integer");
+
+ b.Property("SkipForwardLength")
+ .HasColumnType("integer");
+
+ b.Property("TvHome")
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client")
+ .IsUnique();
+
+ b.ToTable("DisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("DisplayPreferencesId")
+ .HasColumnType("integer");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DisplayPreferencesId");
+
+ b.ToTable("HomeSection");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("LastModified")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("ImageInfos");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("IndexBy")
+ .HasColumnType("integer");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("RememberIndexing")
+ .HasColumnType("boolean");
+
+ b.Property("RememberSorting")
+ .HasColumnType("boolean");
+
+ b.Property("SortBy")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property("SortOrder")
+ .HasColumnType("integer");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("ViewType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ItemDisplayPreferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemValue", b =>
+ {
+ b.Property("ItemValueId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CleanValue")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("ItemValueId");
+
+ b.HasIndex("Type", "CleanValue")
+ .IsUnique();
+
+ b.ToTable("ItemValues");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemValueMap", b =>
+ {
+ b.Property("ItemValueId")
+ .HasColumnType("uuid");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.HasKey("ItemValueId", "ItemId");
+
+ b.HasIndex("ItemId");
+
+ b.ToTable("ItemValuesMap");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.MediaSegment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("EndTicks")
+ .HasColumnType("bigint");
+
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("SegmentProviderId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("StartTicks")
+ .HasColumnType("bigint");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("MediaSegments");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.MediaStreamInfo", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("StreamIndex")
+ .HasColumnType("integer");
+
+ b.Property("AspectRatio")
+ .HasColumnType("text");
+
+ b.Property("AverageFrameRate")
+ .HasColumnType("real");
+
+ b.Property("BitDepth")
+ .HasColumnType("integer");
+
+ b.Property("BitRate")
+ .HasColumnType("integer");
+
+ b.Property("BlPresentFlag")
+ .HasColumnType("integer");
+
+ b.Property("ChannelLayout")
+ .HasColumnType("text");
+
+ b.Property("Channels")
+ .HasColumnType("integer");
+
+ b.Property("Codec")
+ .HasColumnType("text");
+
+ b.Property("CodecTag")
+ .HasColumnType("text");
+
+ b.Property("CodecTimeBase")
+ .HasColumnType("text");
+
+ b.Property("ColorPrimaries")
+ .HasColumnType("text");
+
+ b.Property("ColorSpace")
+ .HasColumnType("text");
+
+ b.Property("ColorTransfer")
+ .HasColumnType("text");
+
+ b.Property("Comment")
+ .HasColumnType("text");
+
+ b.Property("DvBlSignalCompatibilityId")
+ .HasColumnType("integer");
+
+ b.Property("DvLevel")
+ .HasColumnType("integer");
+
+ b.Property("DvProfile")
+ .HasColumnType("integer");
+
+ b.Property("DvVersionMajor")
+ .HasColumnType("integer");
+
+ b.Property("DvVersionMinor")
+ .HasColumnType("integer");
+
+ b.Property("ElPresentFlag")
+ .HasColumnType("integer");
+
+ b.Property("Height")
+ .HasColumnType("integer");
+
+ b.Property("IsAnamorphic")
+ .HasColumnType("boolean");
+
+ b.Property("IsAvc")
+ .HasColumnType("boolean");
+
+ b.Property("IsDefault")
+ .HasColumnType("boolean");
+
+ b.Property("IsExternal")
+ .HasColumnType("boolean");
+
+ b.Property("IsForced")
+ .HasColumnType("boolean");
+
+ b.Property("IsHearingImpaired")
+ .HasColumnType("boolean");
+
+ b.Property("IsInterlaced")
+ .HasColumnType("boolean");
+
+ b.Property("KeyFrames")
+ .HasColumnType("text");
+
+ b.Property("Language")
+ .HasColumnType("text");
+
+ b.Property("Level")
+ .HasColumnType("real");
+
+ b.Property("NalLengthSize")
+ .HasColumnType("text");
+
+ b.Property("Path")
+ .HasColumnType("text");
+
+ b.Property("PixelFormat")
+ .HasColumnType("text");
+
+ b.Property("Profile")
+ .HasColumnType("text");
+
+ b.Property("RealFrameRate")
+ .HasColumnType("real");
+
+ b.Property("RefFrames")
+ .HasColumnType("integer");
+
+ b.Property("Rotation")
+ .HasColumnType("integer");
+
+ b.Property("RpuPresentFlag")
+ .HasColumnType("integer");
+
+ b.Property("SampleRate")
+ .HasColumnType("integer");
+
+ b.Property("StreamType")
+ .HasColumnType("integer");
+
+ b.Property("TimeBase")
+ .HasColumnType("text");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ b.Property("Width")
+ .HasColumnType("integer");
+
+ b.HasKey("ItemId", "StreamIndex");
+
+ b.HasIndex("StreamIndex");
+
+ b.HasIndex("StreamType");
+
+ b.HasIndex("StreamIndex", "StreamType");
+
+ b.HasIndex("StreamIndex", "StreamType", "Language");
+
+ b.ToTable("MediaStreamInfos");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.People", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PersonType")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name");
+
+ b.ToTable("Peoples");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.PeopleBaseItemMap", b =>
+ {
+ b.Property("ItemId")
+ .HasColumnType("uuid");
+
+ b.Property("PeopleId")
+ .HasColumnType("uuid");
+
+ b.Property("ListOrder")
+ .HasColumnType("integer");
+
+ b.Property("Role")
+ .HasColumnType("text");
+
+ b.Property("SortOrder")
+ .HasColumnType("integer");
+
+ b.HasKey("ItemId", "PeopleId");
+
+ b.HasIndex("PeopleId");
+
+ b.HasIndex("ItemId", "ListOrder");
+
+ b.HasIndex("ItemId", "SortOrder");
+
+ b.ToTable("PeopleBaseItemMap");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Kind")
+ .HasColumnType("integer");
+
+ b.Property("Permission_Permissions_Guid")
+ .HasColumnType("uuid");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Value")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Permissions");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Kind")
+ .HasColumnType("integer");
+
+ b.Property("Preference_Preferences_Guid")
+ .HasColumnType("uuid");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("bigint");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(65535)
+ .HasColumnType("character varying(65535)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Preferences");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.ApiKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AccessToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateLastActivity")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AccessToken")
+ .IsUnique();
+
+ b.ToTable("ApiKeys");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AccessToken")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("AppName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("character varying(64)");
+
+ b.Property