aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs52
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs26
-rw-r--r--Jellyfin.Api/Controllers/DisplayPreferencesController.cs22
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs3
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs28
-rw-r--r--Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs2
-rw-r--r--Jellyfin.Data/Entities/ItemDisplayPreferences.cs1
-rw-r--r--Jellyfin.Data/Enums/ViewType.cs101
-rw-r--r--Jellyfin.Networking/Configuration/NetworkConfiguration.cs12
-rw-r--r--Jellyfin.Server.Implementations/Activity/ActivityManager.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs2
-rw-r--r--Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj1
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs1
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs3
-rw-r--r--Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs2
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs3
-rw-r--r--Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs4
-rw-r--r--Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs9
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs4
-rw-r--r--README.md8
23 files changed, 191 insertions, 101 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index d74ea0352..50ef71a46 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -284,13 +285,6 @@ namespace Emby.Server.Implementations
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- CertificateInfo = new CertificateInfo
- {
- Path = ServerConfigurationManager.Configuration.CertificatePath,
- Password = ServerConfigurationManager.Configuration.CertificatePassword
- };
- Certificate = GetCertificate(CertificateInfo);
-
ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version;
ApplicationVersionString = ApplicationVersion.ToString(3);
ApplicationUserAgent = Name.Replace(' ', '-') + "/" + ApplicationVersionString;
@@ -456,6 +450,7 @@ namespace Emby.Server.Implementations
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
+ ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated;
_mediaEncoder.SetFFmpegPath();
@@ -505,6 +500,13 @@ namespace Emby.Server.Implementations
HttpsPort = NetworkConfiguration.DefaultHttpsPort;
}
+ CertificateInfo = new CertificateInfo
+ {
+ Path = networkConfiguration.CertificatePath,
+ Password = networkConfiguration.CertificatePassword
+ };
+ Certificate = GetCertificate(CertificateInfo);
+
DiscoverTypes();
RegisterServices();
@@ -714,7 +716,7 @@ namespace Emby.Server.Implementations
// Don't use an empty string password
var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password;
- var localCert = new X509Certificate2(certificateLocation, password);
+ var localCert = new X509Certificate2(certificateLocation, password, X509KeyStorageFlags.UserKeySet);
// localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
if (!localCert.HasPrivateKey)
{
@@ -912,11 +914,11 @@ namespace Emby.Server.Implementations
protected void OnConfigurationUpdated(object sender, EventArgs e)
{
var requiresRestart = false;
+ var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration();
// Don't do anything if these haven't been set yet
if (HttpPort != 0 && HttpsPort != 0)
{
- var networkConfiguration = ServerConfigurationManager.GetNetworkConfiguration();
// Need to restart if ports have changed
if (networkConfiguration.HttpServerPortNumber != HttpPort ||
networkConfiguration.HttpsPortNumber != HttpsPort)
@@ -936,10 +938,7 @@ namespace Emby.Server.Implementations
requiresRestart = true;
}
- var currentCertPath = CertificateInfo?.Path;
- var newCertPath = ServerConfigurationManager.Configuration.CertificatePath;
-
- if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase))
+ if (ValidateSslCertificate(networkConfiguration))
{
requiresRestart = true;
}
@@ -953,6 +952,33 @@ namespace Emby.Server.Implementations
}
/// <summary>
+ /// Validates the SSL certificate.
+ /// </summary>
+ /// <param name="networkConfig">The new configuration.</param>
+ /// <exception cref="FileNotFoundException">The certificate path doesn't exist.</exception>
+ private bool ValidateSslCertificate(NetworkConfiguration networkConfig)
+ {
+ var newPath = networkConfig.CertificatePath;
+
+ if (!string.IsNullOrWhiteSpace(newPath)
+ && !string.Equals(CertificateInfo?.Path, newPath, StringComparison.Ordinal))
+ {
+ if (File.Exists(newPath))
+ {
+ return true;
+ }
+
+ throw new FileNotFoundException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Certificate file '{0}' does not exist.",
+ newPath));
+ }
+
+ return false;
+ }
+
+ /// <summary>
/// Notifies that the kernel that a change has been made that requires a restart.
/// </summary>
public void NotifyPendingRestart()
diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index f05a30a89..7a8ed8c29 100644
--- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -88,7 +88,6 @@ namespace Emby.Server.Implementations.Configuration
var newConfig = (ServerConfiguration)newConfiguration;
ValidateMetadataPath(newConfig);
- ValidateSslCertificate(newConfig);
ConfigurationUpdating?.Invoke(this, new GenericEventArgs<ServerConfiguration>(newConfig));
@@ -96,31 +95,6 @@ namespace Emby.Server.Implementations.Configuration
}
/// <summary>
- /// Validates the SSL certificate.
- /// </summary>
- /// <param name="newConfig">The new configuration.</param>
- /// <exception cref="FileNotFoundException">The certificate path doesn't exist.</exception>
- private void ValidateSslCertificate(BaseApplicationConfiguration newConfig)
- {
- var serverConfig = (ServerConfiguration)newConfig;
-
- var newPath = serverConfig.CertificatePath;
-
- if (!string.IsNullOrWhiteSpace(newPath)
- && !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
- {
- if (!File.Exists(newPath))
- {
- throw new FileNotFoundException(
- string.Format(
- CultureInfo.InvariantCulture,
- "Certificate file '{0}' does not exist.",
- newPath));
- }
- }
- }
-
- /// <summary>
/// Validates the metadata path.
/// </summary>
/// <param name="newConfig">The new configuration.</param>
diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
index 8b8f63015..f7bb968f0 100644
--- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
+++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Model.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Controllers
{
@@ -22,14 +23,17 @@ namespace Jellyfin.Api.Controllers
public class DisplayPreferencesController : BaseJellyfinApiController
{
private readonly IDisplayPreferencesManager _displayPreferencesManager;
+ private readonly ILogger<DisplayPreferencesController> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesController"/> class.
/// </summary>
/// <param name="displayPreferencesManager">Instance of <see cref="IDisplayPreferencesManager"/> interface.</param>
- public DisplayPreferencesController(IDisplayPreferencesManager displayPreferencesManager)
+ /// <param name="logger">Instance of <see cref="ILogger{DisplayPreferencesController}"/> interface.</param>
+ public DisplayPreferencesController(IDisplayPreferencesManager displayPreferencesManager, ILogger<DisplayPreferencesController> logger)
{
_displayPreferencesManager = displayPreferencesManager;
+ _logger = logger;
}
/// <summary>
@@ -61,7 +65,6 @@ namespace Jellyfin.Api.Controllers
{
Client = displayPreferences.Client,
Id = displayPreferences.ItemId.ToString(),
- ViewType = itemPreferences.ViewType.ToString(),
SortBy = itemPreferences.SortBy,
SortOrder = itemPreferences.SortOrder,
IndexBy = displayPreferences.IndexBy?.ToString(),
@@ -77,11 +80,6 @@ namespace Jellyfin.Api.Controllers
dto.CustomPrefs["homesection" + homeSection.Order] = homeSection.Type.ToString().ToLowerInvariant();
}
- foreach (var itemDisplayPreferences in _displayPreferencesManager.ListItemDisplayPreferences(displayPreferences.UserId, displayPreferences.Client))
- {
- dto.CustomPrefs["landing-" + itemDisplayPreferences.ItemId] = itemDisplayPreferences.ViewType.ToString().ToLowerInvariant();
- }
-
dto.CustomPrefs["chromecastVersion"] = displayPreferences.ChromecastVersion.ToString().ToLowerInvariant();
dto.CustomPrefs["skipForwardLength"] = displayPreferences.SkipForwardLength.ToString(CultureInfo.InvariantCulture);
dto.CustomPrefs["skipBackLength"] = displayPreferences.SkipBackwardLength.ToString(CultureInfo.InvariantCulture);
@@ -189,10 +187,9 @@ namespace Jellyfin.Api.Controllers
foreach (var key in displayPreferences.CustomPrefs.Keys.Where(key => key.StartsWith("landing-", StringComparison.OrdinalIgnoreCase)))
{
- if (Guid.TryParse(key.AsSpan().Slice("landing-".Length), out var preferenceId))
+ if (!Enum.TryParse<ViewType>(displayPreferences.CustomPrefs[key], true, out var type))
{
- var itemPreferences = _displayPreferencesManager.GetItemDisplayPreferences(existingDisplayPreferences.UserId, preferenceId, existingDisplayPreferences.Client);
- itemPreferences.ViewType = Enum.Parse<ViewType>(displayPreferences.ViewType);
+ _logger.LogError("Invalid ViewType: {LandingScreenOption}", displayPreferences.CustomPrefs[key]);
displayPreferences.CustomPrefs.Remove(key);
}
}
@@ -204,11 +201,6 @@ namespace Jellyfin.Api.Controllers
itemPrefs.RememberSorting = displayPreferences.RememberSorting;
itemPrefs.ItemId = itemId;
- if (Enum.TryParse<ViewType>(displayPreferences.ViewType, true, out var viewType))
- {
- itemPrefs.ViewType = viewType;
- }
-
// Set all remaining custom preferences.
_displayPreferencesManager.SetCustomItemDisplayPreferences(userId, itemId, existingDisplayPreferences.Client, displayPreferences.CustomPrefs);
_displayPreferencesManager.SaveChanges();
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index a76dc057a..2a1da31c9 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -17,6 +17,7 @@ using MediaBrowser.Model.MediaInfo;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Controllers
@@ -119,7 +120,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableTranscoding,
[FromQuery] bool? allowVideoStreamCopy,
[FromQuery] bool? allowAudioStreamCopy,
- [FromBody] PlaybackInfoDto? playbackInfoDto)
+ [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto)
{
var authInfo = _authContext.GetAuthorizationInfo(Request);
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index 3e55434c0..fcdad4bc7 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
@@ -17,6 +18,7 @@ using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Jellyfin.Api.Controllers
{
@@ -53,6 +55,13 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Creates a new playlist.
/// </summary>
+ /// <remarks>
+ /// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
+ /// </remarks>
+ /// <param name="name">The playlist name.</param>
+ /// <param name="ids">The item ids.</param>
+ /// <param name="userId">The user id.</param>
+ /// <param name="mediaType">The media type.</param>
/// <param name="createPlaylistRequest">The create playlist payload.</param>
/// <returns>
/// A <see cref="Task" /> that represents the asynchronous operation to create a playlist.
@@ -61,14 +70,23 @@ namespace Jellyfin.Api.Controllers
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
- [FromBody, Required] CreatePlaylistDto createPlaylistRequest)
+ [FromQuery] string? name,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] IReadOnlyList<Guid> ids,
+ [FromQuery] Guid? userId,
+ [FromQuery] string? mediaType,
+ [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
{
+ if (ids.Count == 0)
+ {
+ ids = createPlaylistRequest?.Ids ?? Array.Empty<Guid>();
+ }
+
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
{
- Name = createPlaylistRequest.Name,
- ItemIdList = createPlaylistRequest.Ids,
- UserId = createPlaylistRequest.UserId,
- MediaType = createPlaylistRequest.MediaType
+ Name = name ?? createPlaylistRequest?.Name,
+ ItemIdList = ids,
+ UserId = userId ?? createPlaylistRequest?.UserId ?? default,
+ MediaType = mediaType ?? createPlaylistRequest?.MediaType
}).ConfigureAwait(false);
return result;
diff --git a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
index d0d6889fc..65d4b644e 100644
--- a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
+++ b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs
@@ -24,7 +24,7 @@ namespace Jellyfin.Api.Models.PlaylistDtos
/// <summary>
/// Gets or sets the user id.
/// </summary>
- public Guid UserId { get; set; }
+ public Guid? UserId { get; set; }
/// <summary>
/// Gets or sets the media type.
diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
index d81e4a31c..2b25bb25f 100644
--- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
+++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs
@@ -23,7 +23,6 @@ namespace Jellyfin.Data.Entities
Client = client;
SortBy = "SortName";
- ViewType = ViewType.Poster;
SortOrder = SortOrder.Ascending;
RememberSorting = false;
RememberIndexing = false;
diff --git a/Jellyfin.Data/Enums/ViewType.cs b/Jellyfin.Data/Enums/ViewType.cs
index 595429ab1..c0fd7d448 100644
--- a/Jellyfin.Data/Enums/ViewType.cs
+++ b/Jellyfin.Data/Enums/ViewType.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Data.Enums
+namespace Jellyfin.Data.Enums
{
/// <summary>
/// An enum representing the type of view for a library or collection.
@@ -6,33 +6,108 @@
public enum ViewType
{
/// <summary>
- /// Shows banners.
+ /// Shows albums.
/// </summary>
- Banner = 0,
+ Albums = 0,
/// <summary>
- /// Shows a list of content.
+ /// Shows album artists.
/// </summary>
- List = 1,
+ AlbumArtists = 1,
/// <summary>
- /// Shows poster artwork.
+ /// Shows artists.
/// </summary>
- Poster = 2,
+ Artists = 2,
/// <summary>
- /// Shows poster artwork with a card containing the name and year.
+ /// Shows channels.
/// </summary>
- PosterCard = 3,
+ Channels = 3,
/// <summary>
- /// Shows a thumbnail.
+ /// Shows collections.
/// </summary>
- Thumb = 4,
+ Collections = 4,
/// <summary>
- /// Shows a thumbnail with a card containing the name and year.
+ /// Shows episodes.
/// </summary>
- ThumbCard = 5
+ Episodes = 5,
+
+ /// <summary>
+ /// Shows favorites.
+ /// </summary>
+ Favorites = 6,
+
+ /// <summary>
+ /// Shows genres.
+ /// </summary>
+ Genres = 7,
+
+ /// <summary>
+ /// Shows guide.
+ /// </summary>
+ Guide = 8,
+
+ /// <summary>
+ /// Shows movies.
+ /// </summary>
+ Movies = 9,
+
+ /// <summary>
+ /// Shows networks.
+ /// </summary>
+ Networks = 10,
+
+ /// <summary>
+ /// Shows playlists.
+ /// </summary>
+ Playlists = 11,
+
+ /// <summary>
+ /// Shows programs.
+ /// </summary>
+ Programs = 12,
+
+ /// <summary>
+ /// Shows recordings.
+ /// </summary>
+ Recordings = 13,
+
+ /// <summary>
+ /// Shows schedule.
+ /// </summary>
+ Schedule = 14,
+
+ /// <summary>
+ /// Shows series.
+ /// </summary>
+ Series = 15,
+
+ /// <summary>
+ /// Shows shows.
+ /// </summary>
+ Shows = 16,
+
+ /// <summary>
+ /// Shows songs.
+ /// </summary>
+ Songs = 17,
+
+ /// <summary>
+ /// Shows songs.
+ /// </summary>
+ Suggestions = 18,
+
+ /// <summary>
+ /// Shows trailers.
+ /// </summary>
+ Trailers = 19,
+
+ /// <summary>
+ /// Shows upcoming.
+ /// </summary>
+ Upcoming = 20
}
}
diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
index df420f48a..792e57f6a 100644
--- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
+++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs
@@ -28,6 +28,16 @@ namespace Jellyfin.Networking.Configuration
public bool RequireHttps { get; set; }
/// <summary>
+ /// Gets or sets the filesystem path of an X.509 certificate to use for SSL.
+ /// </summary>
+ public string CertificatePath { get; set; } = string.Empty;
+
+ /// <summary>
+ /// Gets or sets the password required to access the X.509 certificate data in the file specified by <see cref="CertificatePath"/>.
+ /// </summary>
+ public string CertificatePassword { get; set; } = string.Empty;
+
+ /// <summary>
/// Gets or sets a value used to specify the URL prefix that your Jellyfin instance can be accessed at.
/// </summary>
public string BaseUrl
@@ -83,7 +93,7 @@ namespace Jellyfin.Networking.Configuration
/// </summary>
/// <remarks>
/// In order for HTTPS to be used, in addition to setting this to true, valid values must also be
- /// provided for <see cref="ServerConfiguration.CertificatePath"/> and <see cref="ServerConfiguration.CertificatePassword"/>.
+ /// provided for <see cref="CertificatePath"/> and <see cref="CertificatePassword"/>.
/// </remarks>
public bool EnableHttps { get; set; }
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
index 7bde4f35b..27360afb0 100644
--- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
+++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
@@ -27,7 +27,7 @@ namespace Jellyfin.Server.Implementations.Activity
}
/// <inheritdoc/>
- public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
+ public event EventHandler<GenericEventArgs<ActivityLogEntry>>? EntryCreated;
/// <inheritdoc/>
public async Task CreateAsync(ActivityLog entry)
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
index ec4a76e7f..0340248bb 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs
@@ -86,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session
return name;
}
- private static string GetPlaybackNotificationType(string mediaType)
+ private static string? GetPlaybackNotificationType(string mediaType)
{
if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
index a0bad29e9..1648b1b47 100644
--- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
+++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs
@@ -94,7 +94,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session
return name;
}
- private static string GetPlaybackStoppedNotificationType(string mediaType)
+ private static string? GetPlaybackStoppedNotificationType(string mediaType)
{
if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 5f508ea0a..9e4a2065f 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -5,6 +5,7 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index 7f3f83749..39f842354 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -1,3 +1,4 @@
+#nullable disable
#pragma warning disable CS1591
using System;
diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
index 662b4bf65..6a78e7ee6 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
@@ -1,5 +1,3 @@
-#nullable enable
-
using System;
using System.Linq;
using System.Text;
diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
index 334f27f85..9cc1c3e5e 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
@@ -1,5 +1,3 @@
-#nullable enable
-
using System;
using System.Collections.Generic;
using System.IO;
diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
index 1fb89c4a6..dbba80c21 100644
--- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
+++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs
@@ -1,5 +1,4 @@
-#nullable enable
-#pragma warning disable CS1591
+#pragma warning disable CS1591
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
diff --git a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs
index 5f32479e1..c4e4c460a 100644
--- a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs
@@ -1,5 +1,3 @@
-#nullable enable
-
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Authentication;
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index e37fcc908..b76b272cf 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -1,5 +1,4 @@
-#nullable enable
-#pragma warning disable CA1307
+#pragma warning disable CA1307
using System;
using System.Collections.Concurrent;
diff --git a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
index 8a510898b..a9a18c823 100644
--- a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
+++ b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
@@ -13,9 +13,9 @@ namespace Jellyfin.Server.Implementations.ValueConverters
/// </summary>
/// <param name="kind">The kind to specify.</param>
/// <param name="mappingHints">The mapping hints.</param>
- public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints mappingHints = null)
+ public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints? mappingHints = null)
: base(v => v.ToUniversalTime(), v => DateTime.SpecifyKind(v, kind), mappingHints)
{
}
}
-} \ No newline at end of file
+}
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index af4be5a26..dd005b7f4 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -81,6 +81,7 @@ namespace Jellyfin.Server.Migrations.Routines
{ "unstable", ChromecastVersion.Unstable }
};
+ var customDisplayPrefs = new HashSet<string>();
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null))
{
@@ -185,7 +186,13 @@ namespace Jellyfin.Server.Migrations.Routines
foreach (var (key, value) in dto.CustomPrefs)
{
- dbContext.Add(new CustomItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client, key, value));
+ // Custom display preferences can have a key collision.
+ var indexKey = $"{displayPreferences.UserId}|{itemId}|{displayPreferences.Client}|{key}";
+ if (!customDisplayPrefs.Contains(indexKey))
+ {
+ dbContext.Add(new CustomItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client, key, value));
+ customDisplayPrefs.Add(indexKey);
+ }
}
dbContext.Add(displayPreferences);
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 100756c24..38b333510 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -88,11 +88,11 @@ namespace MediaBrowser.Model.Configuration
// The left side of the dot is the platform number, and the right side is the device number on the platform.
OpenclDevice = "0.0";
EnableTonemapping = false;
- TonemappingAlgorithm = "reinhard";
+ TonemappingAlgorithm = "hable";
TonemappingRange = "auto";
TonemappingDesat = 0;
TonemappingThreshold = 0.8;
- TonemappingPeak = 0;
+ TonemappingPeak = 100;
TonemappingParam = 0;
H264Crf = 23;
H265Crf = 28;
diff --git a/README.md b/README.md
index 1ab246f84..29f992349 100644
--- a/README.md
+++ b/README.md
@@ -105,12 +105,6 @@ There are three options to get the files for the web client.
2. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web)
3. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located at `C:\Program Files\Jellyfin\Server\jellyfin-web`
-Once you have a copy of the built web client files, you need to copy them into a specific directory.
-
-> `<repository root>/Mediabrowser.WebDashboard/jellyfin-web`
-
-As part of the build process, this folder will be copied to the build output directory, where it can be accessed by the server.
-
### Running The Server
The following instructions will help you get the project up and running via the command line, or your preferred IDE.
@@ -133,7 +127,7 @@ To run the server from the command line you can use the `dotnet run` command. Th
```bash
cd jellyfin # Move into the repository directory
-dotnet run --project Jellyfin.Server # Run the server startup project
+dotnet run --project Jellyfin.Server --webdir /absolute/path/to/jellyfin-web/dist # Run the server startup project
```
A second option is to build the project and then run the resulting executable file directly. When running the executable directly you can easily add command line options. Add the `--help` flag to list details on all the supported command line options.