diff --git a/.editorconfig b/.editorconfig index dc9aaa3ed5..b84e563efa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,7 +13,7 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf -max_line_length = null +max_line_length = off # YAML indentation [*.{yml,yaml}] @@ -22,6 +22,7 @@ indent_size = 2 # XML indentation [*.{csproj,xml}] indent_size = 2 + ############################### # .NET Coding Conventions # ############################### @@ -51,11 +52,12 @@ dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_null_propagation = true:suggestion dotnet_style_coalesce_expression = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent -dotnet_prefer_inferred_tuple_names = true:suggestion -dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_auto_properties = true:silent dotnet_style_prefer_conditional_expression_over_assignment = true:silent dotnet_style_prefer_conditional_expression_over_return = true:silent + ############################### # Naming Conventions # ############################### @@ -67,7 +69,7 @@ dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field -dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected dotnet_naming_symbols.non_private_static_fields.required_modifiers = static dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case @@ -159,6 +161,7 @@ csharp_style_deconstructed_variable_declaration = true:suggestion csharp_prefer_simple_default_expression = true:suggestion csharp_style_pattern_local_over_anonymous_function = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion + ############################### # C# Formatting Rules # ############################### @@ -189,9 +192,3 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true -############################### -# VB Coding Conventions # -############################### -[*.vb] -# Modifier preferences -visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/Dockerfile b/Dockerfile index 6e834d4e0b..d3fb138a81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ ARG DOTNET_VERSION=3.1 FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master -RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm \ +RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ && yarn install \ diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs index 473005b556..b3aad85cec 100644 --- a/DvdLib/BigEndianBinaryReader.cs +++ b/DvdLib/BigEndianBinaryReader.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Buffers.Binary; using System.IO; diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index fd0cb5e255..64d041cb05 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -13,6 +13,7 @@ netstandard2.1 false true + true diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs index 268ab897ee..2eab400f7d 100644 --- a/DvdLib/Ifo/Cell.cs +++ b/DvdLib/Ifo/Cell.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; namespace DvdLib.Ifo diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs index e588e51ac0..6e33a0ec5a 100644 --- a/DvdLib/Ifo/CellPlaybackInfo.cs +++ b/DvdLib/Ifo/CellPlaybackInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; namespace DvdLib.Ifo diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs index 2b973e0830..216aa0f77a 100644 --- a/DvdLib/Ifo/CellPositionInfo.cs +++ b/DvdLib/Ifo/CellPositionInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.IO; namespace DvdLib.Ifo diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs index bd3bd97040..1e69429f82 100644 --- a/DvdLib/Ifo/Chapter.cs +++ b/DvdLib/Ifo/Chapter.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + namespace DvdLib.Ifo { public class Chapter diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs index 5af58a2dc8..ca20baa73f 100644 --- a/DvdLib/Ifo/Dvd.cs +++ b/DvdLib/Ifo/Dvd.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs index 3688089ec7..978af90c2e 100644 --- a/DvdLib/Ifo/DvdTime.cs +++ b/DvdLib/Ifo/DvdTime.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace DvdLib.Ifo diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs index af08afa356..9f62512706 100644 --- a/DvdLib/Ifo/Program.cs +++ b/DvdLib/Ifo/Program.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; namespace DvdLib.Ifo diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs index 7b003005b9..4860360afd 100644 --- a/DvdLib/Ifo/ProgramChain.cs +++ b/DvdLib/Ifo/ProgramChain.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs index 335e929928..abf806d2c0 100644 --- a/DvdLib/Ifo/Title.cs +++ b/DvdLib/Ifo/Title.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.IO; diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs index 757a5a05db..5d111ebc06 100644 --- a/DvdLib/Ifo/UserOperation.cs +++ b/DvdLib/Ifo/UserOperation.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; namespace DvdLib.Ifo diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 43e9830540..9d7c0d3659 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -908,7 +908,8 @@ namespace Emby.Dlna.PlayTo return 0; } - public Task SendMessage(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken) + /// + public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) { if (_disposed) { @@ -924,10 +925,12 @@ namespace Emby.Dlna.PlayTo { return SendPlayCommand(data as PlayRequest, cancellationToken); } + if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) { return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); } + if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) { return SendGeneralCommand(data as GeneralCommand, cancellationToken); diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 7f755fd25e..948fe037b5 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -227,7 +227,7 @@ namespace Emby.Naming.Video } return remainingFiles - .Where(i => i.ExtraType == null) + .Where(i => i.ExtraType != null) .Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase))) .ToList(); diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 0b75a8cce9..b4aee614b0 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -89,14 +89,14 @@ namespace Emby.Naming.Video if (parseName) { var cleanDateTimeResult = CleanDateTime(name); + name = cleanDateTimeResult.Name; + year = cleanDateTimeResult.Year; if (extraResult.ExtraType == null - && TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan newName)) + && TryCleanString(name, out ReadOnlySpan newName)) { name = newName.ToString(); } - - year = cleanDateTimeResult.Year; } return new VideoFileInfo diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 4685a03ac3..3983824a3e 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -4,11 +4,10 @@ using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; +using Jellyfin.Data.Entities; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; @@ -30,7 +29,7 @@ namespace Emby.Server.Implementations.Activity /// public sealed class ActivityLogEntryPoint : IServerEntryPoint { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IInstallationManager _installationManager; private readonly ISessionManager _sessionManager; private readonly ITaskManager _taskManager; @@ -38,14 +37,12 @@ namespace Emby.Server.Implementations.Activity private readonly ILocalizationManager _localization; private readonly ISubtitleManager _subManager; private readonly IUserManager _userManager; - private readonly IDeviceManager _deviceManager; /// /// Initializes a new instance of the class. /// /// The logger. /// The session manager. - /// The device manager. /// The task manager. /// The activity manager. /// The localization manager. @@ -55,7 +52,6 @@ namespace Emby.Server.Implementations.Activity public ActivityLogEntryPoint( ILogger logger, ISessionManager sessionManager, - IDeviceManager deviceManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, @@ -65,7 +61,6 @@ namespace Emby.Server.Implementations.Activity { _logger = logger; _sessionManager = sessionManager; - _deviceManager = deviceManager; _taskManager = taskManager; _activityManager = activityManager; _localization = localization; @@ -99,52 +94,38 @@ namespace Emby.Server.Implementations.Activity _userManager.UserPolicyUpdated += OnUserPolicyUpdated; _userManager.UserLockedOut += OnUserLockedOut; - _deviceManager.CameraImageUploaded += OnCameraImageUploaded; - return Task.CompletedTask; } - private void OnCameraImageUploaded(object sender, GenericEventArgs e) + private async void OnUserLockedOut(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("CameraImageUploadedFrom"), - e.Argument.Device.Name), - Type = NotificationType.CameraImageUploaded.ToString() - }); + await CreateLogEntry(new ActivityLog( + string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserLockedOutWithName"), + e.Argument.Name), + NotificationType.UserLockedOut.ToString(), + e.Argument.Id)) + .ConfigureAwait(false); } - private void OnUserLockedOut(object sender, GenericEventArgs e) + private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("UserLockedOutWithName"), - e.Argument.Name), - Type = NotificationType.UserLockedOut.ToString(), - UserId = e.Argument.Id - }); - } - - private void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) - { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.NotificationEntryPoint.GetItemName(e.Item)), - Type = "SubtitleDownloadFailure", + "SubtitleDownloadFailure", + Guid.Empty) + { ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message - }); + }).ConfigureAwait(false); } - private void OnPlaybackStopped(object sender, PlaybackStopEventArgs e) + private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e) { var item = e.MediaInfo; @@ -167,20 +148,19 @@ namespace Emby.Server.Implementations.Activity var user = e.Users[0]; - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), - Type = GetPlaybackStoppedNotificationType(item.MediaType), - UserId = user.Id - }); + GetPlaybackStoppedNotificationType(item.MediaType), + user.Id)) + .ConfigureAwait(false); } - private void OnPlaybackStart(object sender, PlaybackProgressEventArgs e) + private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e) { var item = e.MediaInfo; @@ -203,17 +183,16 @@ namespace Emby.Server.Implementations.Activity var user = e.Users.First(); - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), - Type = GetPlaybackNotificationType(item.MediaType), - UserId = user.Id - }); + GetPlaybackNotificationType(item.MediaType), + user.Id)) + .ConfigureAwait(false); } private static string GetItemName(BaseItemDto item) @@ -263,7 +242,7 @@ namespace Emby.Server.Implementations.Activity return null; } - private void OnSessionEnded(object sender, SessionEventArgs e) + private async void OnSessionEnded(object sender, SessionEventArgs e) { var session = e.SessionInfo; @@ -272,110 +251,108 @@ namespace Emby.Server.Implementations.Activity return; } - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName), - Type = "SessionEnded", + "SessionEnded", + session.UserId) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint), - UserId = session.UserId - }); + }).ConfigureAwait(false); } - private void OnAuthenticationSucceeded(object sender, GenericEventArgs e) + private async void OnAuthenticationSucceeded(object sender, GenericEventArgs e) { var user = e.Argument.User; - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name), - Type = "AuthenticationSucceeded", + "AuthenticationSucceeded", + user.Id) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.SessionInfo.RemoteEndPoint), - UserId = user.Id - }); + }).ConfigureAwait(false); } - private void OnAuthenticationFailed(object sender, GenericEventArgs e) + private async void OnAuthenticationFailed(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username), - Type = "AuthenticationFailed", + "AuthenticationFailed", + Guid.Empty) + { + LogSeverity = LogLevel.Error, ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint), - Severity = LogLevel.Error - }); + }).ConfigureAwait(false); } - private void OnUserPolicyUpdated(object sender, GenericEventArgs e) + private async void OnUserPolicyUpdated(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name), - Type = "UserPolicyUpdated", - UserId = e.Argument.Id - }); + "UserPolicyUpdated", + e.Argument.Id)) + .ConfigureAwait(false); } - private void OnUserDeleted(object sender, GenericEventArgs e) + private async void OnUserDeleted(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name), - Type = "UserDeleted" - }); + "UserDeleted", + Guid.Empty)) + .ConfigureAwait(false); } - private void OnUserPasswordChanged(object sender, GenericEventArgs e) + private async void OnUserPasswordChanged(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name), - Type = "UserPasswordChanged", - UserId = e.Argument.Id - }); + "UserPasswordChanged", + e.Argument.Id)) + .ConfigureAwait(false); } - private void OnUserCreated(object sender, GenericEventArgs e) + private async void OnUserCreated(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name), - Type = "UserCreated", - UserId = e.Argument.Id - }); + "UserCreated", + e.Argument.Id)) + .ConfigureAwait(false); } - private void OnSessionStarted(object sender, SessionEventArgs e) + private async void OnSessionStarted(object sender, SessionEventArgs e) { var session = e.SessionInfo; @@ -384,87 +361,90 @@ namespace Emby.Server.Implementations.Activity return; } - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName), - Type = "SessionStarted", + "SessionStarted", + session.UserId) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("LabelIpAddressValue"), - session.RemoteEndPoint), - UserId = session.UserId - }); + session.RemoteEndPoint) + }).ConfigureAwait(false); } - private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e) + private async void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name), - Type = NotificationType.PluginUpdateInstalled.ToString(), + NotificationType.PluginUpdateInstalled.ToString(), + Guid.Empty) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.version), Overview = e.Argument.Item2.changelog - }); + }).ConfigureAwait(false); } - private void OnPluginUninstalled(object sender, GenericEventArgs e) + private async void OnPluginUninstalled(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name), - Type = NotificationType.PluginUninstalled.ToString() - }); + NotificationType.PluginUninstalled.ToString(), + Guid.Empty)) + .ConfigureAwait(false); } - private void OnPluginInstalled(object sender, GenericEventArgs e) + private async void OnPluginInstalled(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name), - Type = NotificationType.PluginInstalled.ToString(), + NotificationType.PluginInstalled.ToString(), + Guid.Empty) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), e.Argument.version) - }); + }).ConfigureAwait(false); } - private void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e) + private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e) { var installationInfo = e.InstallationInfo; - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name), - Type = NotificationType.InstallationFailed.ToString(), + NotificationType.InstallationFailed.ToString(), + Guid.Empty) + { ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), installationInfo.Version), Overview = e.Exception.Message - }); + }).ConfigureAwait(false); } - private void OnTaskCompleted(object sender, TaskCompletionEventArgs e) + private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e) { var result = e.Result; var task = e.Task; @@ -495,22 +475,20 @@ namespace Emby.Server.Implementations.Activity vals.Add(e.Result.LongErrorMessage); } - CreateLogEntry(new ActivityLogEntry + await CreateLogEntry(new ActivityLog( + string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), + NotificationType.TaskFailed.ToString(), + Guid.Empty) { - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("ScheduledTaskFailedWithName"), - task.Name), - Type = NotificationType.TaskFailed.ToString(), + LogSeverity = LogLevel.Error, Overview = string.Join(Environment.NewLine, vals), - ShortOverview = runningTime, - Severity = LogLevel.Error - }); + ShortOverview = runningTime + }).ConfigureAwait(false); } } - private void CreateLogEntry(ActivityLogEntry entry) - => _activityManager.Create(entry); + private async Task CreateLogEntry(ActivityLog entry) + => await _activityManager.CreateAsync(entry).ConfigureAwait(false); /// public void Dispose() @@ -537,8 +515,6 @@ namespace Emby.Server.Implementations.Activity _userManager.UserDeleted -= OnUserDeleted; _userManager.UserPolicyUpdated -= OnUserPolicyUpdated; _userManager.UserLockedOut -= OnUserLockedOut; - - _deviceManager.CameraImageUploaded -= OnCameraImageUploaded; } /// @@ -566,7 +542,7 @@ namespace Emby.Server.Implementations.Activity { int months = days / DaysInMonth; values.Add(CreateValueString(months, "month")); - days %= DaysInMonth; + days = days % DaysInMonth; } // Number of days diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs deleted file mode 100644 index 81bebae3d2..0000000000 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Querying; - -namespace Emby.Server.Implementations.Activity -{ - /// - /// The activity log manager. - /// - public class ActivityManager : IActivityManager - { - private readonly IActivityRepository _repo; - private readonly IUserManager _userManager; - - /// - /// Initializes a new instance of the class. - /// - /// The activity repository. - /// The user manager. - public ActivityManager(IActivityRepository repo, IUserManager userManager) - { - _repo = repo; - _userManager = userManager; - } - - /// - public event EventHandler> EntryCreated; - - public void Create(ActivityLogEntry entry) - { - entry.Date = DateTime.UtcNow; - - _repo.Create(entry); - - EntryCreated?.Invoke(this, new GenericEventArgs(entry)); - } - - /// - public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) - { - var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit); - - foreach (var item in result.Items) - { - if (item.UserId == Guid.Empty) - { - continue; - } - - var user = _userManager.GetUserById(item.UserId); - - if (user != null) - { - var dto = _userManager.GetUserDto(user); - item.UserPrimaryImageTag = dto.PrimaryImageTag; - } - } - - return result; - } - - /// - public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) - { - return GetActivityLogEntries(minDate, null, startIndex, limit); - } - } -} diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs deleted file mode 100644 index 22796ba3f8..0000000000 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using Emby.Server.Implementations.Data; -using MediaBrowser.Controller; -using MediaBrowser.Model.Activity; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Querying; -using Microsoft.Extensions.Logging; -using SQLitePCL.pretty; - -namespace Emby.Server.Implementations.Activity -{ - /// - /// The activity log repository. - /// - public class ActivityRepository : BaseSqliteRepository, IActivityRepository - { - private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; - - private readonly IFileSystem _fileSystem; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The server application paths. - /// The filesystem. - public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) - : base(logger) - { - DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); - _fileSystem = fileSystem; - } - - /// - /// Initializes the . - /// - public void Initialize() - { - try - { - InitializeInternal(); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error loading database file. Will reset and retry."); - - _fileSystem.DeleteFile(DbFilePath); - - InitializeInternal(); - } - } - - private void InitializeInternal() - { - using var connection = GetConnection(); - connection.RunQueries(new[] - { - "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)", - "drop index if exists idx_ActivityLogEntries" - }); - - TryMigrate(connection); - } - - private void TryMigrate(ManagedConnection connection) - { - try - { - if (TableExists(connection, "ActivityLogEntries")) - { - connection.RunQueries(new[] - { - "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries", - "drop table if exists ActivityLogEntries" - }); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error migrating activity log database"); - } - } - - /// - public void Create(ActivityLogEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - using var connection = GetConnection(); - connection.RunInTransaction(db => - { - using var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"); - statement.TryBind("@Name", entry.Name); - - statement.TryBind("@Overview", entry.Overview); - statement.TryBind("@ShortOverview", entry.ShortOverview); - statement.TryBind("@Type", entry.Type); - statement.TryBind("@ItemId", entry.ItemId); - - if (entry.UserId.Equals(Guid.Empty)) - { - statement.TryBindNull("@UserId"); - } - else - { - statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); - } - - statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); - statement.TryBind("@LogSeverity", entry.Severity.ToString()); - - statement.MoveNext(); - }, TransactionMode); - } - - /// - /// Adds the provided to this repository. - /// - /// The activity log entry. - /// If entry is null. - public void Update(ActivityLogEntry entry) - { - if (entry == null) - { - throw new ArgumentNullException(nameof(entry)); - } - - using var connection = GetConnection(); - connection.RunInTransaction(db => - { - using var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"); - statement.TryBind("@Id", entry.Id); - - statement.TryBind("@Name", entry.Name); - statement.TryBind("@Overview", entry.Overview); - statement.TryBind("@ShortOverview", entry.ShortOverview); - statement.TryBind("@Type", entry.Type); - statement.TryBind("@ItemId", entry.ItemId); - - if (entry.UserId.Equals(Guid.Empty)) - { - statement.TryBindNull("@UserId"); - } - else - { - statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture)); - } - - statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); - statement.TryBind("@LogSeverity", entry.Severity.ToString()); - - statement.MoveNext(); - }, TransactionMode); - } - - /// - public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) - { - var commandText = BaseActivitySelectText; - var whereClauses = new List(); - - if (minDate.HasValue) - { - whereClauses.Add("DateCreated>=@DateCreated"); - } - - if (hasUserId.HasValue) - { - whereClauses.Add(hasUserId.Value ? "UserId not null" : "UserId is null"); - } - - var whereTextWithoutPaging = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - if (startIndex.HasValue && startIndex.Value > 0) - { - var pagingWhereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - whereClauses.Add( - string.Format( - CultureInfo.InvariantCulture, - "Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})", - pagingWhereText, - startIndex.Value)); - } - - var whereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - commandText += whereText; - - commandText += " ORDER BY DateCreated DESC"; - - if (limit.HasValue) - { - commandText += " LIMIT " + limit.Value.ToString(CultureInfo.InvariantCulture); - } - - var statementTexts = new[] - { - commandText, - "select count (Id) from ActivityLog" + whereTextWithoutPaging - }; - - var list = new List(); - var result = new QueryResult(); - - using var connection = GetConnection(true); - connection.RunInTransaction( - db => - { - var statements = PrepareAll(db, statementTexts).ToList(); - - using (var statement = statements[0]) - { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } - - list.AddRange(statement.ExecuteQuery().Select(GetEntry)); - } - - using (var statement = statements[1]) - { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } - - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); - } - }, - ReadTransactionMode); - - result.Items = list; - return result; - } - - private static ActivityLogEntry GetEntry(IReadOnlyList reader) - { - var index = 0; - - var info = new ActivityLogEntry - { - Id = reader[index].ToInt64() - }; - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Name = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Overview = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.ShortOverview = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Type = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.ItemId = reader[index].ToString(); - } - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.UserId = new Guid(reader[index].ToString()); - } - - index++; - info.Date = reader[index].ReadDateTime(); - - index++; - if (reader[index].SQLiteType != SQLiteType.Null) - { - info.Severity = Enum.Parse(reader[index].ToString(), true); - } - - return info; - } - } -} diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ffc916b980..e6410f8570 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -22,7 +22,6 @@ using Emby.Dlna.Ssdp; using Emby.Drawing; using Emby.Notifications; using Emby.Photos; -using Emby.Server.Implementations.Activity; using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; @@ -44,7 +43,6 @@ using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Services; using Emby.Server.Implementations.Session; -using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using MediaBrowser.Api; @@ -82,7 +80,6 @@ using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; -using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; @@ -94,7 +91,6 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.TheTvdb; @@ -102,11 +98,10 @@ using MediaBrowser.Providers.Subtitles; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; using Prometheus.DotNetRuntime; +using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations { @@ -503,32 +498,8 @@ namespace Emby.Server.Implementations RegisterServices(serviceCollection); } - public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next) - { - if (!context.WebSockets.IsWebSocketRequest) - { - await next().ConfigureAwait(false); - return; - } - - await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false); - } - - public async Task ExecuteHttpHandlerAsync(HttpContext context, Func next) - { - if (context.WebSockets.IsWebSocketRequest) - { - await next().ConfigureAwait(false); - return; - } - - var request = context.Request; - var response = context.Response; - var localPath = context.Request.Path.ToString(); - - var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger()); - await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); - } + public Task ExecuteHttpHandlerAsync(HttpContext context, Func next) + => _httpServer.RequestHandler(context); /// /// Registers services/resources with the service collection that will be available via DI. @@ -546,13 +517,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - // TODO: Remove support for injecting ILogger completely - serviceCollection.AddSingleton((provider) => - { - Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger"); - return Logger; - }); - serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton(); @@ -621,7 +585,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -663,9 +626,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -696,7 +656,6 @@ namespace Emby.Server.Implementations ((SqliteDisplayPreferencesRepository)Resolve()).Initialize(); ((AuthenticationRepository)Resolve()).Initialize(); ((SqliteUserRepository)Resolve()).Initialize(); - ((ActivityRepository)Resolve()).Initialize(); SetStaticProperties(); @@ -1150,9 +1109,6 @@ namespace Emby.Server.Implementations ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, - HttpServerPortNumber = HttpPort, - SupportsHttps = SupportsHttps, - HttpsPortNumber = HttpsPort, OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystemDisplayName = OperatingSystem.Name, CanSelfRestart = CanSelfRestart, @@ -1188,23 +1144,22 @@ namespace Emby.Server.Implementations }; } - public bool EnableHttps => SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps; + /// + public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; - public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; - - public async Task GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false) + /// + public async Task GetLocalApiUrl(CancellationToken cancellationToken) { try { // Return the first matched address, if found, or the first known local address var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false); - - foreach (var address in addresses) + if (addresses.Count == 0) { - return GetLocalApiUrl(address, forceHttp); + return null; } - return null; + return GetLocalApiUrl(addresses.First()); } catch (Exception ex) { @@ -1231,7 +1186,7 @@ namespace Emby.Server.Implementations } /// - public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp = false) + public string GetLocalApiUrl(IPAddress ipAddress) { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { @@ -1241,29 +1196,30 @@ namespace Emby.Server.Implementations str.CopyTo(span.Slice(1)); span[^1] = ']'; - return GetLocalApiUrl(span, forceHttp); + return GetLocalApiUrl(span); } - return GetLocalApiUrl(ipAddress.ToString(), forceHttp); + return GetLocalApiUrl(ipAddress.ToString()); } - /// - public string GetLocalApiUrl(ReadOnlySpan host, bool forceHttp = false) + /// + public string GetLoopbackHttpApiUrl() { - var url = new StringBuilder(64); - bool useHttps = EnableHttps && !forceHttp; - url.Append(useHttps ? "https://" : "http://") - .Append(host) - .Append(':') - .Append(useHttps ? HttpsPort : HttpPort); + return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); + } - string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; - if (baseUrl.Length != 0) + /// + public string GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null) + { + // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does + // not. For consistency, always trim the trailing slash. + return new UriBuilder { - url.Append(baseUrl); - } - - return url.ToString(); + Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), + Host = host.ToString(), + Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), + Path = ServerConfigurationManager.Configuration.BaseUrl + }.ToString().TrimEnd('/'); } public Task> GetLocalIpAddresses(CancellationToken cancellationToken) @@ -1297,7 +1253,7 @@ namespace Emby.Server.Implementations } } - var valid = await IsIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false); + var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false); if (valid) { resultList.Add(address); @@ -1331,7 +1287,7 @@ namespace Emby.Server.Implementations private readonly ConcurrentDictionary _validAddressResults = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private async Task IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) + private async Task IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) { if (address.Equals(IPAddress.Loopback) || address.Equals(IPAddress.IPv6Loopback)) @@ -1339,8 +1295,7 @@ namespace Emby.Server.Implementations return true; } - var apiUrl = GetLocalApiUrl(address); - apiUrl += "/system/ping"; + var apiUrl = GetLocalApiUrl(address) + "/system/ping"; if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult)) { diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs index 96096e142a..7f7c6a0be4 100644 --- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs +++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs @@ -31,18 +31,18 @@ namespace Emby.Server.Implementations.Browser /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored. /// /// The application host. - /// The URL. - private static void TryOpenUrl(IServerApplicationHost appHost, string url) + /// The URL to open, relative to the server base URL. + private static void TryOpenUrl(IServerApplicationHost appHost, string relativeUrl) { try { string baseUrl = appHost.GetLocalApiUrl("localhost"); - appHost.LaunchUrl(baseUrl + url); + appHost.LaunchUrl(baseUrl + relativeUrl); } catch (Exception ex) { var logger = appHost.Resolve(); - logger?.LogError(ex, "Failed to open browser window with URL {URL}", url); + logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl); } } } diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index a6eaf2d0a3..305e67e8c3 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -193,12 +193,6 @@ namespace Emby.Server.Implementations.Configuration changed = true; } - if (!config.CameraUploadUpgraded) - { - config.CameraUploadUpgraded = true; - changed = true; - } - if (!config.CollectionsUpgraded) { config.CollectionsUpgraded = true; diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index db7c35a7c8..dea9b6682a 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.Updates; -using MediaBrowser.Providers.Music; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; namespace Emby.Server.Implementations diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 22955850ab..6ee6230fc6 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -375,5 +375,15 @@ namespace Emby.Server.Implementations.Data return userData; } + + /// + /// + /// There is nothing to dispose here since and + /// are managed by . + /// See . + /// + protected override void Dispose(bool dispose) + { + } } } diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 579cb895e4..2283f2433a 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -5,27 +5,18 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Security; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Devices; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; using MediaBrowser.Model.Users; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Devices { @@ -33,38 +24,23 @@ namespace Emby.Server.Implementations.Devices { private readonly IJsonSerializer _json; private readonly IUserManager _userManager; - private readonly IFileSystem _fileSystem; - private readonly ILibraryMonitor _libraryMonitor; private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localizationManager; private readonly IAuthenticationRepository _authRepo; private readonly Dictionary _capabilitiesCache; public event EventHandler>> DeviceOptionsUpdated; - public event EventHandler> CameraImageUploaded; - - private readonly object _cameraUploadSyncLock = new object(); private readonly object _capabilitiesSyncLock = new object(); public DeviceManager( IAuthenticationRepository authRepo, IJsonSerializer json, - ILibraryManager libraryManager, - ILocalizationManager localizationManager, IUserManager userManager, - IFileSystem fileSystem, - ILibraryMonitor libraryMonitor, IServerConfigurationManager config) { _json = json; _userManager = userManager; - _fileSystem = fileSystem; - _libraryMonitor = libraryMonitor; _config = config; - _libraryManager = libraryManager; - _localizationManager = localizationManager; _authRepo = authRepo; _capabilitiesCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -194,172 +170,6 @@ namespace Emby.Server.Implementations.Devices return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N", CultureInfo.InvariantCulture)); } - public ContentUploadHistory GetCameraUploadHistory(string deviceId) - { - var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); - - lock (_cameraUploadSyncLock) - { - try - { - return _json.DeserializeFromFile(path); - } - catch (IOException) - { - return new ContentUploadHistory - { - DeviceId = deviceId - }; - } - } - } - - public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file) - { - var device = GetDevice(deviceId, false); - var uploadPathInfo = GetUploadPath(device); - - var path = uploadPathInfo.Item1; - - if (!string.IsNullOrWhiteSpace(file.Album)) - { - path = Path.Combine(path, _fileSystem.GetValidFilename(file.Album)); - } - - path = Path.Combine(path, file.Name); - path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg"); - - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - await EnsureLibraryFolder(uploadPathInfo.Item2, uploadPathInfo.Item3).ConfigureAwait(false); - - _libraryMonitor.ReportFileSystemChangeBeginning(path); - - try - { - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - await stream.CopyToAsync(fs).ConfigureAwait(false); - } - - AddCameraUpload(deviceId, file); - } - finally - { - _libraryMonitor.ReportFileSystemChangeComplete(path, true); - } - - if (CameraImageUploaded != null) - { - CameraImageUploaded?.Invoke(this, new GenericEventArgs - { - Argument = new CameraImageUploadInfo - { - Device = device, - FileInfo = file - } - }); - } - } - - private void AddCameraUpload(string deviceId, LocalFileInfo file) - { - var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json"); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - lock (_cameraUploadSyncLock) - { - ContentUploadHistory history; - - try - { - history = _json.DeserializeFromFile(path); - } - catch (IOException) - { - history = new ContentUploadHistory - { - DeviceId = deviceId - }; - } - - history.DeviceId = deviceId; - - var list = history.FilesUploaded.ToList(); - list.Add(file); - history.FilesUploaded = list.ToArray(); - - _json.SerializeToFile(history, path); - } - } - - internal Task EnsureLibraryFolder(string path, string name) - { - var existingFolders = _libraryManager - .RootFolder - .Children - .OfType() - .Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path)) - .ToList(); - - if (existingFolders.Count > 0) - { - return Task.CompletedTask; - } - - Directory.CreateDirectory(path); - - var libraryOptions = new LibraryOptions - { - PathInfos = new[] { new MediaPathInfo { Path = path } }, - EnablePhotos = true, - EnableRealtimeMonitor = false, - SaveLocalMetadata = true - }; - - if (string.IsNullOrWhiteSpace(name)) - { - name = _localizationManager.GetLocalizedString("HeaderCameraUploads"); - } - - return _libraryManager.AddVirtualFolder(name, CollectionType.HomeVideos, libraryOptions, true); - } - - private Tuple GetUploadPath(DeviceInfo device) - { - var config = _config.GetUploadOptions(); - var path = config.CameraUploadPath; - - if (string.IsNullOrWhiteSpace(path)) - { - path = DefaultCameraUploadsPath; - } - - var topLibraryPath = path; - - if (config.EnableCameraUploadSubfolders) - { - path = Path.Combine(path, _fileSystem.GetValidFilename(device.Name)); - } - - return new Tuple(path, topLibraryPath, null); - } - - internal string GetUploadsPath() - { - var config = _config.GetUploadOptions(); - var path = config.CameraUploadPath; - - if (string.IsNullOrWhiteSpace(path)) - { - path = DefaultCameraUploadsPath; - } - - return path; - } - - private string DefaultCameraUploadsPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); - public bool CanAccessDevice(User user, string deviceId) { if (user == null) @@ -399,102 +209,4 @@ namespace Emby.Server.Implementations.Devices return policy.EnabledDevices.Contains(id, StringComparer.OrdinalIgnoreCase); } } - - public class DeviceManagerEntryPoint : IServerEntryPoint - { - private readonly DeviceManager _deviceManager; - private readonly IServerConfigurationManager _config; - private ILogger _logger; - - public DeviceManagerEntryPoint( - IDeviceManager deviceManager, - IServerConfigurationManager config, - ILogger logger) - { - _deviceManager = (DeviceManager)deviceManager; - _config = config; - _logger = logger; - } - - public async Task RunAsync() - { - if (!_config.Configuration.CameraUploadUpgraded && _config.Configuration.IsStartupWizardCompleted) - { - var path = _deviceManager.GetUploadsPath(); - - if (Directory.Exists(path)) - { - try - { - await _deviceManager.EnsureLibraryFolder(path, null).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error creating camera uploads library"); - } - - _config.Configuration.CameraUploadUpgraded = true; - _config.SaveConfiguration(); - } - } - } - - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~DeviceManagerEntryPoint() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } - - public class DevicesConfigStore : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new ConfigurationStore[] - { - new ConfigurationStore - { - Key = "devices", - ConfigurationType = typeof(DevicesOptions) - } - }; - } - } - - public static class UploadConfigExtension - { - public static DevicesOptions GetUploadOptions(this IConfigurationManager config) - { - return config.GetConfiguration("devices"); - } - } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 44fc932e39..e95228b706 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -1,4 +1,4 @@ - + @@ -34,10 +34,10 @@ - - - - + + + + diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 37d7fd4799..878cee23c4 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.EntryPoints .Append(config.PublicHttpsPort).Append(Separator) .Append(_appHost.HttpPort).Append(Separator) .Append(_appHost.HttpsPort).Append(Separator) - .Append(_appHost.EnableHttps).Append(Separator) + .Append(_appHost.ListenWithHttps).Append(Separator) .Append(config.EnableRemoteAccess).Append(Separator) .ToString(); } @@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.EntryPoints { yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort); - if (_appHost.EnableHttps) + if (_appHost.ListenWithHttps) { yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort); } diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 77878eacc9..9554b9f462 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -6,11 +6,12 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Sockets; +using System.Net.WebSockets; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Services; +using Emby.Server.Implementations.SocketSharp; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -22,15 +23,17 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using ServiceStack.Text.Jsv; namespace Emby.Server.Implementations.HttpServer { - public class HttpListenerHost : IHttpServer, IDisposable + public class HttpListenerHost : IHttpServer { /// /// The key for a setting that specifies the default redirect path @@ -39,17 +42,17 @@ namespace Emby.Server.Implementations.HttpServer public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath"; private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; private readonly IServerApplicationHost _appHost; private readonly IJsonSerializer _jsonSerializer; private readonly IXmlSerializer _xmlSerializer; - private readonly IHttpListener _socketListener; private readonly Func> _funcParseFn; private readonly string _defaultRedirectPath; private readonly string _baseUrlPrefix; + private readonly Dictionary _serviceOperationsMap = new Dictionary(); - private readonly List _webSocketConnections = new List(); private readonly IHostEnvironment _hostEnvironment; private IWebSocketListener[] _webSocketListeners = Array.Empty(); @@ -63,10 +66,10 @@ namespace Emby.Server.Implementations.HttpServer INetworkManager networkManager, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, - IHttpListener socketListener, ILocalizationManager localizationManager, ServiceController serviceController, - IHostEnvironment hostEnvironment) + IHostEnvironment hostEnvironment, + ILoggerFactory loggerFactory) { _appHost = applicationHost; _logger = logger; @@ -76,11 +79,9 @@ namespace Emby.Server.Implementations.HttpServer _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; - _socketListener = socketListener; ServiceController = serviceController; - - _socketListener.WebSocketConnected = OnWebSocketConnected; _hostEnvironment = hostEnvironment; + _loggerFactory = loggerFactory; _funcParseFn = t => s => JsvReader.GetParseFn(t)(s); @@ -172,38 +173,6 @@ namespace Emby.Server.Implementations.HttpServer return attributes; } - private void OnWebSocketConnected(WebSocketConnectEventArgs e) - { - if (_disposed) - { - return; - } - - var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) - { - OnReceive = ProcessWebSocketMessageReceived, - Url = e.Url, - QueryString = e.QueryString - }; - - connection.Closed += OnConnectionClosed; - - lock (_webSocketConnections) - { - _webSocketConnections.Add(connection); - } - - WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); - } - - private void OnConnectionClosed(object sender, EventArgs e) - { - lock (_webSocketConnections) - { - _webSocketConnections.Remove((IWebSocketConnection)sender); - } - } - private static Exception GetActualException(Exception ex) { if (ex is AggregateException agg) @@ -241,16 +210,8 @@ namespace Emby.Server.Implementations.HttpServer } } - private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog) + private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog, bool ignoreStackTrace) { - bool ignoreStackTrace = - ex is SocketException - || ex is IOException - || ex is OperationCanceledException - || ex is SecurityException - || ex is AuthenticationException - || ex is FileNotFoundException; - if (ignoreStackTrace) { _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog); @@ -289,32 +250,6 @@ namespace Emby.Server.Implementations.HttpServer .Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase); } - /// - /// Shut down the Web Service - /// - public void Stop() - { - List connections; - - lock (_webSocketConnections) - { - connections = _webSocketConnections.ToList(); - _webSocketConnections.Clear(); - } - - foreach (var connection in connections) - { - try - { - connection.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error disposing connection"); - } - } - } - public static string RemoveQueryStringByKey(string url, string key) { var uri = new Uri(url); @@ -424,33 +359,52 @@ namespace Emby.Server.Implementations.HttpServer return true; } + /// + /// Validate a connection from a remote IP address to a URL to see if a redirection to HTTPS is required. + /// + /// True if the request is valid, or false if the request is not valid and an HTTPS redirect is required. private bool ValidateSsl(string remoteIp, string urlString) { - if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy) + if (_config.Configuration.RequireHttps + && _appHost.ListenWithHttps + && !urlString.Contains("https://", StringComparison.OrdinalIgnoreCase)) { - if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1) + // These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected + if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1 + || urlString.IndexOf("dlna/", StringComparison.OrdinalIgnoreCase) != -1) { - // These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected - if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1 - || urlString.IndexOf("dlna/", StringComparison.OrdinalIgnoreCase) != -1) - { - return true; - } + return true; + } - if (!_networkManager.IsInLocalNetwork(remoteIp)) - { - return false; - } + if (!_networkManager.IsInLocalNetwork(remoteIp)) + { + return false; } } return true; } + /// + public Task RequestHandler(HttpContext context) + { + if (context.WebSockets.IsWebSocketRequest) + { + return WebSocketRequestHandler(context); + } + + var request = context.Request; + var response = context.Response; + var localPath = context.Request.Path.ToString(); + + var req = new WebSocketSharpRequest(request, response, request.Path, _logger); + return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted); + } + /// /// Overridable method that can be used to implement a custom handler. /// - public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken) + private async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken) { var stopWatch = new Stopwatch(); stopWatch.Start(); @@ -493,9 +447,10 @@ namespace Emby.Server.Implementations.HttpServer if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) { httpRes.StatusCode = 200; - httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); - httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + foreach(var (key, value) in GetDefaultCorsHeaders(httpReq)) + { + httpRes.Headers.Add(key, value); + } httpRes.ContentType = "text/plain"; await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); return; @@ -547,14 +502,24 @@ namespace Emby.Server.Implementations.HttpServer httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); } - // Do not handle 500 server exceptions manually when in development mode - // The framework-defined development exception page will be returned instead - if (statusCode == 500 && _hostEnvironment.IsDevelopment()) + bool ignoreStackTrace = + requestInnerEx is SocketException + || requestInnerEx is IOException + || requestInnerEx is OperationCanceledException + || requestInnerEx is SecurityException + || requestInnerEx is AuthenticationException + || requestInnerEx is FileNotFoundException; + + // Do not handle 500 server exceptions manually when in development mode. + // Instead, re-throw the exception so it can be handled by the DeveloperExceptionPageMiddleware. + // However, do not use the DeveloperExceptionPageMiddleware when the stack trace should be ignored, + // because it will log the stack trace when it handles the exception. + if (statusCode == 500 && !ignoreStackTrace && _hostEnvironment.IsDevelopment()) { throw; } - await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false); + await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog, ignoreStackTrace).ConfigureAwait(false); } catch (Exception handlerException) { @@ -583,6 +548,68 @@ namespace Emby.Server.Implementations.HttpServer } } + private async Task WebSocketRequestHandler(HttpContext context) + { + if (_disposed) + { + return; + } + + try + { + _logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress); + + WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); + + var connection = new WebSocketConnection( + _loggerFactory.CreateLogger(), + webSocket, + context.Connection.RemoteIpAddress, + context.Request.Query) + { + OnReceive = ProcessWebSocketMessageReceived + }; + + WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); + + await connection.ProcessAsync().ConfigureAwait(false); + _logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress); + } + catch (Exception ex) // Otherwise ASP.Net will ignore the exception + { + _logger.LogError(ex, "WS {IP} WebSocketRequestHandler error", context.Connection.RemoteIpAddress); + if (!context.Response.HasStarted) + { + context.Response.StatusCode = 500; + } + } + } + + /// + /// Get the default CORS headers + /// + /// + /// + public IDictionary GetDefaultCorsHeaders(IRequest req) + { + var origin = req.Headers["Origin"]; + if (origin == StringValues.Empty) + { + origin = req.Headers["Host"]; + if (origin == StringValues.Empty) + { + origin = "*"; + } + } + + var headers = new Dictionary(); + headers.Add("Access-Control-Allow-Origin", origin); + headers.Add("Access-Control-Allow-Credentials", "true"); + headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization, Cookie"); + return headers; + } + // Entry point for HttpListener public ServiceHandler GetServiceHandler(IHttpRequest httpReq) { @@ -629,7 +656,7 @@ namespace Emby.Server.Implementations.HttpServer ResponseFilters = new Action[] { - new ResponseFilter(_logger).FilterResponse + new ResponseFilter(this, _logger).FilterResponse }; } @@ -690,11 +717,6 @@ namespace Emby.Server.Implementations.HttpServer return _jsonSerializer.DeserializeFromStreamAsync(stream, type); } - public Task ProcessWebSocketRequest(HttpContext context) - { - return _socketListener.ProcessWebSocketRequest(context); - } - private string NormalizeEmbyRoutePath(string path) { _logger.LogDebug("Normalizing /emby route"); @@ -713,28 +735,6 @@ namespace Emby.Server.Implementations.HttpServer return _baseUrlPrefix + NormalizeUrlPath(path); } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - Stop(); - } - - _disposed = true; - } - /// /// Processes the web socket message received. /// @@ -746,8 +746,6 @@ namespace Emby.Server.Implementations.HttpServer return Task.CompletedTask; } - _logger.LogDebug("Websocket message received: {0}", result.MessageType); - IEnumerable GetTasks() { foreach (var x in _webSocketListeners) diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs deleted file mode 100644 index 5015937256..0000000000 --- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs +++ /dev/null @@ -1,39 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Implementations.Net; -using MediaBrowser.Model.Services; -using Microsoft.AspNetCore.Http; - -namespace Emby.Server.Implementations.HttpServer -{ - public interface IHttpListener : IDisposable - { - /// - /// Gets or sets the error handler. - /// - /// The error handler. - Func ErrorHandler { get; set; } - - /// - /// Gets or sets the request handler. - /// - /// The request handler. - Func RequestHandler { get; set; } - - /// - /// Gets or sets the web socket handler. - /// - /// The web socket handler. - Action WebSocketConnected { get; set; } - - /// - /// Stops this instance. - /// - Task Stop(); - - Task ProcessWebSocketRequest(HttpContext ctx); - } -} diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 4089aa578e..85c3db9b20 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Text; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -13,14 +15,17 @@ namespace Emby.Server.Implementations.HttpServer /// public class ResponseFilter { + private readonly IHttpServer _server; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// + /// The HTTP server. /// The logger. - public ResponseFilter(ILogger logger) + public ResponseFilter(IHttpServer server, ILogger logger) { + _server = server; _logger = logger; } @@ -32,10 +37,16 @@ namespace Emby.Server.Implementations.HttpServer /// The dto. public void FilterResponse(IRequest req, HttpResponse res, object dto) { + foreach(var (key, value) in _server.GetDefaultCorsHeaders(req)) + { + res.Headers.Add(key, value); + } // Try to prevent compatibility view - res.Headers.Add("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization"); - res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - res.Headers.Add("Access-Control-Allow-Origin", "*"); + res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " + + "Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " + + "Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " + + "Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " + + "X-Emby-Authorization"); if (dto is Exception exception) { diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 2292d86a4a..095725c504 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -1,15 +1,18 @@ -using System; +#nullable enable + +using System; +using System.Buffers; +using System.IO.Pipelines; +using System.Net; using System.Net.WebSockets; -using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Net; +using MediaBrowser.Common.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using UtfUnknown; namespace Emby.Server.Implementations.HttpServer { @@ -24,69 +27,50 @@ namespace Emby.Server.Implementations.HttpServer private readonly ILogger _logger; /// - /// The json serializer. + /// The json serializer options. /// - private readonly IJsonSerializer _jsonSerializer; + private readonly JsonSerializerOptions _jsonOptions; /// /// The socket. /// - private readonly IWebSocket _socket; + private readonly WebSocket _socket; /// /// Initializes a new instance of the class. /// + /// The logger. /// The socket. /// The remote end point. - /// The json serializer. - /// The logger. - /// socket - public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger) + /// The query. + public WebSocketConnection( + ILogger logger, + WebSocket socket, + IPAddress? remoteEndPoint, + IQueryCollection query) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } - - if (string.IsNullOrEmpty(remoteEndPoint)) - { - throw new ArgumentNullException(nameof(remoteEndPoint)); - } - - if (jsonSerializer == null) - { - throw new ArgumentNullException(nameof(jsonSerializer)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - - Id = Guid.NewGuid(); - _jsonSerializer = jsonSerializer; - _socket = socket; - _socket.OnReceiveBytes = OnReceiveInternal; - - RemoteEndPoint = remoteEndPoint; _logger = logger; + _socket = socket; + RemoteEndPoint = remoteEndPoint; + QueryString = query; - socket.Closed += OnSocketClosed; + _jsonOptions = JsonDefaults.GetOptions(); + LastActivityDate = DateTime.Now; } /// - public event EventHandler Closed; + public event EventHandler? Closed; /// /// Gets or sets the remote end point. /// - public string RemoteEndPoint { get; private set; } + public IPAddress? RemoteEndPoint { get; } /// /// Gets or sets the receive action. /// /// The receive action. - public Func OnReceive { get; set; } + public Func? OnReceive { get; set; } /// /// Gets the last activity date. @@ -94,23 +78,11 @@ namespace Emby.Server.Implementations.HttpServer /// The last activity date. public DateTime LastActivityDate { get; private set; } - /// - /// Gets the id. - /// - /// The id. - public Guid Id { get; private set; } - - /// - /// Gets or sets the URL. - /// - /// The URL. - public string Url { get; set; } - /// /// Gets or sets the query string. /// /// The query string. - public IQueryCollection QueryString { get; set; } + public IQueryCollection QueryString { get; } /// /// Gets the state. @@ -118,70 +90,6 @@ namespace Emby.Server.Implementations.HttpServer /// The state. public WebSocketState State => _socket.State; - void OnSocketClosed(object sender, EventArgs e) - { - Closed?.Invoke(this, EventArgs.Empty); - } - - /// - /// Called when [receive]. - /// - /// The bytes. - private void OnReceiveInternal(byte[] bytes) - { - LastActivityDate = DateTime.UtcNow; - - if (OnReceive == null) - { - return; - } - var charset = CharsetDetector.DetectFromBytes(bytes).Detected?.EncodingName; - - if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase)) - { - OnReceiveInternal(Encoding.UTF8.GetString(bytes, 0, bytes.Length)); - } - else - { - OnReceiveInternal(Encoding.ASCII.GetString(bytes, 0, bytes.Length)); - } - } - - private void OnReceiveInternal(string message) - { - LastActivityDate = DateTime.UtcNow; - - if (!message.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - { - // This info is useful sometimes but also clogs up the log - _logger.LogDebug("Received web socket message that is not a json structure: {message}", message); - return; - } - - if (OnReceive == null) - { - return; - } - - try - { - var stub = (WebSocketMessage)_jsonSerializer.DeserializeFromString(message, typeof(WebSocketMessage)); - - var info = new WebSocketMessageInfo - { - MessageType = stub.MessageType, - Data = stub.Data?.ToString(), - Connection = this - }; - - OnReceive(info); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error processing web socket message"); - } - } - /// /// Sends a message asynchronously. /// @@ -189,67 +97,128 @@ namespace Emby.Server.Implementations.HttpServer /// The message. /// The cancellation token. /// Task. - /// message public Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken) { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var json = _jsonSerializer.SerializeToString(message); - - return SendAsync(json, cancellationToken); - } - - /// - /// Sends a message asynchronously. - /// - /// The buffer. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] buffer, CancellationToken cancellationToken) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return _socket.SendAsync(buffer, true, cancellationToken); + var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); + return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken); } /// - public Task SendAsync(string text, CancellationToken cancellationToken) + public async Task ProcessAsync(CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(text)) + var pipe = new Pipe(); + var writer = pipe.Writer; + + ValueWebSocketReceiveResult receiveresult; + do { - throw new ArgumentNullException(nameof(text)); + // Allocate at least 512 bytes from the PipeWriter + Memory memory = writer.GetMemory(512); + try + { + receiveresult = await _socket.ReceiveAsync(memory, cancellationToken); + } + catch (WebSocketException ex) + { + _logger.LogWarning("WS {IP} error receiving data: {Message}", RemoteEndPoint, ex.Message); + break; + } + + int bytesRead = receiveresult.Count; + if (bytesRead == 0) + { + break; + } + + // Tell the PipeWriter how much was read from the Socket + writer.Advance(bytesRead); + + // Make the data available to the PipeReader + FlushResult flushResult = await writer.FlushAsync(); + if (flushResult.IsCompleted) + { + // The PipeReader stopped reading + break; + } + + LastActivityDate = DateTime.UtcNow; + + if (receiveresult.EndOfMessage) + { + await ProcessInternal(pipe.Reader).ConfigureAwait(false); + } + } while ( + (_socket.State == WebSocketState.Open || _socket.State == WebSocketState.Connecting) + && receiveresult.MessageType != WebSocketMessageType.Close); + + Closed?.Invoke(this, EventArgs.Empty); + + if (_socket.State == WebSocketState.Open + || _socket.State == WebSocketState.CloseReceived + || _socket.State == WebSocketState.CloseSent) + { + await _socket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + string.Empty, + cancellationToken).ConfigureAwait(false); } - - cancellationToken.ThrowIfCancellationRequested(); - - return _socket.SendAsync(text, true, cancellationToken); } - /// - public void Dispose() + private async Task ProcessInternal(PipeReader reader) { - Dispose(true); - GC.SuppressFinalize(this); - } + ReadResult result = await reader.ReadAsync().ConfigureAwait(false); + ReadOnlySequence buffer = result.Buffer; - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) + if (OnReceive == null) { - _socket.Dispose(); + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); + return; } + + WebSocketMessage stub; + try + { + + if (buffer.IsSingleSegment) + { + stub = JsonSerializer.Deserialize>(buffer.FirstSpan, _jsonOptions); + } + else + { + var buf = ArrayPool.Shared.Rent(Convert.ToInt32(buffer.Length)); + try + { + buffer.CopyTo(buf); + stub = JsonSerializer.Deserialize>(buf, _jsonOptions); + } + finally + { + ArrayPool.Shared.Return(buf); + } + } + } + catch (JsonException ex) + { + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); + _logger.LogError(ex, "Error processing web socket message"); + return; + } + + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); + + _logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub); + + var info = new WebSocketMessageInfo + { + MessageType = stub.MessageType, + Data = stub.Data?.ToString(), // Data can be null + Connection = this + }; + + await OnReceive(info).ConfigureAwait(false); } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 85b1b6e323..6c9ba7c272 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// public class MusicAlbumResolver : ItemResolver { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; @@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// The logger. /// The file system. /// The library manager. - public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager) + public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager) { _logger = logger; _fileSystem = fileSystem; diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 681db4896e..5f5cd0e928 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// public class MusicArtistResolver : ItemResolver { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; @@ -23,12 +23,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// /// Initializes a new instance of the class. /// - /// The logger. + /// The logger for the created instances. /// The file system. /// The library manager. /// The configuration manager. public MusicArtistResolver( - ILogger logger, + ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config) diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 0b93ebeb81..503de0b4e9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -11,7 +11,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books { public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver { - private readonly string[] _validExtensions = { ".pdf", ".epub", ".mobi", ".cbr", ".cbz", ".azw3" }; + private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".opf", ".pdf" }; protected override Book Resolve(ItemResolveArgs args) { diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index d63bc6bda8..b8feb5535f 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -608,6 +608,31 @@ namespace Emby.Server.Implementations.Library return dto; } + public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + IAuthenticationProvider authenticationProvider = GetAuthenticationProvider(user); + bool hasConfiguredPassword = authenticationProvider.HasPassword(user); + bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(authenticationProvider.GetEasyPasswordHash(user)); + + bool hasPassword = user.Configuration.EnableLocalPassword && + !string.IsNullOrEmpty(remoteEndPoint) && + _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword; + + PublicUserDto dto = new PublicUserDto + { + Name = user.Name, + HasPassword = hasPassword, + HasConfiguredPassword = hasConfiguredPassword, + }; + + return dto; + } + public UserDto GetOfflineUserDto(User user) { var dto = GetUserDto(user); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 33f4ca1465..3efe1ee253 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var stream = new MediaSourceInfo { - EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveRecordings/" + info.Id + "/stream", + EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", EncoderProtocol = MediaProtocol.Http, Path = info.Path, Protocol = MediaProtocol.File, diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index d89a816b3b..82b1f3cf1f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.Path = tempFile; //OpenedMediaSource.ReadAtNativeFramerate = true; - MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; //OpenedMediaSource.SupportsDirectPlay = false; //OpenedMediaSource.SupportsDirectStream = true; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index f5dda79db3..f7c9c736e3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public M3UTunerHost( IServerConfigurationManager config, IMediaSourceManager mediaSourceManager, - ILogger logger, + ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IHttpClient httpClient, @@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(list); } - private static readonly string[] _disallowedSharedStreamExtensions = new string[] + private static readonly string[] _disallowedSharedStreamExtensions = { ".mkv", ".mp4", diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 0e600202aa..322fbbbaa5 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net.Http; using System.Threading; @@ -106,7 +107,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts //OpenedMediaSource.Path = tempFile; //OpenedMediaSource.ReadAtNativeFramerate = true; - MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; //OpenedMediaSource.Path = TempFilePath; @@ -118,6 +119,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts //OpenedMediaSource.SupportsDirectStream = true; //OpenedMediaSource.SupportsTranscoding = true; await taskCompletionSource.Task.ConfigureAwait(false); + if (taskCompletionSource.Task.Exception != null) + { + // Error happened while opening the stream so raise the exception again to inform the caller + throw taskCompletionSource.Task.Exception; + } + + if (!taskCompletionSource.Task.Result) + { + Logger.LogWarning("Zero bytes copied from stream {0} to {1} but no exception raised", GetType().Name, TempFilePath); + throw new EndOfStreamException(String.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name)); + } } private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) @@ -139,14 +151,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts cancellationToken).ConfigureAwait(false); } } - catch (OperationCanceledException) + catch (OperationCanceledException ex) { + Logger.LogInformation("Copying of {0} to {1} was canceled", GetType().Name, TempFilePath); + openTaskCompletionSource.TrySetException(ex); } catch (Exception ex) { - Logger.LogError(ex, "Error copying live stream."); + Logger.LogError(ex, "Error copying live stream {0} to {1}.", GetType().Name, TempFilePath); + openTaskCompletionSource.TrySetException(ex); } + openTaskCompletionSource.TrySetResult(false); + EnableStreamSharing = false; await DeleteTempFiles(new List { TempFilePath }).ConfigureAwait(false); }); diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json index 1363eaf854..20447347b3 100644 --- a/Emby.Server.Implementations/Localization/Core/af.json +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -4,7 +4,7 @@ "Folders": "Fouers", "Favorites": "Gunstelinge", "HeaderFavoriteShows": "Gunsteling Vertonings", - "ValueSpecialEpisodeName": "Spesiaal - {0}", + "ValueSpecialEpisodeName": "Spesiale - {0}", "HeaderAlbumArtists": "Album Kunstenaars", "Books": "Boeke", "HeaderNextUp": "Volgende", diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index ef7792356a..4949b10e6a 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -91,5 +91,7 @@ "HeaderNextUp": "এরপরে আসছে", "HeaderLiveTV": "লাইভ টিভি", "HeaderFavoriteSongs": "প্রিয় গানগুলো", - "HeaderFavoriteShows": "প্রিয় শোগুলো" + "HeaderFavoriteShows": "প্রিয় শোগুলো", + "TasksLibraryCategory": "গ্রন্থাগার", + "TasksMaintenanceCategory": "রক্ষণাবেক্ষণ" } diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index 1b6c6b5ae4..fc9a10f276 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -24,7 +24,7 @@ "HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en vivo", - "HeaderNextUp": "A Continuación", + "HeaderNextUp": "Siguiente", "HeaderRecordingGroups": "Grupos de grabación", "HomeVideos": "Videos caseros", "Inherit": "Heredar", @@ -44,7 +44,7 @@ "NameInstallFailed": "{0} instalación fallida", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada desconocida", - "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible para descargar.", + "NewVersionIsAvailable": "Una nueva versión del servidor Jellyfin está disponible para descargar.", "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible", "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada", "NotificationOptionAudioPlayback": "Se inició la reproducción de audio", @@ -56,7 +56,7 @@ "NotificationOptionPluginInstalled": "Complemento instalado", "NotificationOptionPluginUninstalled": "Complemento desinstalado", "NotificationOptionPluginUpdateInstalled": "Actualización de complemento instalada", - "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor", + "NotificationOptionServerRestartRequired": "Se necesita reiniciar el servidor", "NotificationOptionTaskFailed": "Falla de tarea programada", "NotificationOptionUserLockedOut": "Usuario bloqueado", "NotificationOptionVideoPlayback": "Se inició la reproducción de video", @@ -71,7 +71,7 @@ "ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskStartedWithName": "{0} iniciado", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", - "Shows": "Series", + "Shows": "Programas", "Songs": "Canciones", "StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", @@ -94,25 +94,25 @@ "ValueSpecialEpisodeName": "Especial - {0}", "VersionNumber": "Versión {0}", "TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.", - "TaskDownloadMissingSubtitles": "Descargar subtítulos extraviados", + "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", "TaskRefreshChannelsDescription": "Actualizar información de canales de internet.", "TaskRefreshChannels": "Actualizar canales", "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados con mas de un día de antigüedad.", - "TaskCleanTranscode": "Limpiar directorio de Transcodificado", + "TaskCleanTranscode": "Limpiar directorio de transcodificación", "TaskUpdatePluginsDescription": "Descargar e instalar actualizaciones para complementos que estén configurados en actualizar automáticamente.", "TaskUpdatePlugins": "Actualizar complementos", - "TaskRefreshPeopleDescription": "Actualizar metadatos de actores y directores en su librería multimedia.", + "TaskRefreshPeopleDescription": "Actualizar metadatos de actores y directores en su biblioteca multimedia.", "TaskRefreshPeople": "Actualizar personas", "TaskCleanLogsDescription": "Eliminar archivos de registro que tengan mas de {0} días de antigüedad.", "TaskCleanLogs": "Limpiar directorio de registros", - "TaskRefreshLibraryDescription": "Escanear su librería multimedia por nuevos archivos y refrescar metadatos.", - "TaskRefreshLibrary": "Escanear librería multimedia", + "TaskRefreshLibraryDescription": "Escanear su biblioteca multimedia por nuevos archivos y refrescar metadatos.", + "TaskRefreshLibrary": "Escanear biblioteca multimedia", "TaskRefreshChapterImagesDescription": "Crear miniaturas de videos que tengan capítulos.", - "TaskRefreshChapterImages": "Extraer imágenes de capitulo", - "TaskCleanCacheDescription": "Eliminar archivos de cache que no se necesiten en el sistema.", - "TaskCleanCache": "Limpiar directorio Cache", - "TasksChannelsCategory": "Canales de Internet", - "TasksApplicationCategory": "Solicitud", + "TaskRefreshChapterImages": "Extraer imágenes de capítulo", + "TaskCleanCacheDescription": "Eliminar archivos de caché que no se necesiten en el sistema.", + "TaskCleanCache": "Limpiar directorio caché", + "TasksChannelsCategory": "Canales de internet", + "TasksApplicationCategory": "Aplicación", "TasksLibraryCategory": "Biblioteca", "TasksMaintenanceCategory": "Mantenimiento" } diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index e0bbe90b36..d93920f433 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -11,7 +11,7 @@ "Collections": "Colecciones", "DeviceOfflineWithName": "{0} se ha desconectado", "DeviceOnlineWithName": "{0} está conectado", - "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}", + "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}", "Favorites": "Favoritos", "Folders": "Carpetas", "Genres": "Géneros", diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index de1baada84..e7bd3959bf 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -71,7 +71,7 @@ "ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskStartedWithName": "{0} iniciada", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", - "Shows": "Series", + "Shows": "Mostrar", "Songs": "Canciones", "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}", diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 2c9dae6a17..3dcfa68441 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -94,5 +94,24 @@ "ValueSpecialEpisodeName": "Spécial - {0}", "VersionNumber": "Version {0}", "TasksLibraryCategory": "Bibliothèque", - "TasksMaintenanceCategory": "Entretien" + "TasksMaintenanceCategory": "Entretien", + "TaskDownloadMissingSubtitlesDescription": "Recherche l'internet pour des sous-titres manquants à base de métadonnées configurées.", + "TaskDownloadMissingSubtitles": "Télécharger les sous-titres manquants", + "TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines internet.", + "TaskRefreshChannels": "Rafraîchir des chaines", + "TaskCleanTranscodeDescription": "Supprime les fichiers de transcodage de plus d'un jour.", + "TaskCleanTranscode": "Nettoyer le répertoire de transcodage", + "TaskUpdatePluginsDescription": "Télécharger et installer les mises à jours des extensions qui sont configurés pour les m.à.j. automisés.", + "TaskUpdatePlugins": "Mise à jour des extensions", + "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque de médias.", + "TaskRefreshPeople": "Rafraîchir les acteurs", + "TaskCleanLogsDescription": "Supprime les journaux qui ont plus que {0} jours.", + "TaskCleanLogs": "Nettoyer le répertoire des journaux", + "TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour trouver de nouveaux fichiers et rafraîchit les métadonnées.", + "TaskRefreshChapterImages": "Extraire les images de chapitre", + "TaskRefreshChapterImagesDescription": "Créer des vignettes pour les vidéos qui ont des chapitres", + "TaskRefreshLibrary": "Analyser la bibliothèque de médias", + "TaskCleanCache": "Nettoyer le répertoire des fichiers temporaires", + "TasksApplicationCategory": "Application", + "TaskCleanCacheDescription": "Supprime les fichiers temporaires qui ne sont plus nécessaire pour le système." } diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index 9611e33f57..8780a884ba 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -1,41 +1,41 @@ { - "Albums": "Albom", - "AppDeviceValues": "App: {0}, Grät: {1}", - "Application": "Aawändig", - "Artists": "Könstler", - "AuthenticationSucceededWithUserName": "{0} het sech aagmäudet", - "Books": "Büecher", - "CameraImageUploadedFrom": "Es nöis Foti esch ufeglade worde vo {0}", - "Channels": "Kanäu", - "ChapterNameValue": "Kapitu {0}", - "Collections": "Sammlige", - "DeviceOfflineWithName": "{0} esch offline gange", - "DeviceOnlineWithName": "{0} esch online cho", - "FailedLoginAttemptWithUserName": "Fäugschlagne Aamäudeversuech vo {0}", - "Favorites": "Favorite", + "Albums": "Alben", + "AppDeviceValues": "App: {0}, Gerät: {1}", + "Application": "Anwendung", + "Artists": "Künstler", + "AuthenticationSucceededWithUserName": "{0} hat sich angemeldet", + "Books": "Bücher", + "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", + "Channels": "Kanäle", + "ChapterNameValue": "Kapitel {0}", + "Collections": "Sammlungen", + "DeviceOfflineWithName": "{0} wurde getrennt", + "DeviceOnlineWithName": "{0} ist verbunden", + "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", + "Favorites": "Favoriten", "Folders": "Ordner", "Genres": "Genres", - "HeaderAlbumArtists": "Albom-Könstler", + "HeaderAlbumArtists": "Album-Künstler", "HeaderCameraUploads": "Kamera-Uploads", - "HeaderContinueWatching": "Wiiterluege", - "HeaderFavoriteAlbums": "Lieblingsalbe", - "HeaderFavoriteArtists": "Lieblings-Interprete", - "HeaderFavoriteEpisodes": "Lieblingsepisode", - "HeaderFavoriteShows": "Lieblingsserie", + "HeaderContinueWatching": "weiter schauen", + "HeaderFavoriteAlbums": "Lieblingsalben", + "HeaderFavoriteArtists": "Lieblings-Künstler", + "HeaderFavoriteEpisodes": "Lieblingsepisoden", + "HeaderFavoriteShows": "Lieblingsserien", "HeaderFavoriteSongs": "Lieblingslieder", - "HeaderLiveTV": "Live-Färnseh", - "HeaderNextUp": "Als nächts", - "HeaderRecordingGroups": "Ufnahmegruppe", - "HomeVideos": "Heimfilmli", - "Inherit": "Hinzuefüege", - "ItemAddedWithName": "{0} esch de Bibliothek dezuegfüegt worde", - "ItemRemovedWithName": "{0} esch vo de Bibliothek entfärnt worde", - "LabelIpAddressValue": "IP-Adrässe: {0}", - "LabelRunningTimeValue": "Loufziit: {0}", - "Latest": "Nöischti", - "MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde", - "MessageApplicationUpdatedTo": "Jellyfin Server esch of Version {0} aktualisiert worde", - "MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde", + "HeaderLiveTV": "Live-Fernseh", + "HeaderNextUp": "Als Nächstes", + "HeaderRecordingGroups": "Aufnahme-Gruppen", + "HomeVideos": "Heimvideos", + "Inherit": "Vererben", + "ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt", + "ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt", + "LabelIpAddressValue": "IP-Adresse: {0}", + "LabelRunningTimeValue": "Laufzeit: {0}", + "Latest": "Neueste", + "MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert", + "MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert", + "MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert", "MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde", "MixedContent": "Gmeschti Inhäut", "Movies": "Film", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Audiowedergab gstartet", "NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt", "NotificationOptionCameraImageUploaded": "Foti ueglade", - "NotificationOptionInstallationFailed": "Installationsfäuer", + "NotificationOptionInstallationFailed": "Installationsfehler", "NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt", "NotificationOptionPluginError": "Plugin-Fäuer", "NotificationOptionPluginInstalled": "Plugin installiert", @@ -92,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt", "ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde", "ValueSpecialEpisodeName": "Extra - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskCleanLogs": "Lösche Log Pfad", + "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.", + "TaskRefreshLibrary": "Scanne alle Bibliotheken", + "TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.", + "TaskRefreshChapterImages": "Extrahiere Kapitel-Bilder", + "TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.", + "TaskCleanCache": "Leere Cache Pfad", + "TasksChannelsCategory": "Internet Kanäle", + "TasksApplicationCategory": "Applikation", + "TasksLibraryCategory": "Bibliothek", + "TasksMaintenanceCategory": "Verwaltung", + "TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Metadaten Einstellungen.", + "TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter", + "TaskRefreshChannelsDescription": "Aktualisiert Internet Kanal Informationen.", + "TaskRefreshChannels": "Aktualisiere Kanäle", + "TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.", + "TaskCleanTranscode": "Räume Transcodier Verzeichnis auf", + "TaskUpdatePluginsDescription": "Lädt Aktualisierungen für Erweiterungen herunter und installiert diese, für welche automatische Aktualisierungen konfiguriert sind.", + "TaskUpdatePlugins": "Aktualisiere Erweiterungen", + "TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schausteller und Regisseure in deiner Bibliothek.", + "TaskRefreshPeople": "Aktualisiere Schauspieler", + "TaskCleanLogsDescription": "Löscht Log Dateien die älter als {0} Tage sind." } diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 8abe31d2a0..4e54b9f7ad 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -99,5 +99,13 @@ "TaskCleanCache": "נקה תיקיית מטמון", "TasksApplicationCategory": "יישום", "TasksLibraryCategory": "ספרייה", - "TasksMaintenanceCategory": "תחזוקה" + "TasksMaintenanceCategory": "תחזוקה", + "TaskUpdatePlugins": "עדכן תוספים", + "TaskRefreshPeopleDescription": "מעדכן מטא נתונים עבור שחקנים ובמאים בספריית המדיה שלך.", + "TaskRefreshPeople": "רענן אנשים", + "TaskCleanLogsDescription": "מוחק קבצי יומן בני יותר מ- {0} ימים.", + "TaskCleanLogs": "נקה תיקיית יומן", + "TaskRefreshLibraryDescription": "סורק את ספריית המדיה שלך אחר קבצים חדשים ומרענן מטא נתונים.", + "TaskRefreshChapterImagesDescription": "יוצר תמונות ממוזערות לסרטונים שיש להם פרקים.", + "TasksChannelsCategory": "ערוצי אינטרנט" } diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 6947178d7a..c169a35e79 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -30,7 +30,7 @@ "Inherit": "Naslijedi", "ItemAddedWithName": "{0} je dodano u biblioteku", "ItemRemovedWithName": "{0} je uklonjen iz biblioteke", - "LabelIpAddressValue": "Ip adresa: {0}", + "LabelIpAddressValue": "IP adresa: {0}", "LabelRunningTimeValue": "Vrijeme rada: {0}", "Latest": "Najnovije", "MessageApplicationUpdated": "Jellyfin Server je ažuriran", @@ -92,5 +92,13 @@ "UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "Specijal - {0}", - "VersionNumber": "Verzija {0}" + "VersionNumber": "Verzija {0}", + "TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.", + "TaskRefreshLibrary": "Skeniraj medijsku knjižnicu", + "TaskRefreshChapterImagesDescription": "Stvara sličice za videozapise koji imaju poglavlja.", + "TaskRefreshChapterImages": "Raspakiraj slike poglavlja", + "TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.", + "TaskCleanCache": "Očisti priručnu memoriju", + "TasksApplicationCategory": "Aplikacija", + "TasksMaintenanceCategory": "Održavanje" } diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 0758bbe9ce..7f5a56e86c 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -5,7 +5,7 @@ "Artists": "Artisti", "AuthenticationSucceededWithUserName": "{0} autenticato con successo", "Books": "Libri", - "CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}", + "CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 01a740187d..35053766b4 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -92,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} baigė leisti {1} į {2}", "ValueHasBeenAddedToLibrary": "{0} pridėtas į mediateką", "ValueSpecialEpisodeName": "Ypatinga - {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskUpdatePluginsDescription": "Atsisiųsti ir įdiegti atnaujinimus priedams kuriem yra nustatytas automatiškas atnaujinimas.", + "TaskUpdatePlugins": "Atnaujinti Priedus", + "TaskDownloadMissingSubtitlesDescription": "Ieško internete trūkstamų subtitrų remiantis metaduomenų konfigūracija.", + "TaskCleanTranscodeDescription": "Ištrina dienos senumo perkodavimo failus.", + "TaskCleanTranscode": "Išvalyti Perkodavimo Direktorija", + "TaskRefreshLibraryDescription": "Ieškoti naujų failų jūsų mediatekoje ir atnaujina metaduomenis.", + "TaskRefreshLibrary": "Skenuoti Mediateka", + "TaskDownloadMissingSubtitles": "Atsisiųsti trūkstamus subtitrus", + "TaskRefreshChannelsDescription": "Atnaujina internetinių kanalų informacija.", + "TaskRefreshChannels": "Atnaujinti Kanalus", + "TaskRefreshPeopleDescription": "Atnaujina metaduomenis apie aktorius ir režisierius jūsų mediatekoje.", + "TaskRefreshPeople": "Atnaujinti Žmones", + "TaskCleanLogsDescription": "Ištrina žurnalo failus kurie yra senesni nei {0} dienos.", + "TaskCleanLogs": "Išvalyti Žurnalą", + "TaskRefreshChapterImagesDescription": "Sukuria miniatiūras vaizdo įrašam, kurie turi scenas.", + "TaskRefreshChapterImages": "Ištraukti Scenų Paveikslus", + "TaskCleanCache": "Išvalyti Talpyklą", + "TaskCleanCacheDescription": "Ištrina talpyklos failus, kurių daugiau nereikia sistemai.", + "TasksChannelsCategory": "Internetiniai Kanalai", + "TasksApplicationCategory": "Programa", + "TasksLibraryCategory": "Mediateka", + "TasksMaintenanceCategory": "Priežiūra" } diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index 8df137302f..bbdf99abab 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -91,5 +91,12 @@ "Songs": "Песни", "Shows": "Серии", "ServerNameNeedsToBeRestarted": "{0} треба да се рестартира", - "ScheduledTaskStartedWithName": "{0} започна" + "ScheduledTaskStartedWithName": "{0} започна", + "TaskRefreshChapterImages": "Извези Слики од Поглавје", + "TaskCleanCacheDescription": "Ги брише кешираните фајлови што не се повеќе потребни од системот.", + "TaskCleanCache": "Исчисти Го Кешот", + "TasksChannelsCategory": "Интернет Канали", + "TasksApplicationCategory": "Апликација", + "TasksLibraryCategory": "Библиотека", + "TasksMaintenanceCategory": "Одржување" } diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 50d0d083cb..5637ce3462 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -97,5 +97,9 @@ "TasksApplicationCategory": "Applikasjon", "TasksLibraryCategory": "Bibliotek", "TasksMaintenanceCategory": "Vedlikehold", - "TaskCleanCache": "Tøm buffer katalog" + "TaskCleanCache": "Tøm buffer katalog", + "TaskRefreshLibrary": "Skann mediebibliotek", + "TaskRefreshChapterImagesDescription": "Lager forhåndsvisningsbilder for videoer som har kapitler.", + "TaskRefreshChapterImages": "Trekk ut Kapittelbilder", + "TaskCleanCacheDescription": "Sletter mellomlagrede filer som ikke lengre trengs av systemet." } diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index baa12e98ec..41c74d54de 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -5,7 +5,7 @@ "Artists": "Artiesten", "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd", "Books": "Boeken", - "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}", + "CameraImageUploadedFrom": "Er is een nieuwe camera afbeelding toegevoegd via {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", @@ -26,7 +26,7 @@ "HeaderLiveTV": "Live TV", "HeaderNextUp": "Volgende", "HeaderRecordingGroups": "Opnamegroepen", - "HomeVideos": "Start video's", + "HomeVideos": "Home video's", "Inherit": "Overerven", "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek", "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Muziek gestart", "NotificationOptionAudioPlaybackStopped": "Muziek gestopt", "NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload", - "NotificationOptionInstallationFailed": "Installatie mislukking", + "NotificationOptionInstallationFailed": "Installatie mislukt", "NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd", "NotificationOptionPluginError": "Plug-in fout", "NotificationOptionPluginInstalled": "Plug-in geïnstalleerd", diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index b60dd33bd5..60c58d472d 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -92,5 +92,26 @@ "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici", "ValueSpecialEpisodeName": "Poseben - {0}", - "VersionNumber": "Različica {0}" + "VersionNumber": "Različica {0}", + "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise", + "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.", + "TaskRefreshChannels": "Osveži kanale", + "TaskCleanTranscodeDescription": "Izbriše več kot dan stare datoteke prekodiranja.", + "TaskCleanTranscode": "Počisti mapo prekodiranja", + "TaskUpdatePluginsDescription": "Prenese in namesti posodobitve za dodatke, ki imajo omogočene samodejne posodobitve.", + "TaskUpdatePlugins": "Posodobi dodatke", + "TaskRefreshPeopleDescription": "Osveži metapodatke za igralce in režiserje v vaši knjižnici.", + "TaskRefreshPeople": "Osveži osebe", + "TaskCleanLogsDescription": "Izbriše dnevniške datoteke starejše od {0} dni.", + "TaskCleanLogs": "Počisti mapo dnevnika", + "TaskRefreshLibraryDescription": "Preišče vašo knjižnico za nove datoteke in osveži metapodatke.", + "TaskRefreshLibrary": "Preišči knjižnico predstavnosti", + "TaskRefreshChapterImagesDescription": "Ustvari sličice za poglavja videoposnetkov.", + "TaskRefreshChapterImages": "Izvleči slike poglavij", + "TaskCleanCacheDescription": "Izbriše predpomnjene datoteke, ki niso več potrebne.", + "TaskCleanCache": "Počisti mapo predpomnilnika", + "TasksChannelsCategory": "Spletni kanali", + "TasksApplicationCategory": "Aplikacija", + "TasksLibraryCategory": "Knjižnica", + "TasksMaintenanceCategory": "Vzdrževanje" } diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json new file mode 100644 index 0000000000..b2e0b66fe1 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -0,0 +1,36 @@ +{ + "MusicVideos": "Музичні відео", + "Music": "Музика", + "Movies": "Фільми", + "MessageApplicationUpdatedTo": "Jellyfin Server був оновлений до версії {0}", + "MessageApplicationUpdated": "Jellyfin Server був оновлений", + "Latest": "Останні", + "LabelIpAddressValue": "IP-адреси: {0}", + "ItemRemovedWithName": "{0} видалено з бібліотеки", + "ItemAddedWithName": "{0} додано до бібліотеки", + "HeaderNextUp": "Наступний", + "HeaderLiveTV": "Ефірне ТБ", + "HeaderFavoriteSongs": "Улюблені пісні", + "HeaderFavoriteShows": "Улюблені шоу", + "HeaderFavoriteEpisodes": "Улюблені серії", + "HeaderFavoriteArtists": "Улюблені виконавці", + "HeaderFavoriteAlbums": "Улюблені альбоми", + "HeaderContinueWatching": "Продовжити перегляд", + "HeaderCameraUploads": "Завантажено з камери", + "HeaderAlbumArtists": "Виконавці альбомів", + "Genres": "Жанри", + "Folders": "Директорії", + "Favorites": "Улюблені", + "DeviceOnlineWithName": "{0} під'єднано", + "DeviceOfflineWithName": "{0} від'єднано", + "Collections": "Колекції", + "ChapterNameValue": "Глава {0}", + "Channels": "Канали", + "CameraImageUploadedFrom": "Нова фотографія завантажена з {0}", + "Books": "Книги", + "AuthenticationSucceededWithUserName": "{0} успішно авторизовані", + "Artists": "Виконавці", + "Application": "Додаток", + "AppDeviceValues": "Додаток: {0}, Пристрій: {1}", + "Albums": "Альбоми" +} diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 224748e611..a67a67582f 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -1,6 +1,6 @@ { "Albums": "專輯", - "AppDeviceValues": "軟體: {0}, 設備: {1}", + "AppDeviceValues": "軟件: {0}, 設備: {1}", "Application": "應用程式", "Artists": "藝人", "AuthenticationSucceededWithUserName": "{0} 授權成功", @@ -92,5 +92,8 @@ "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}", "ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", - "VersionNumber": "版本{0}" + "VersionNumber": "版本{0}", + "TaskDownloadMissingSubtitles": "下載遺失的字幕", + "TaskUpdatePlugins": "更新插件", + "TasksApplicationCategory": "應用程式" } diff --git a/Emby.Server.Implementations/Middleware/WebSocketMiddleware.cs b/Emby.Server.Implementations/Middleware/WebSocketMiddleware.cs deleted file mode 100644 index fda32da5e8..0000000000 --- a/Emby.Server.Implementations/Middleware/WebSocketMiddleware.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using WebSocketManager = Emby.Server.Implementations.WebSockets.WebSocketManager; - -namespace Emby.Server.Implementations.Middleware -{ - public class WebSocketMiddleware - { - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly WebSocketManager _webSocketManager; - - public WebSocketMiddleware(RequestDelegate next, ILogger logger, WebSocketManager webSocketManager) - { - _next = next; - _logger = logger; - _webSocketManager = webSocketManager; - } - - public async Task Invoke(HttpContext httpContext) - { - _logger.LogInformation("Handling request: " + httpContext.Request.Path); - - if (httpContext.WebSockets.IsWebSocketRequest) - { - var webSocketContext = await httpContext.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false); - if (webSocketContext != null) - { - await _webSocketManager.OnWebSocketConnected(webSocketContext).ConfigureAwait(false); - } - } - else - { - await _next.Invoke(httpContext).ConfigureAwait(false); - } - } - } -} diff --git a/Emby.Server.Implementations/Net/IWebSocket.cs b/Emby.Server.Implementations/Net/IWebSocket.cs deleted file mode 100644 index 4d160aa66f..0000000000 --- a/Emby.Server.Implementations/Net/IWebSocket.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; - -namespace Emby.Server.Implementations.Net -{ - /// - /// Interface IWebSocket - /// - public interface IWebSocket : IDisposable - { - /// - /// Occurs when [closed]. - /// - event EventHandler Closed; - - /// - /// Gets or sets the state. - /// - /// The state. - WebSocketState State { get; } - - /// - /// Gets or sets the receive action. - /// - /// The receive action. - Action OnReceiveBytes { get; set; } - - /// - /// Sends the async. - /// - /// The bytes. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken); - - /// - /// Sends the asynchronous. - /// - /// The text. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken); - } -} diff --git a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs deleted file mode 100644 index 6880766f9a..0000000000 --- a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Microsoft.AspNetCore.Http; - -namespace Emby.Server.Implementations.Net -{ - public class WebSocketConnectEventArgs : EventArgs - { - /// - /// Gets or sets the URL. - /// - /// The URL. - public string Url { get; set; } - /// - /// Gets or sets the query string. - /// - /// The query string. - public IQueryCollection QueryString { get; set; } - /// - /// Gets or sets the web socket. - /// - /// The web socket. - public IWebSocket WebSocket { get; set; } - /// - /// Gets or sets the endpoint. - /// - /// The endpoint. - public string Endpoint { get; set; } - } -} diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs deleted file mode 100644 index dfb81816c7..0000000000 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Session; - -namespace Emby.Server.Implementations.Session -{ - public class HttpSessionController : ISessionController - { - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _json; - private readonly ISessionManager _sessionManager; - - public SessionInfo Session { get; private set; } - - private readonly string _postUrl; - - public HttpSessionController(IHttpClient httpClient, - IJsonSerializer json, - SessionInfo session, - string postUrl, ISessionManager sessionManager) - { - _httpClient = httpClient; - _json = json; - Session = session; - _postUrl = postUrl; - _sessionManager = sessionManager; - } - - private string PostUrl => string.Format("http://{0}{1}", Session.RemoteEndPoint, _postUrl); - - public bool IsSessionActive => (DateTime.UtcNow - Session.LastActivityDate).TotalMinutes <= 5; - - public bool SupportsMediaControl => true; - - private Task SendMessage(string name, string messageId, CancellationToken cancellationToken) - { - return SendMessage(name, messageId, new Dictionary(), cancellationToken); - } - - private Task SendMessage(string name, string messageId, Dictionary args, CancellationToken cancellationToken) - { - args["messageId"] = messageId; - var url = PostUrl + "/" + name + ToQueryString(args); - - return SendRequest(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - }); - } - - private Task SendPlayCommand(PlayRequest command, string messageId, CancellationToken cancellationToken) - { - var dict = new Dictionary(); - - dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); - - if (command.StartPositionTicks.HasValue) - { - dict["StartPositionTicks"] = command.StartPositionTicks.Value.ToString(CultureInfo.InvariantCulture); - } - if (command.AudioStreamIndex.HasValue) - { - dict["AudioStreamIndex"] = command.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture); - } - if (command.SubtitleStreamIndex.HasValue) - { - dict["SubtitleStreamIndex"] = command.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture); - } - if (command.StartIndex.HasValue) - { - dict["StartIndex"] = command.StartIndex.Value.ToString(CultureInfo.InvariantCulture); - } - if (!string.IsNullOrEmpty(command.MediaSourceId)) - { - dict["MediaSourceId"] = command.MediaSourceId; - } - - return SendMessage(command.PlayCommand.ToString(), messageId, dict, cancellationToken); - } - - private Task SendPlaystateCommand(PlaystateRequest command, string messageId, CancellationToken cancellationToken) - { - var args = new Dictionary(); - - if (command.Command == PlaystateCommand.Seek) - { - if (!command.SeekPositionTicks.HasValue) - { - throw new ArgumentException("SeekPositionTicks cannot be null"); - } - - args["SeekPositionTicks"] = command.SeekPositionTicks.Value.ToString(CultureInfo.InvariantCulture); - } - - return SendMessage(command.Command.ToString(), messageId, args, cancellationToken); - } - - private string[] _supportedMessages = Array.Empty(); - public Task SendMessage(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken) - { - if (!IsSessionActive) - { - return Task.CompletedTask; - } - - if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) - { - return SendPlayCommand(data as PlayRequest, messageId, cancellationToken); - } - if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) - { - return SendPlaystateCommand(data as PlaystateRequest, messageId, cancellationToken); - } - if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) - { - var command = data as GeneralCommand; - return SendMessage(command.Name, messageId, command.Arguments, cancellationToken); - } - - if (!_supportedMessages.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - return Task.CompletedTask; - } - - var url = PostUrl + "/" + name; - - url += "?messageId=" + messageId; - - var options = new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = false - }; - - if (data != null) - { - if (typeof(T) == typeof(string)) - { - var str = data as string; - if (!string.IsNullOrEmpty(str)) - { - options.RequestContent = str; - options.RequestContentType = "application/json"; - } - } - else - { - options.RequestContent = _json.SerializeToString(data); - options.RequestContentType = "application/json"; - } - } - - return SendRequest(options); - } - - private async Task SendRequest(HttpRequestOptions options) - { - using (var response = await _httpClient.Post(options).ConfigureAwait(false)) - { - - } - } - - private static string ToQueryString(Dictionary nvc) - { - var array = (from item in nvc - select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value))) - .ToArray(); - - var args = string.Join("&", array); - - if (string.IsNullOrEmpty(args)) - { - return args; - } - - return "?" + args; - } - } -} diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index c93c02c480..df98a35bc0 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -477,8 +477,7 @@ namespace Emby.Server.Implementations.Session Client = appName, DeviceId = deviceId, ApplicationVersion = appVersion, - Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture), - ServerId = _appHost.SystemId + Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; var username = user?.Name; @@ -1042,12 +1041,12 @@ namespace Emby.Server.Implementations.Session private static async Task SendMessageToSession(SessionInfo session, string name, T data, CancellationToken cancellationToken) { - var controllers = session.SessionControllers.ToArray(); - var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + var controllers = session.SessionControllers; + var messageId = Guid.NewGuid(); foreach (var controller in controllers) { - await controller.SendMessage(name, messageId, data, controllers, cancellationToken).ConfigureAwait(false); + await controller.SendMessage(name, messageId, data, cancellationToken).ConfigureAwait(false); } } @@ -1055,13 +1054,13 @@ namespace Emby.Server.Implementations.Session { IEnumerable GetTasks() { - var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + var messageId = Guid.NewGuid(); foreach (var session in sessions) { var controllers = session.SessionControllers; foreach (var controller in controllers) { - yield return controller.SendMessage(name, messageId, data, controllers, cancellationToken); + yield return controller.SendMessage(name, messageId, data, cancellationToken); } } } @@ -1762,7 +1761,7 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(info)); } - var user = info.UserId.Equals(Guid.Empty) + var user = info.UserId == Guid.Empty ? null : _userManager.GetUserById(info.UserId); diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 930f2d35d3..d4e4ba1f2f 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -12,7 +11,7 @@ namespace Emby.Server.Implementations.Session /// /// Class SessionWebSocketListener /// - public class SessionWebSocketListener : IWebSocketListener, IDisposable + public sealed class SessionWebSocketListener : IWebSocketListener, IDisposable { /// /// The _session manager @@ -23,42 +22,41 @@ namespace Emby.Server.Implementations.Session /// The _logger /// private readonly ILogger _logger; - - /// - /// The _dto service - /// - private readonly IJsonSerializer _json; + private readonly ILoggerFactory _loggerFactory; private readonly IHttpServer _httpServer; - /// /// Initializes a new instance of the class. /// + /// The logger. /// The session manager. /// The logger factory. - /// The json. /// The HTTP server. - public SessionWebSocketListener(ISessionManager sessionManager, ILoggerFactory loggerFactory, IJsonSerializer json, IHttpServer httpServer) + public SessionWebSocketListener( + ILogger logger, + ISessionManager sessionManager, + ILoggerFactory loggerFactory, + IHttpServer httpServer) { + _logger = logger; _sessionManager = sessionManager; - _logger = loggerFactory.CreateLogger(GetType().Name); - _json = json; + _loggerFactory = loggerFactory; _httpServer = httpServer; - httpServer.WebSocketConnected += _serverManager_WebSocketConnected; + + httpServer.WebSocketConnected += OnServerManagerWebSocketConnected; } - void _serverManager_WebSocketConnected(object sender, GenericEventArgs e) + private void OnServerManagerWebSocketConnected(object sender, GenericEventArgs e) { - var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint); - + var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint.ToString()); if (session != null) { EnsureController(session, e.Argument); } else { - _logger.LogWarning("Unable to determine session based on url: {0}", e.Argument.Url); + _logger.LogWarning("Unable to determine session based on query string: {0}", e.Argument.QueryString); } } @@ -79,9 +77,10 @@ namespace Emby.Server.Implementations.Session return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint); } + /// public void Dispose() { - _httpServer.WebSocketConnected -= _serverManager_WebSocketConnected; + _httpServer.WebSocketConnected -= OnServerManagerWebSocketConnected; } /// @@ -94,7 +93,8 @@ namespace Emby.Server.Implementations.Session private void EnsureController(SessionInfo session, IWebSocketConnection connection) { - var controllerInfo = session.EnsureController(s => new WebSocketController(s, _logger, _sessionManager)); + var controllerInfo = session.EnsureController( + s => new WebSocketController(_loggerFactory.CreateLogger(), s, _sessionManager)); var controller = (WebSocketController)controllerInfo.Item1; controller.AddWebSocket(connection); diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index 0d483c55fa..a0274acd20 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -1,3 +1,7 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -11,60 +15,63 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Session { - public class WebSocketController : ISessionController, IDisposable + public sealed class WebSocketController : ISessionController, IDisposable { - public SessionInfo Session { get; private set; } - public IReadOnlyList Sockets { get; private set; } - private readonly ILogger _logger; - private readonly ISessionManager _sessionManager; + private readonly SessionInfo _session; - public WebSocketController(SessionInfo session, ILogger logger, ISessionManager sessionManager) + private readonly List _sockets; + private bool _disposed = false; + + public WebSocketController( + ILogger logger, + SessionInfo session, + ISessionManager sessionManager) { - Session = session; _logger = logger; + _session = session; _sessionManager = sessionManager; - Sockets = new List(); + _sockets = new List(); } private bool HasOpenSockets => GetActiveSockets().Any(); + /// public bool SupportsMediaControl => HasOpenSockets; + /// public bool IsSessionActive => HasOpenSockets; private IEnumerable GetActiveSockets() - { - return Sockets - .OrderByDescending(i => i.LastActivityDate) - .Where(i => i.State == WebSocketState.Open); - } + => _sockets.Where(i => i.State == WebSocketState.Open); public void AddWebSocket(IWebSocketConnection connection) { - var sockets = Sockets.ToList(); - sockets.Add(connection); + _logger.LogDebug("Adding websocket to session {Session}", _session.Id); + _sockets.Add(connection); - Sockets = sockets; - - connection.Closed += connection_Closed; + connection.Closed += OnConnectionClosed; } - void connection_Closed(object sender, EventArgs e) + private void OnConnectionClosed(object sender, EventArgs e) { var connection = (IWebSocketConnection)sender; - var sockets = Sockets.ToList(); - sockets.Remove(connection); - - Sockets = sockets; - - _sessionManager.CloseIfNeeded(Session); + _logger.LogDebug("Removing websocket from session {Session}", _session.Id); + _sockets.Remove(connection); + connection.Closed -= OnConnectionClosed; + _sessionManager.CloseIfNeeded(_session); } - public Task SendMessage(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken) + /// + public Task SendMessage( + string name, + Guid messageId, + T data, + CancellationToken cancellationToken) { var socket = GetActiveSockets() + .OrderByDescending(i => i.LastActivityDate) .FirstOrDefault(); if (socket == null) @@ -72,21 +79,30 @@ namespace Emby.Server.Implementations.Session return Task.CompletedTask; } - return socket.SendAsync(new WebSocketMessage - { - Data = data, - MessageType = name, - MessageId = messageId - - }, cancellationToken); + return socket.SendAsync( + new WebSocketMessage + { + Data = data, + MessageType = name, + MessageId = messageId + }, + cancellationToken); } + /// public void Dispose() { - foreach (var socket in Sockets.ToList()) + if (_disposed) { - socket.Closed -= connection_Closed; + return; } + + foreach (var socket in _sockets) + { + socket.Closed -= OnConnectionClosed; + } + + _disposed = true; } } } diff --git a/Emby.Server.Implementations/SocketSharp/SharpWebSocket.cs b/Emby.Server.Implementations/SocketSharp/SharpWebSocket.cs deleted file mode 100644 index 67521d6c63..0000000000 --- a/Emby.Server.Implementations/SocketSharp/SharpWebSocket.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Implementations.Net; -using Microsoft.Extensions.Logging; - -namespace Emby.Server.Implementations.SocketSharp -{ - public class SharpWebSocket : IWebSocket - { - /// - /// The logger - /// - private readonly ILogger _logger; - - public event EventHandler Closed; - - /// - /// Gets or sets the web socket. - /// - /// The web socket. - private readonly WebSocket _webSocket; - - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - private bool _disposed; - - public SharpWebSocket(WebSocket socket, ILogger logger) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _webSocket = socket ?? throw new ArgumentNullException(nameof(socket)); - } - - /// - /// Gets the state. - /// - /// The state. - public WebSocketState State => _webSocket.State; - - /// - /// Sends the async. - /// - /// The bytes. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) - { - return _webSocket.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Binary, endOfMessage, cancellationToken); - } - - /// - /// Sends the asynchronous. - /// - /// The text. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) - { - return _webSocket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(text)), WebSocketMessageType.Text, endOfMessage, cancellationToken); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (_disposed) - { - return; - } - - if (dispose) - { - _cancellationTokenSource.Cancel(); - if (_webSocket.State == WebSocketState.Open) - { - _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client", - CancellationToken.None); - } - Closed?.Invoke(this, EventArgs.Empty); - } - - _disposed = true; - } - - /// - /// Gets or sets the receive action. - /// - /// The receive action. - public Action OnReceiveBytes { get; set; } - } -} diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs deleted file mode 100644 index b85750c9b9..0000000000 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Net; -using MediaBrowser.Model.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace Emby.Server.Implementations.SocketSharp -{ - public class WebSocketSharpListener : IHttpListener - { - private readonly ILogger _logger; - - private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); - private CancellationToken _disposeCancellationToken; - - public WebSocketSharpListener(ILogger logger) - { - _logger = logger; - _disposeCancellationToken = _disposeCancellationTokenSource.Token; - } - - public Func ErrorHandler { get; set; } - - public Func RequestHandler { get; set; } - - public Action WebSocketConnected { get; set; } - - private static void LogRequest(ILogger logger, HttpRequest request) - { - var url = request.GetDisplayUrl(); - - logger.LogInformation("WS {Url}. UserAgent: {UserAgent}", url, request.Headers[HeaderNames.UserAgent].ToString()); - } - - public async Task ProcessWebSocketRequest(HttpContext ctx) - { - try - { - LogRequest(_logger, ctx.Request); - var endpoint = ctx.Connection.RemoteIpAddress.ToString(); - var url = ctx.Request.GetDisplayUrl(); - - var webSocketContext = await ctx.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false); - var socket = new SharpWebSocket(webSocketContext, _logger); - - WebSocketConnected(new WebSocketConnectEventArgs - { - Url = url, - QueryString = ctx.Request.Query, - WebSocket = socket, - Endpoint = endpoint - }); - - WebSocketReceiveResult result; - var message = new List(); - - do - { - var buffer = WebSocket.CreateServerBuffer(4096); - result = await webSocketContext.ReceiveAsync(buffer, _disposeCancellationToken); - message.AddRange(buffer.Array.Take(result.Count)); - - if (result.EndOfMessage) - { - socket.OnReceiveBytes(message.ToArray()); - message.Clear(); - } - } while (socket.State == WebSocketState.Open && result.MessageType != WebSocketMessageType.Close); - - - if (webSocketContext.State == WebSocketState.Open) - { - await webSocketContext.CloseAsync( - result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, - result.CloseStatusDescription, - _disposeCancellationToken).ConfigureAwait(false); - } - - socket.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "AcceptWebSocketAsync error"); - if (!ctx.Response.HasStarted) - { - ctx.Response.StatusCode = 500; - } - } - } - - public Task Stop() - { - _disposeCancellationTokenSource.Cancel(); - return Task.CompletedTask; - } - - /// - /// Releases the unmanaged resources and disposes of the managed resources used. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private bool _disposed; - - /// - /// Releases the unmanaged resources and disposes of the managed resources used. - /// - /// Whether or not the managed resources should be disposed. - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - Stop().GetAwaiter().GetResult(); - } - - _disposed = true; - } - } -} diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 9c638f4395..ee5131c1ff 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -63,6 +63,9 @@ namespace Emby.Server.Implementations.SocketSharp if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip)) { ip = Request.HttpContext.Connection.RemoteIpAddress; + + // Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests) + ip ??= IPAddress.Loopback; } } @@ -90,7 +93,10 @@ namespace Emby.Server.Implementations.SocketSharp public IQueryCollection QueryString => Request.Query; - public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress); + public bool IsLocal => + (Request.HttpContext.Connection.LocalIpAddress == null + && Request.HttpContext.Connection.RemoteIpAddress == null) + || Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress); public string HttpMethod => Request.Method; diff --git a/Emby.Server.Implementations/WebSockets/WebSocketHandler.cs b/Emby.Server.Implementations/WebSockets/WebSocketHandler.cs deleted file mode 100644 index eb18774408..0000000000 --- a/Emby.Server.Implementations/WebSockets/WebSocketHandler.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading.Tasks; -using MediaBrowser.Model.Net; - -namespace Emby.Server.Implementations.WebSockets -{ - public interface IWebSocketHandler - { - Task ProcessMessage(WebSocketMessage message, TaskCompletionSource taskCompletionSource); - } -} diff --git a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs b/Emby.Server.Implementations/WebSockets/WebSocketManager.cs deleted file mode 100644 index 31a7468fbc..0000000000 --- a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; -using UtfUnknown; - -namespace Emby.Server.Implementations.WebSockets -{ - public class WebSocketManager - { - private readonly IWebSocketHandler[] _webSocketHandlers; - private readonly IJsonSerializer _jsonSerializer; - private readonly ILogger _logger; - private const int BufferSize = 4096; - - public WebSocketManager(IWebSocketHandler[] webSocketHandlers, IJsonSerializer jsonSerializer, ILogger logger) - { - _webSocketHandlers = webSocketHandlers; - _jsonSerializer = jsonSerializer; - _logger = logger; - } - - public async Task OnWebSocketConnected(WebSocket webSocket) - { - var taskCompletionSource = new TaskCompletionSource(); - var cancellationToken = new CancellationTokenSource().Token; - WebSocketReceiveResult result; - var message = new List(); - - // Keep listening for incoming messages, otherwise the socket closes automatically - do - { - var buffer = WebSocket.CreateServerBuffer(BufferSize); - result = await webSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); - message.AddRange(buffer.Array.Take(result.Count)); - - if (result.EndOfMessage) - { - await ProcessMessage(message.ToArray(), taskCompletionSource).ConfigureAwait(false); - message.Clear(); - } - } while (!taskCompletionSource.Task.IsCompleted && - webSocket.State == WebSocketState.Open && - result.MessageType != WebSocketMessageType.Close); - - if (webSocket.State == WebSocketState.Open) - { - await webSocket.CloseAsync( - result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, - result.CloseStatusDescription, - cancellationToken).ConfigureAwait(false); - } - } - - private async Task ProcessMessage(byte[] messageBytes, TaskCompletionSource taskCompletionSource) - { - var charset = CharsetDetector.DetectFromBytes(messageBytes).Detected?.EncodingName; - var message = string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase) - ? Encoding.UTF8.GetString(messageBytes, 0, messageBytes.Length) - : Encoding.ASCII.GetString(messageBytes, 0, messageBytes.Length); - - // All messages are expected to be valid JSON objects - if (!message.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogDebug("Received web socket message that is not a json structure: {Message}", message); - return; - } - - try - { - var info = _jsonSerializer.DeserializeFromString>(message); - - _logger.LogDebug("Websocket message received: {0}", info.MessageType); - - var tasks = _webSocketHandlers.Select(handler => Task.Run(() => - { - try - { - handler.ProcessMessage(info, taskCompletionSource).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "{HandlerType} failed processing WebSocket message {MessageType}", - handler.GetType().Name, info.MessageType ?? string.Empty); - } - })); - - await Task.WhenAll(tasks); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error processing web socket message"); - } - } - } -} diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs index 26f7d9d2dd..aab1141ee8 100644 --- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs +++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs @@ -1,3 +1,4 @@ +using System.Security.Authentication; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; @@ -59,6 +60,10 @@ namespace Jellyfin.Api.Auth return Task.FromResult(AuthenticateResult.Success(ticket)); } + catch (AuthenticationException ex) + { + return Task.FromResult(AuthenticateResult.Fail(ex)); + } catch (SecurityException ex) { return Task.FromResult(AuthenticateResult.Fail(ex)); diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index a582a209cb..25d5d0c893 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -13,7 +13,7 @@ - + diff --git a/Jellyfin.Data/DbContexts/Jellyfin.cs b/Jellyfin.Data/DbContexts/Jellyfin.cs deleted file mode 100644 index fd488ce7d7..0000000000 --- a/Jellyfin.Data/DbContexts/Jellyfin.cs +++ /dev/null @@ -1,1140 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore; - -namespace Jellyfin.Data.DbContexts -{ - /// - public partial class Jellyfin : DbContext - { - #region DbSets - public virtual Microsoft.EntityFrameworkCore.DbSet Artwork { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Books { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet BookMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Chapters { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Collections { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet CollectionItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Companies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet CompanyMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet CustomItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet CustomItemMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Episodes { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet EpisodeMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Genres { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Groups { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Libraries { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet LibraryItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet LibraryRoot { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MediaFiles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MediaFileStream { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Metadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MetadataProviders { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MetadataProviderIds { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Movies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MovieMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MusicAlbums { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet MusicAlbumMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Permissions { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet People { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet PersonRoles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Photo { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet PhotoMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Preferences { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet ProviderMappings { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Ratings { get; set; } - - /// - /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to - /// store review ratings, not age ratings - /// - public virtual Microsoft.EntityFrameworkCore.DbSet RatingSources { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Releases { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Seasons { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet SeasonMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Series { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet SeriesMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Tracks { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet TrackMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet Users { get; set; } - #endregion DbSets - - /// - /// Default connection string - /// - public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; - - /// - public Jellyfin(DbContextOptions options) : base(options) - { - } - - partial void CustomInit(DbContextOptionsBuilder optionsBuilder); - - /// - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - CustomInit(optionsBuilder); - } - - partial void OnModelCreatingImpl(ModelBuilder modelBuilder); - partial void OnModelCreatedImpl(ModelBuilder modelBuilder); - - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - OnModelCreatingImpl(modelBuilder); - - modelBuilder.HasDefaultSchema("jellyfin"); - - modelBuilder.Entity() - .ToTable("Artwork") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity().HasIndex(t => t.Kind); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .HasMany(x => x.BookMetadata) - .WithOne() - .HasForeignKey("BookMetadata_BookMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.ISBN) - .HasField("_ISBN") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Publishers) - .WithOne() - .HasForeignKey("Company_Publishers_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("Chapter") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.TimeStart) - .IsRequired() - .HasField("_TimeStart") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.TimeEnd) - .HasField("_TimeEnd") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .ToTable("Collection") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.CollectionItem) - .WithOne() - .HasForeignKey("CollectionItem_CollectionItem_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("CollectionItem") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.LibraryItem) - .WithOne() - .HasForeignKey("LibraryItem_Id") - .IsRequired(); - modelBuilder.Entity() - .HasOne(x => x.Next) - .WithOne() - .HasForeignKey("CollectionItem_Next_Id") - .IsRequired(); - modelBuilder.Entity() - .HasOne(x => x.Previous) - .WithOne() - .HasForeignKey("CollectionItem_Previous_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("Company") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.CompanyMetadata) - .WithOne() - .HasForeignKey("CompanyMetadata_CompanyMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasOne(x => x.Parent) - .WithOne() - .HasForeignKey("Company_Parent_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Description) - .HasMaxLength(65535) - .HasField("_Description") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Headquarters) - .HasMaxLength(255) - .HasField("_Headquarters") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Homepage) - .HasMaxLength(1024) - .HasField("_Homepage") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity() - .HasMany(x => x.CustomItemMetadata) - .WithOne() - .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity() - .Property(t => t.EpisodeNumber) - .HasField("_EpisodeNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.EpisodeMetadata) - .WithOne() - .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity() - .ToTable("Genre") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity().HasIndex(t => t.Name) - .IsUnique(); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .ToTable("Groups") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity().Property("Timestamp").IsConcurrencyToken(); - modelBuilder.Entity() - .HasMany(x => x.GroupPermissions) - .WithOne() - .HasForeignKey("Permission_GroupPermissions_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Preferences) - .WithOne() - .HasForeignKey("Preference_Preferences_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("Library") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .ToTable("LibraryItem") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity().HasIndex(t => t.UrlId) - .IsUnique(); - modelBuilder.Entity() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.LibraryRoot) - .WithOne() - .HasForeignKey("LibraryRoot_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("LibraryRoot") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.NetworkPath) - .HasMaxLength(65535) - .HasField("_NetworkPath") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.Library) - .WithOne() - .HasForeignKey("Library_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("MediaFile") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.MediaFileStreams) - .WithOne() - .HasForeignKey("MediaFileStream_MediaFileStreams_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("MediaFileStream") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.StreamNumber) - .IsRequired() - .HasField("_StreamNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .ToTable("Metadata") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Title) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Title") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.OriginalTitle) - .HasMaxLength(1024) - .HasField("_OriginalTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.SortTitle) - .HasMaxLength(1024) - .HasField("_SortTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.ReleaseDate) - .HasField("_ReleaseDate") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.PersonRoles) - .WithOne() - .HasForeignKey("PersonRole_PersonRoles_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Genres) - .WithOne() - .HasForeignKey("Genre_Genres_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Artwork) - .WithOne() - .HasForeignKey("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Ratings) - .WithOne() - .HasForeignKey("Rating_Ratings_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("MetadataProvider") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity() - .ToTable("MetadataProviderId") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.ProviderId) - .HasMaxLength(255) - .IsRequired() - .HasField("_ProviderId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.MetadataProvider) - .WithOne() - .HasForeignKey("MetadataProvider_Id") - .IsRequired(); - - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.MovieMetadata) - .WithOne() - .HasForeignKey("MovieMetadata_MovieMetadata_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Studios) - .WithOne() - .HasForeignKey("Company_Studios_Id") - .IsRequired(); - - modelBuilder.Entity() - .HasMany(x => x.MusicAlbumMetadata) - .WithOne() - .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Tracks) - .WithOne() - .HasForeignKey("Track_Tracks_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Barcode) - .HasMaxLength(255) - .HasField("_Barcode") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.LabelNumber) - .HasMaxLength(255) - .HasField("_LabelNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Labels) - .WithOne() - .HasForeignKey("Company_Labels_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("Permissions") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Value) - .IsRequired(); - modelBuilder.Entity().Property("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity() - .ToTable("Person") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.SourceId) - .HasMaxLength(255) - .HasField("_SourceId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("PersonRole") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Role) - .HasMaxLength(1024) - .HasField("_Role") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Type) - .IsRequired() - .HasField("_Type") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.Person) - .WithOne() - .HasForeignKey("Person_Id") - .IsRequired() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() - .HasOne(x => x.Artwork) - .WithOne() - .HasForeignKey("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity() - .HasMany(x => x.PhotoMetadata) - .WithOne() - .HasForeignKey("PhotoMetadata_PhotoMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity() - .ToTable("Preferences") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Kind) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.Value) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity().Property("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity() - .ToTable("ProviderMappings") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.ProviderName) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.ProviderSecrets) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.ProviderData) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity().Property("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity() - .ToTable("Rating") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Value) - .IsRequired() - .HasField("_Value") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Votes) - .HasField("_Votes") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.RatingType) - .WithOne() - .HasForeignKey("RatingSource_RatingType_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("RatingType") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.MaximumValue) - .IsRequired() - .HasField("_MaximumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.MinimumValue) - .IsRequired() - .HasField("_MinimumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasOne(x => x.Source) - .WithOne() - .HasForeignKey("MetadataProviderId_Source_Id") - .IsRequired(); - - modelBuilder.Entity() - .ToTable("Release") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity() - .HasMany(x => x.MediaFiles) - .WithOne() - .HasForeignKey("MediaFile_MediaFiles_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Chapters) - .WithOne() - .HasForeignKey("Chapter_Chapters_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.SeasonNumber) - .HasField("_SeasonNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.SeasonMetadata) - .WithOne() - .HasForeignKey("SeasonMetadata_SeasonMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Episodes) - .WithOne() - .HasForeignKey("Episode_Episodes_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity() - .Property(t => t.AirsDayOfWeek) - .HasField("_AirsDayOfWeek") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.AirsTime) - .HasField("_AirsTime") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.FirstAired) - .HasField("_FirstAired") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.SeriesMetadata) - .WithOne() - .HasForeignKey("SeriesMetadata_SeriesMetadata_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Seasons) - .WithOne() - .HasForeignKey("Season_Seasons_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Networks) - .WithOne() - .HasForeignKey("Company_Networks_Id") - .IsRequired(); - - modelBuilder.Entity() - .Property(t => t.TrackNumber) - .HasField("_TrackNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.TrackMetadata) - .WithOne() - .HasForeignKey("TrackMetadata_TrackMetadata_Id") - .IsRequired(); - - - modelBuilder.Entity() - .ToTable("Users") - .HasKey(t => t.Id); - modelBuilder.Entity() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity() - .Property(t => t.LastLoginTimestamp) - .IsRequired() - .IsRowVersion(); - modelBuilder.Entity() - .Property(t => t.Username) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.Password) - .HasMaxLength(65535); - modelBuilder.Entity() - .Property(t => t.MustUpdatePassword) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.AudioLanguagePreference) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.AuthenticationProviderId) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.GroupedFolders) - .HasMaxLength(65535); - modelBuilder.Entity() - .Property(t => t.InvalidLoginAttemptCount) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.LatestItemExcludes) - .HasMaxLength(65535); - modelBuilder.Entity() - .Property(t => t.MyMediaExcludes) - .HasMaxLength(65535); - modelBuilder.Entity() - .Property(t => t.OrderedViews) - .HasMaxLength(65535); - modelBuilder.Entity() - .Property(t => t.SubtitleMode) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.PlayDefaultAudioTrack) - .IsRequired(); - modelBuilder.Entity() - .Property(t => t.SubtitleLanguagePrefernce) - .HasMaxLength(255); - modelBuilder.Entity() - .HasMany(x => x.Groups) - .WithOne() - .HasForeignKey("Group_Groups_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Permissions) - .WithOne() - .HasForeignKey("Permission_Permissions_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity() - .HasMany(x => x.Preferences) - .WithOne() - .HasForeignKey("Preference_Preferences_Id") - .IsRequired(); - - OnModelCreatedImpl(modelBuilder); - } - } -} diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs new file mode 100644 index 0000000000..522c206640 --- /dev/null +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -0,0 +1,154 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Data.Entities +{ + /// + /// An entity referencing an activity log entry. + /// + public partial class ActivityLog : ISavingChanges + { + /// + /// Initializes a new instance of the class. + /// Public constructor with required data. + /// + /// The name. + /// The type. + /// The user id. + public ActivityLog(string name, string type, Guid userId) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (string.IsNullOrEmpty(type)) + { + throw new ArgumentNullException(nameof(type)); + } + + this.Name = name; + this.Type = type; + this.UserId = userId; + this.DateCreated = DateTime.UtcNow; + this.LogSeverity = LogLevel.Trace; + + Init(); + } + + /// + /// Initializes a new instance of the class. + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected ActivityLog() + { + Init(); + } + + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The name. + /// The type. + /// The user's id. + /// The new instance. + public static ActivityLog Create(string name, string type, Guid userId) + { + return new ActivityLog(name, type, userId); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// + /// Gets or sets the identity of this instance. + /// This is the key in the backing database. + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// + /// Gets or sets the name. + /// Required, Max length = 512. + /// + [Required] + [MaxLength(512)] + [StringLength(512)] + public string Name { get; set; } + + /// + /// Gets or sets the overview. + /// Max length = 512. + /// + [MaxLength(512)] + [StringLength(512)] + public string Overview { get; set; } + + /// + /// Gets or sets the short overview. + /// Max length = 512. + /// + [MaxLength(512)] + [StringLength(512)] + public string ShortOverview { get; set; } + + /// + /// Gets or sets the type. + /// Required, Max length = 256. + /// + [Required] + [MaxLength(256)] + [StringLength(256)] + public string Type { get; set; } + + /// + /// Gets or sets the user id. + /// Required. + /// + [Required] + public Guid UserId { get; set; } + + /// + /// Gets or sets the item id. + /// Max length = 256. + /// + [MaxLength(256)] + [StringLength(256)] + public string ItemId { get; set; } + + /// + /// Gets or sets the date created. This should be in UTC. + /// Required. + /// + [Required] + public DateTime DateCreated { get; set; } + + /// + /// Gets or sets the log severity. Default is . + /// Required. + /// + [Required] + public LogLevel LogSeverity { get; set; } + + /// + /// Gets or sets the row version. + /// Required, ConcurrencyToken. + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + partial void Init(); + + /// + public void OnSavingChanges() + { + RowVersion++; + } + } +} diff --git a/Jellyfin.Data/Entities/Artwork.cs b/Jellyfin.Data/Entities/Artwork.cs index be13686dc2..bf3029368a 100644 --- a/Jellyfin.Data/Entities/Artwork.cs +++ b/Jellyfin.Data/Entities/Artwork.cs @@ -1,208 +1,195 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Artwork - { - partial void Init(); + public partial class Artwork + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Artwork() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Artwork() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Artwork CreateArtworkUnsafe() - { - return new Artwork(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Artwork CreateArtworkUnsafe() + { + return new Artwork(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - public Artwork(string path, global::Jellyfin.Data.Enums.ArtKind kind, global::Jellyfin.Data.Entities.Metadata _metadata0, global::Jellyfin.Data.Entities.PersonRole _personrole1) - { - if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); - this.Path = path; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + public Artwork(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; - this.Kind = kind; + this.Kind = kind; - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Artwork.Add(this); + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Artwork.Add(this); - if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1)); - _personrole1.Artwork = this; + if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1)); + _personrole1.Artwork = this; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - public static Artwork Create(string path, global::Jellyfin.Data.Enums.ArtKind kind, global::Jellyfin.Data.Entities.Metadata _metadata0, global::Jellyfin.Data.Entities.PersonRole _personrole1) - { - return new Artwork(path, kind, _metadata0, _personrole1); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + public static Artwork Create(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1) + { + return new Artwork(path, kind, _metadata0, _personrole1); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Path - /// - protected string _Path; - /// - /// When provided in a partial class, allows value of Path to be changed before setting. - /// - partial void SetPath(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Path to be changed before returning. - /// - partial void GetPath(ref string result); - - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string Path - { - get - { - string value = _Path; - GetPath(ref value); - return (_Path = value); - } - set - { - string oldValue = _Path; - SetPath(oldValue, ref value); - if (oldValue != value) + protected set { - _Path = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Kind - /// - internal global::Jellyfin.Data.Enums.ArtKind _Kind; - /// - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// - partial void SetKind(global::Jellyfin.Data.Enums.ArtKind oldValue, ref global::Jellyfin.Data.Enums.ArtKind newValue); - /// - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// - partial void GetKind(ref global::Jellyfin.Data.Enums.ArtKind result); + /// + /// Backing field for Path + /// + protected string _Path; + /// + /// When provided in a partial class, allows value of Path to be changed before setting. + /// + partial void SetPath(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Path to be changed before returning. + /// + partial void GetPath(ref string result); - /// - /// Indexed, Required - /// - [Required] - public global::Jellyfin.Data.Enums.ArtKind Kind - { - get - { - global::Jellyfin.Data.Enums.ArtKind value = _Kind; - GetKind(ref value); - return (_Kind = value); - } - set - { - global::Jellyfin.Data.Enums.ArtKind oldValue = _Kind; - SetKind(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Max length = 65535 + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get { - _Kind = value; + string value = _Path; + GetPath(ref value); + return (_Path = value); } - } - } + set + { + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Kind + /// + internal Enums.ArtKind _Kind; + /// + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// + partial void SetKind(Enums.ArtKind oldValue, ref Enums.ArtKind newValue); + /// + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// + partial void GetKind(ref Enums.ArtKind result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Indexed, Required + /// + [Required] + public Enums.ArtKind Kind + { + get + { + Enums.ArtKind value = _Kind; + GetKind(ref value); + return (_Kind = value); + } + set + { + Enums.ArtKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + } + } + } - } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Book.cs b/Jellyfin.Data/Entities/Book.cs index 30c89ae5c5..42d24e31d5 100644 --- a/Jellyfin.Data/Entities/Book.cs +++ b/Jellyfin.Data/Entities/Book.cs @@ -1,84 +1,69 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Book: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Book : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Book(): base() - { - BookMetadata = new System.Collections.Generic.HashSet(); - Releases = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Book() + { + BookMetadata = new HashSet(); + Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Book CreateBookUnsafe() - { - return new Book(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Book CreateBookUnsafe() + { + return new Book(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public Book(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public Book(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.BookMetadata = new System.Collections.Generic.HashSet(); - this.Releases = new System.Collections.Generic.HashSet(); + this.BookMetadata = new HashSet(); + this.Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static Book Create(Guid urlid, DateTime dateadded) - { - return new Book(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static Book Create(Guid urlid, DateTime dateadded) + { + return new Book(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection BookMetadata { get; protected set; } + [ForeignKey("BookMetadata_BookMetadata_Id")] + public virtual ICollection BookMetadata { get; protected set; } - public virtual ICollection Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } - } + } } diff --git a/Jellyfin.Data/Entities/BookMetadata.cs b/Jellyfin.Data/Entities/BookMetadata.cs index 3a28244d69..d52fe76051 100644 --- a/Jellyfin.Data/Entities/BookMetadata.cs +++ b/Jellyfin.Data/Entities/BookMetadata.cs @@ -1,123 +1,107 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class BookMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class BookMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected BookMetadata(): base() - { - Publishers = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected BookMetadata() + { + Publishers = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static BookMetadata CreateBookMetadataUnsafe() - { - return new BookMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static BookMetadata CreateBookMetadataUnsafe() + { + return new BookMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public BookMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Book _book0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public BookMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_book0 == null) throw new ArgumentNullException(nameof(_book0)); - _book0.BookMetadata.Add(this); + if (_book0 == null) throw new ArgumentNullException(nameof(_book0)); + _book0.BookMetadata.Add(this); - this.Publishers = new System.Collections.Generic.HashSet(); + this.Publishers = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Book _book0) - { - return new BookMetadata(title, language, dateadded, datemodified, _book0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0) + { + return new BookMetadata(title, language, dateadded, datemodified, _book0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for ISBN - /// - protected long? _ISBN; - /// - /// When provided in a partial class, allows value of ISBN to be changed before setting. - /// - partial void SetISBN(long? oldValue, ref long? newValue); - /// - /// When provided in a partial class, allows value of ISBN to be changed before returning. - /// - partial void GetISBN(ref long? result); + /// + /// Backing field for ISBN + /// + protected long? _ISBN; + /// + /// When provided in a partial class, allows value of ISBN to be changed before setting. + /// + partial void SetISBN(long? oldValue, ref long? newValue); + /// + /// When provided in a partial class, allows value of ISBN to be changed before returning. + /// + partial void GetISBN(ref long? result); - public long? ISBN - { - get - { - long? value = _ISBN; - GetISBN(ref value); - return (_ISBN = value); - } - set - { - long? oldValue = _ISBN; - SetISBN(oldValue, ref value); - if (oldValue != value) + public long? ISBN + { + get { - _ISBN = value; + long? value = _ISBN; + GetISBN(ref value); + return (_ISBN = value); } - } - } + set + { + long? oldValue = _ISBN; + SetISBN(oldValue, ref value); + if (oldValue != value) + { + _ISBN = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection Publishers { get; protected set; } + [ForeignKey("Company_Publishers_Id")] + public virtual ICollection Publishers { get; protected set; } - } + } } diff --git a/Jellyfin.Data/Entities/Chapter.cs b/Jellyfin.Data/Entities/Chapter.cs index 21a5dd73ee..d48cb9b627 100644 --- a/Jellyfin.Data/Entities/Chapter.cs +++ b/Jellyfin.Data/Entities/Chapter.cs @@ -1,274 +1,263 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Chapter - { - partial void Init(); + public partial class Chapter + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Chapter() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Chapter() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Chapter CreateChapterUnsafe() - { - return new Chapter(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Chapter CreateChapterUnsafe() + { + return new Chapter(); + } - /// - /// Public constructor with required data - /// - /// ISO-639-3 3-character language codes - /// - /// - public Chapter(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) - { - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + /// + /// Public constructor with required data + /// + /// ISO-639-3 3-character language codes + /// + /// + public Chapter(string language, long timestart, Release _release0) + { + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - this.TimeStart = timestart; + this.TimeStart = timestart; - if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); - _release0.Chapters.Add(this); + if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); + _release0.Chapters.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// ISO-639-3 3-character language codes - /// - /// - public static Chapter Create(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) - { - return new Chapter(language, timestart, _release0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// ISO-639-3 3-character language codes + /// + /// + public static Chapter Create(string language, long timestart, Release _release0) + { + return new Chapter(language, timestart, _release0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Language - /// - protected string _Language; - /// - /// When provided in a partial class, allows value of Language to be changed before setting. - /// - partial void SetLanguage(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Language to be changed before returning. - /// - partial void GetLanguage(ref string result); + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /// - /// Required, Min length = 3, Max length = 3 - /// ISO-639-3 3-character language codes - /// - [Required] - [MinLength(3)] - [MaxLength(3)] - [StringLength(3)] - public string Language - { - get - { - string value = _Language; - GetLanguage(ref value); - return (_Language = value); - } - set - { - string oldValue = _Language; - SetLanguage(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _Language = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } - - /// - /// Backing field for TimeStart - /// - protected long _TimeStart; - /// - /// When provided in a partial class, allows value of TimeStart to be changed before setting. - /// - partial void SetTimeStart(long oldValue, ref long newValue); - /// - /// When provided in a partial class, allows value of TimeStart to be changed before returning. - /// - partial void GetTimeStart(ref long result); - - /// - /// Required - /// - [Required] - public long TimeStart - { - get - { - long value = _TimeStart; - GetTimeStart(ref value); - return (_TimeStart = value); - } - set - { - long oldValue = _TimeStart; - SetTimeStart(oldValue, ref value); - if (oldValue != value) + set { - _TimeStart = value; + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } } - } - } + } - /// - /// Backing field for TimeEnd - /// - protected long? _TimeEnd; - /// - /// When provided in a partial class, allows value of TimeEnd to be changed before setting. - /// - partial void SetTimeEnd(long? oldValue, ref long? newValue); - /// - /// When provided in a partial class, allows value of TimeEnd to be changed before returning. - /// - partial void GetTimeEnd(ref long? result); + /// + /// Backing field for Language + /// + protected string _Language; + /// + /// When provided in a partial class, allows value of Language to be changed before setting. + /// + partial void SetLanguage(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Language to be changed before returning. + /// + partial void GetLanguage(ref string result); - public long? TimeEnd - { - get - { - long? value = _TimeEnd; - GetTimeEnd(ref value); - return (_TimeEnd = value); - } - set - { - long? oldValue = _TimeEnd; - SetTimeEnd(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// + [Required] + [MinLength(3)] + [MaxLength(3)] + [StringLength(3)] + public string Language + { + get { - _TimeEnd = value; + string value = _Language; + GetLanguage(ref value); + return (_Language = value); } - } - } + set + { + string oldValue = _Language; + SetLanguage(oldValue, ref value); + if (oldValue != value) + { + _Language = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for TimeStart + /// + protected long _TimeStart; + /// + /// When provided in a partial class, allows value of TimeStart to be changed before setting. + /// + partial void SetTimeStart(long oldValue, ref long newValue); + /// + /// When provided in a partial class, allows value of TimeStart to be changed before returning. + /// + partial void GetTimeStart(ref long result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public long TimeStart + { + get + { + long value = _TimeStart; + GetTimeStart(ref value); + return (_TimeStart = value); + } + set + { + long oldValue = _TimeStart; + SetTimeStart(oldValue, ref value); + if (oldValue != value) + { + _TimeStart = value; + } + } + } - } + /// + /// Backing field for TimeEnd + /// + protected long? _TimeEnd; + /// + /// When provided in a partial class, allows value of TimeEnd to be changed before setting. + /// + partial void SetTimeEnd(long? oldValue, ref long? newValue); + /// + /// When provided in a partial class, allows value of TimeEnd to be changed before returning. + /// + partial void GetTimeEnd(ref long? result); + + public long? TimeEnd + { + get + { + long? value = _TimeEnd; + GetTimeEnd(ref value); + return (_TimeEnd = value); + } + set + { + long? oldValue = _TimeEnd; + SetTimeEnd(oldValue, ref value); + if (oldValue != value) + { + _TimeEnd = value; + } + } + } + + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Collection.cs b/Jellyfin.Data/Entities/Collection.cs index 68979eb2fe..e2fa3a5bd3 100644 --- a/Jellyfin.Data/Entities/Collection.cs +++ b/Jellyfin.Data/Entities/Collection.cs @@ -1,131 +1,120 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Collection - { - partial void Init(); + public partial class Collection + { + partial void Init(); - /// - /// Default constructor - /// - public Collection() - { - CollectionItem = new System.Collections.Generic.LinkedList(); + /// + /// Default constructor + /// + public Collection() + { + CollectionItem = new LinkedList(); - Init(); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - public virtual ICollection CollectionItem { get; protected set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CollectionItem_CollectionItem_Id")] + public virtual ICollection CollectionItem { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/CollectionItem.cs b/Jellyfin.Data/Entities/CollectionItem.cs index 8e575e0a28..4a3d066396 100644 --- a/Jellyfin.Data/Entities/CollectionItem.cs +++ b/Jellyfin.Data/Entities/CollectionItem.cs @@ -1,151 +1,143 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CollectionItem - { - partial void Init(); + public partial class CollectionItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CollectionItem() - { - // NOTE: This class has one-to-one associations with CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected CollectionItem() + { + // NOTE: This class has one-to-one associations with CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static CollectionItem CreateCollectionItemUnsafe() - { - return new CollectionItem(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static CollectionItem CreateCollectionItemUnsafe() + { + return new CollectionItem(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - public CollectionItem(global::Jellyfin.Data.Entities.Collection _collection0, global::Jellyfin.Data.Entities.CollectionItem _collectionitem1, global::Jellyfin.Data.Entities.CollectionItem _collectionitem2) - { - // NOTE: This class has one-to-one associations with CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// + /// + /// + public CollectionItem(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2) + { + // NOTE: This class has one-to-one associations with CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - if (_collection0 == null) throw new ArgumentNullException(nameof(_collection0)); - _collection0.CollectionItem.Add(this); + if (_collection0 == null) throw new ArgumentNullException(nameof(_collection0)); + _collection0.CollectionItem.Add(this); - if (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1)); - _collectionitem1.Next = this; + if (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1)); + _collectionitem1.Next = this; - if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2)); - _collectionitem2.Previous = this; + if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2)); + _collectionitem2.Previous = this; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - public static CollectionItem Create(global::Jellyfin.Data.Entities.Collection _collection0, global::Jellyfin.Data.Entities.CollectionItem _collectionitem1, global::Jellyfin.Data.Entities.CollectionItem _collectionitem2) - { - return new CollectionItem(_collection0, _collectionitem1, _collectionitem2); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + public static CollectionItem Create(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2) + { + return new CollectionItem(_collection0, _collectionitem1, _collectionitem2); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } + protected set + { + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - /// - /// Required - /// - public virtual global::Jellyfin.Data.Entities.LibraryItem LibraryItem { get; set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ - /// - /// TODO check if this properly updated dependant and has the proper principal relationship - /// - public virtual global::Jellyfin.Data.Entities.CollectionItem Next { get; set; } + /// + /// Required + /// + [ForeignKey("LibraryItem_Id")] + public virtual LibraryItem LibraryItem { get; set; } - /// - /// TODO check if this properly updated dependant and has the proper principal relationship - /// - public virtual global::Jellyfin.Data.Entities.CollectionItem Previous { get; set; } + /// + /// TODO check if this properly updated dependant and has the proper principal relationship + /// + [ForeignKey("CollectionItem_Next_Id")] + public virtual CollectionItem Next { get; set; } - } + /// + /// TODO check if this properly updated dependant and has the proper principal relationship + /// + [ForeignKey("CollectionItem_Previous_Id")] + public virtual CollectionItem Previous { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/Company.cs b/Jellyfin.Data/Entities/Company.cs index 444ae9c564..0650271c65 100644 --- a/Jellyfin.Data/Entities/Company.cs +++ b/Jellyfin.Data/Entities/Company.cs @@ -1,147 +1,137 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Company - { - partial void Init(); + public partial class Company + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Company() - { - CompanyMetadata = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Company() + { + CompanyMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Company CreateCompanyUnsafe() - { - return new Company(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Company CreateCompanyUnsafe() + { + return new Company(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// - public Company(global::Jellyfin.Data.Entities.MovieMetadata _moviemetadata0, global::Jellyfin.Data.Entities.SeriesMetadata _seriesmetadata1, global::Jellyfin.Data.Entities.MusicAlbumMetadata _musicalbummetadata2, global::Jellyfin.Data.Entities.BookMetadata _bookmetadata3, global::Jellyfin.Data.Entities.Company _company4) - { - if (_moviemetadata0 == null) throw new ArgumentNullException(nameof(_moviemetadata0)); - _moviemetadata0.Studios.Add(this); + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + public Company(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4) + { + if (_moviemetadata0 == null) throw new ArgumentNullException(nameof(_moviemetadata0)); + _moviemetadata0.Studios.Add(this); - if (_seriesmetadata1 == null) throw new ArgumentNullException(nameof(_seriesmetadata1)); - _seriesmetadata1.Networks.Add(this); + if (_seriesmetadata1 == null) throw new ArgumentNullException(nameof(_seriesmetadata1)); + _seriesmetadata1.Networks.Add(this); - if (_musicalbummetadata2 == null) throw new ArgumentNullException(nameof(_musicalbummetadata2)); - _musicalbummetadata2.Labels.Add(this); + if (_musicalbummetadata2 == null) throw new ArgumentNullException(nameof(_musicalbummetadata2)); + _musicalbummetadata2.Labels.Add(this); - if (_bookmetadata3 == null) throw new ArgumentNullException(nameof(_bookmetadata3)); - _bookmetadata3.Publishers.Add(this); + if (_bookmetadata3 == null) throw new ArgumentNullException(nameof(_bookmetadata3)); + _bookmetadata3.Publishers.Add(this); - if (_company4 == null) throw new ArgumentNullException(nameof(_company4)); - _company4.Parent = this; + if (_company4 == null) throw new ArgumentNullException(nameof(_company4)); + _company4.Parent = this; - this.CompanyMetadata = new System.Collections.Generic.HashSet(); + this.CompanyMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - public static Company Create(global::Jellyfin.Data.Entities.MovieMetadata _moviemetadata0, global::Jellyfin.Data.Entities.SeriesMetadata _seriesmetadata1, global::Jellyfin.Data.Entities.MusicAlbumMetadata _musicalbummetadata2, global::Jellyfin.Data.Entities.BookMetadata _bookmetadata3, global::Jellyfin.Data.Entities.Company _company4) - { - return new Company(_moviemetadata0, _seriesmetadata1, _musicalbummetadata2, _bookmetadata3, _company4); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + public static Company Create(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4) + { + return new Company(_moviemetadata0, _seriesmetadata1, _musicalbummetadata2, _bookmetadata3, _company4); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } + protected set + { + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection CompanyMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CompanyMetadata_CompanyMetadata_Id")] + public virtual ICollection CompanyMetadata { get; protected set; } + [ForeignKey("Company_Parent_Id")] + public virtual Company Parent { get; set; } - public virtual global::Jellyfin.Data.Entities.Company Parent { get; set; } - - } + } } diff --git a/Jellyfin.Data/Entities/CompanyMetadata.cs b/Jellyfin.Data/Entities/CompanyMetadata.cs index 6d636e8846..b3ec9c1a7f 100644 --- a/Jellyfin.Data/Entities/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/CompanyMetadata.cs @@ -1,234 +1,216 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CompanyMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class CompanyMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CompanyMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected CompanyMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static CompanyMetadata CreateCompanyMetadataUnsafe() - { - return new CompanyMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static CompanyMetadata CreateCompanyMetadataUnsafe() + { + return new CompanyMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public CompanyMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Company _company0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public CompanyMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_company0 == null) throw new ArgumentNullException(nameof(_company0)); - _company0.CompanyMetadata.Add(this); + if (_company0 == null) throw new ArgumentNullException(nameof(_company0)); + _company0.CompanyMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Company _company0) - { - return new CompanyMetadata(title, language, dateadded, datemodified, _company0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0) + { + return new CompanyMetadata(title, language, dateadded, datemodified, _company0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Description - /// - protected string _Description; - /// - /// When provided in a partial class, allows value of Description to be changed before setting. - /// - partial void SetDescription(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Description to be changed before returning. - /// - partial void GetDescription(ref string result); + /// + /// Backing field for Description + /// + protected string _Description; + /// + /// When provided in a partial class, allows value of Description to be changed before setting. + /// + partial void SetDescription(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Description to be changed before returning. + /// + partial void GetDescription(ref string result); - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string Description - { - get - { - string value = _Description; - GetDescription(ref value); - return (_Description = value); - } - set - { - string oldValue = _Description; - SetDescription(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string Description + { + get { - _Description = value; + string value = _Description; + GetDescription(ref value); + return (_Description = value); } - } - } - - /// - /// Backing field for Headquarters - /// - protected string _Headquarters; - /// - /// When provided in a partial class, allows value of Headquarters to be changed before setting. - /// - partial void SetHeadquarters(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Headquarters to be changed before returning. - /// - partial void GetHeadquarters(ref string result); - - /// - /// Max length = 255 - /// - [MaxLength(255)] - [StringLength(255)] - public string Headquarters - { - get - { - string value = _Headquarters; - GetHeadquarters(ref value); - return (_Headquarters = value); - } - set - { - string oldValue = _Headquarters; - SetHeadquarters(oldValue, ref value); - if (oldValue != value) + set { - _Headquarters = value; + string oldValue = _Description; + SetDescription(oldValue, ref value); + if (oldValue != value) + { + _Description = value; + } } - } - } + } - /// - /// Backing field for Country - /// - protected string _Country; - /// - /// When provided in a partial class, allows value of Country to be changed before setting. - /// - partial void SetCountry(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Country to be changed before returning. - /// - partial void GetCountry(ref string result); + /// + /// Backing field for Headquarters + /// + protected string _Headquarters; + /// + /// When provided in a partial class, allows value of Headquarters to be changed before setting. + /// + partial void SetHeadquarters(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Headquarters to be changed before returning. + /// + partial void GetHeadquarters(ref string result); - /// - /// Max length = 2 - /// - [MaxLength(2)] - [StringLength(2)] - public string Country - { - get - { - string value = _Country; - GetCountry(ref value); - return (_Country = value); - } - set - { - string oldValue = _Country; - SetCountry(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 255 + /// + [MaxLength(255)] + [StringLength(255)] + public string Headquarters + { + get { - _Country = value; + string value = _Headquarters; + GetHeadquarters(ref value); + return (_Headquarters = value); } - } - } - - /// - /// Backing field for Homepage - /// - protected string _Homepage; - /// - /// When provided in a partial class, allows value of Homepage to be changed before setting. - /// - partial void SetHomepage(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Homepage to be changed before returning. - /// - partial void GetHomepage(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Homepage - { - get - { - string value = _Homepage; - GetHomepage(ref value); - return (_Homepage = value); - } - set - { - string oldValue = _Homepage; - SetHomepage(oldValue, ref value); - if (oldValue != value) + set { - _Homepage = value; + string oldValue = _Headquarters; + SetHeadquarters(oldValue, ref value); + if (oldValue != value) + { + _Headquarters = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for Country + /// + protected string _Country; + /// + /// When provided in a partial class, allows value of Country to be changed before setting. + /// + partial void SetCountry(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Country to be changed before returning. + /// + partial void GetCountry(ref string result); - } + /// + /// Max length = 2 + /// + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get + { + string value = _Country; + GetCountry(ref value); + return (_Country = value); + } + set + { + string oldValue = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } + } + } + + /// + /// Backing field for Homepage + /// + protected string _Homepage; + /// + /// When provided in a partial class, allows value of Homepage to be changed before setting. + /// + partial void SetHomepage(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Homepage to be changed before returning. + /// + partial void GetHomepage(ref string result); + + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Homepage + { + get + { + string value = _Homepage; + GetHomepage(ref value); + return (_Homepage = value); + } + set + { + string oldValue = _Homepage; + SetHomepage(oldValue, ref value); + if (oldValue != value) + { + _Homepage = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/CustomItem.cs b/Jellyfin.Data/Entities/CustomItem.cs index eb6d2752d3..2006717bf2 100644 --- a/Jellyfin.Data/Entities/CustomItem.cs +++ b/Jellyfin.Data/Entities/CustomItem.cs @@ -1,84 +1,68 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CustomItem: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class CustomItem : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CustomItem(): base() - { - CustomItemMetadata = new System.Collections.Generic.HashSet(); - Releases = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected CustomItem() + { + CustomItemMetadata = new HashSet(); + Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static CustomItem CreateCustomItemUnsafe() - { - return new CustomItem(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static CustomItem CreateCustomItemUnsafe() + { + return new CustomItem(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public CustomItem(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public CustomItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.CustomItemMetadata = new System.Collections.Generic.HashSet(); - this.Releases = new System.Collections.Generic.HashSet(); + this.CustomItemMetadata = new HashSet(); + this.Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static CustomItem Create(Guid urlid, DateTime dateadded) - { - return new CustomItem(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static CustomItem Create(Guid urlid, DateTime dateadded) + { + return new CustomItem(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CustomItemMetadata_CustomItemMetadata_Id")] + public virtual ICollection CustomItemMetadata { get; protected set; } - public virtual ICollection CustomItemMetadata { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } - public virtual ICollection Releases { get; protected set; } - - } + } } diff --git a/Jellyfin.Data/Entities/CustomItemMetadata.cs b/Jellyfin.Data/Entities/CustomItemMetadata.cs index f2c15d3fe3..e09e4467ac 100644 --- a/Jellyfin.Data/Entities/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/CustomItemMetadata.cs @@ -1,86 +1,67 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CustomItemMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class CustomItemMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected CustomItemMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected CustomItemMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static CustomItemMetadata CreateCustomItemMetadataUnsafe() - { - return new CustomItemMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static CustomItemMetadata CreateCustomItemMetadataUnsafe() + { + return new CustomItemMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public CustomItemMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.CustomItem _customitem0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public CustomItemMetadata(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_customitem0 == null) throw new ArgumentNullException(nameof(_customitem0)); - _customitem0.CustomItemMetadata.Add(this); + if (_customitem0 == null) throw new ArgumentNullException(nameof(_customitem0)); + _customitem0.CustomItemMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static CustomItemMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.CustomItem _customitem0) - { - return new CustomItemMetadata(title, language, dateadded, datemodified, _customitem0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static CustomItemMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0) + { + return new CustomItemMetadata(title, language, dateadded, datemodified, _customitem0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - } + } } diff --git a/Jellyfin.Data/Entities/Episode.cs b/Jellyfin.Data/Entities/Episode.cs index 3a23f0976f..6f6baa14de 100644 --- a/Jellyfin.Data/Entities/Episode.cs +++ b/Jellyfin.Data/Entities/Episode.cs @@ -1,127 +1,110 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Episode: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Episode : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Episode(): base() - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Episode() + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - Releases = new System.Collections.Generic.HashSet(); - EpisodeMetadata = new System.Collections.Generic.HashSet(); + Releases = new HashSet(); + EpisodeMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Episode CreateEpisodeUnsafe() - { - return new Episode(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Episode CreateEpisodeUnsafe() + { + return new Episode(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public Episode(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Season _season0) - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public Episode(Guid urlid, DateTime dateadded, Season _season0) + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - this.UrlId = urlid; + this.UrlId = urlid; - if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); - _season0.Episodes.Add(this); + if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.Episodes.Add(this); - this.Releases = new System.Collections.Generic.HashSet(); - this.EpisodeMetadata = new System.Collections.Generic.HashSet(); + this.Releases = new HashSet(); + this.EpisodeMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public static Episode Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Season _season0) - { - return new Episode(urlid, dateadded, _season0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public static Episode Create(Guid urlid, DateTime dateadded, Season _season0) + { + return new Episode(urlid, dateadded, _season0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for EpisodeNumber - /// - protected int? _EpisodeNumber; - /// - /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting. - /// - partial void SetEpisodeNumber(int? oldValue, ref int? newValue); - /// - /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning. - /// - partial void GetEpisodeNumber(ref int? result); + /// + /// Backing field for EpisodeNumber + /// + protected int? _EpisodeNumber; + /// + /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting. + /// + partial void SetEpisodeNumber(int? oldValue, ref int? newValue); + /// + /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning. + /// + partial void GetEpisodeNumber(ref int? result); - public int? EpisodeNumber - { - get - { - int? value = _EpisodeNumber; - GetEpisodeNumber(ref value); - return (_EpisodeNumber = value); - } - set - { - int? oldValue = _EpisodeNumber; - SetEpisodeNumber(oldValue, ref value); - if (oldValue != value) + public int? EpisodeNumber + { + get { - _EpisodeNumber = value; + int? value = _EpisodeNumber; + GetEpisodeNumber(ref value); + return (_EpisodeNumber = value); } - } - } + set + { + int? oldValue = _EpisodeNumber; + SetEpisodeNumber(oldValue, ref value); + if (oldValue != value) + { + _EpisodeNumber = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } + [ForeignKey("EpisodeMetadata_EpisodeMetadata_Id")] + public virtual ICollection EpisodeMetadata { get; protected set; } - public virtual ICollection Releases { get; protected set; } - - public virtual ICollection EpisodeMetadata { get; protected set; } - - } + } } diff --git a/Jellyfin.Data/Entities/EpisodeMetadata.cs b/Jellyfin.Data/Entities/EpisodeMetadata.cs index 963219140d..e5431bf223 100644 --- a/Jellyfin.Data/Entities/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/EpisodeMetadata.cs @@ -1,197 +1,179 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class EpisodeMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class EpisodeMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected EpisodeMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected EpisodeMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static EpisodeMetadata CreateEpisodeMetadataUnsafe() - { - return new EpisodeMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static EpisodeMetadata CreateEpisodeMetadataUnsafe() + { + return new EpisodeMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public EpisodeMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Episode _episode0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public EpisodeMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_episode0 == null) throw new ArgumentNullException(nameof(_episode0)); - _episode0.EpisodeMetadata.Add(this); + if (_episode0 == null) throw new ArgumentNullException(nameof(_episode0)); + _episode0.EpisodeMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Episode _episode0) - { - return new EpisodeMetadata(title, language, dateadded, datemodified, _episode0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0) + { + return new EpisodeMetadata(title, language, dateadded, datemodified, _episode0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Outline - /// - protected string _Outline; - /// - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// - partial void SetOutline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// - partial void GetOutline(ref string result); + /// + /// Backing field for Outline + /// + protected string _Outline; + /// + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// + partial void SetOutline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// + partial void GetOutline(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Outline - { - get - { - string value = _Outline; - GetOutline(ref value); - return (_Outline = value); - } - set - { - string oldValue = _Outline; - SetOutline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } - - /// - /// Backing field for Plot - /// - protected string _Plot; - /// - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// - partial void SetPlot(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// - partial void GetPlot(ref string result); - - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string Plot - { - get - { - string value = _Plot; - GetPlot(ref value); - return (_Plot = value); - } - set - { - string oldValue = _Plot; - SetPlot(oldValue, ref value); - if (oldValue != value) + set { - _Plot = value; + string oldValue = _Outline; + SetOutline(oldValue, ref value); + if (oldValue != value) + { + _Outline = value; + } } - } - } + } - /// - /// Backing field for Tagline - /// - protected string _Tagline; - /// - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// - partial void SetTagline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// - partial void GetTagline(ref string result); + /// + /// Backing field for Plot + /// + protected string _Plot; + /// + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// + partial void SetPlot(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// + partial void GetPlot(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Tagline - { - get - { - string value = _Tagline; - GetTagline(ref value); - return (_Tagline = value); - } - set - { - string oldValue = _Tagline; - SetTagline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get { - _Tagline = value; + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); } - } - } + set + { + string oldValue = _Plot; + SetPlot(oldValue, ref value); + if (oldValue != value) + { + _Plot = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for Tagline + /// + protected string _Tagline; + /// + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// + partial void SetTagline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// + partial void GetTagline(ref string result); - } + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Tagline + { + get + { + string value = _Tagline; + GetTagline(ref value); + return (_Tagline = value); + } + set + { + string oldValue = _Tagline; + SetTagline(oldValue, ref value); + if (oldValue != value) + { + _Tagline = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Genre.cs b/Jellyfin.Data/Entities/Genre.cs index 982600553e..38f289a8e3 100644 --- a/Jellyfin.Data/Entities/Genre.cs +++ b/Jellyfin.Data/Entities/Genre.cs @@ -1,163 +1,152 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Genre - { - partial void Init(); + public partial class Genre + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Genre() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Genre() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Genre CreateGenreUnsafe() - { - return new Genre(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Genre CreateGenreUnsafe() + { + return new Genre(); + } - /// - /// Public constructor with required data - /// - /// - /// - public Genre(string name, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + /// + /// Public constructor with required data + /// + /// + /// + public Genre(string name, Metadata _metadata0) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Genres.Add(this); + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Genres.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static Genre Create(string name, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new Genre(name, _metadata0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static Genre Create(string name, Metadata _metadata0) + { + return new Genre(name, _metadata0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - internal string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Indexed, Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Name + /// + internal string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Indexed, Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index ff19e9b019..54f9f49057 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,115 +1,109 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Group - { - partial void Init(); + public partial class Group + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Group() - { - GroupPermissions = new System.Collections.Generic.HashSet(); - ProviderMappings = new System.Collections.Generic.HashSet(); - Preferences = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Group() + { + GroupPermissions = new HashSet(); + ProviderMappings = new HashSet(); + Preferences = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Group CreateGroupUnsafe() - { - return new Group(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Group CreateGroupUnsafe() + { + return new Group(); + } - /// - /// Public constructor with required data - /// - /// - /// - public Group(string name, global::Jellyfin.Data.Entities.User _user0) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + /// + /// Public constructor with required data + /// + /// + /// + public Group(string name, User _user0) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.Groups.Add(this); + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Groups.Add(this); - this.GroupPermissions = new System.Collections.Generic.HashSet(); - this.ProviderMappings = new System.Collections.Generic.HashSet(); - this.Preferences = new System.Collections.Generic.HashSet(); + this.GroupPermissions = new HashSet(); + this.ProviderMappings = new HashSet(); + this.Preferences = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static Group Create(string name, global::Jellyfin.Data.Entities.User _user0) - { - return new Group(name, _user0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static Group Create(string name, User _user0) + { + return new Group(name, _user0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id { get; protected set; } + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string Name { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Name { get; set; } - /// - /// Concurrency token - /// - [Timestamp] - public Byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection GroupPermissions { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection ProviderMappings { get; protected set; } + [ForeignKey("Permission_GroupPermissions_Id")] + public virtual ICollection GroupPermissions { get; protected set; } - public virtual ICollection Preferences { get; protected set; } + [ForeignKey("ProviderMapping_ProviderMappings_Id")] + public virtual ICollection ProviderMappings { get; protected set; } - } + [ForeignKey("Preference_Preferences_Id")] + public virtual ICollection Preferences { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/Library.cs b/Jellyfin.Data/Entities/Library.cs index 19ca142947..c11c09e916 100644 --- a/Jellyfin.Data/Entities/Library.cs +++ b/Jellyfin.Data/Entities/Library.cs @@ -1,158 +1,147 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Library - { - partial void Init(); + public partial class Library + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Library() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Library() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Library CreateLibraryUnsafe() - { - return new Library(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Library CreateLibraryUnsafe() + { + return new Library(); + } - /// - /// Public constructor with required data - /// - /// - public Library(string name) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + /// + /// Public constructor with required data + /// + /// + public Library(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - public static Library Create(string name) - { - return new Library(name); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + public static Library Create(string name) + { + return new Library(name); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Required, Max length = 1024 - /// - [Required] - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required, Max length = 1024 + /// + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/LibraryItem.cs b/Jellyfin.Data/Entities/LibraryItem.cs index 1987196d69..af6c640b97 100644 --- a/Jellyfin.Data/Entities/LibraryItem.cs +++ b/Jellyfin.Data/Entities/LibraryItem.cs @@ -1,180 +1,170 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public abstract partial class LibraryItem - { - partial void Init(); + public abstract partial class LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to being abstract. - /// - protected LibraryItem() - { - Init(); - } + /// + /// Default constructor. Protected due to being abstract. + /// + protected LibraryItem() + { + Init(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - protected LibraryItem(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + protected LibraryItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - Init(); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for UrlId - /// - internal Guid _UrlId; - /// - /// When provided in a partial class, allows value of UrlId to be changed before setting. - /// - partial void SetUrlId(Guid oldValue, ref Guid newValue); - /// - /// When provided in a partial class, allows value of UrlId to be changed before returning. - /// - partial void GetUrlId(ref Guid result); - - /// - /// Indexed, Required - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - [Required] - public Guid UrlId - { - get - { - Guid value = _UrlId; - GetUrlId(ref value); - return (_UrlId = value); - } - set - { - Guid oldValue = _UrlId; - SetUrlId(oldValue, ref value); - if (oldValue != value) + protected set { - _UrlId = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for DateAdded - /// - protected DateTime _DateAdded; - /// - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// - partial void GetDateAdded(ref DateTime result); + /// + /// Backing field for UrlId + /// + internal Guid _UrlId; + /// + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// + partial void GetUrlId(ref Guid result); - /// - /// Required - /// - [Required] - public DateTime DateAdded - { - get - { - DateTime value = _DateAdded; - GetDateAdded(ref value); - return (_DateAdded = value); - } - internal set - { - DateTime oldValue = _DateAdded; - SetDateAdded(oldValue, ref value); - if (oldValue != value) + /// + /// Indexed, Required + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + [Required] + public Guid UrlId + { + get { - _DateAdded = value; + Guid value = _UrlId; + GetUrlId(ref value); + return (_UrlId = value); } - } - } + set + { + Guid oldValue = _UrlId; + SetUrlId(oldValue, ref value); + if (oldValue != value) + { + _UrlId = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for DateAdded + /// + protected DateTime _DateAdded; + /// + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// + partial void GetDateAdded(ref DateTime result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public DateTime DateAdded + { + get + { + DateTime value = _DateAdded; + GetDateAdded(ref value); + return (_DateAdded = value); + } + internal set + { + DateTime oldValue = _DateAdded; + SetDateAdded(oldValue, ref value); + if (oldValue != value) + { + _DateAdded = value; + } + } + } - /// - /// Required - /// - public virtual global::Jellyfin.Data.Entities.LibraryRoot LibraryRoot { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// + /// Required + /// + [ForeignKey("LibraryRoot_Id")] + public virtual LibraryRoot LibraryRoot { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/LibraryRoot.cs b/Jellyfin.Data/Entities/LibraryRoot.cs index 015fc4ea98..bbc23e1c96 100644 --- a/Jellyfin.Data/Entities/LibraryRoot.cs +++ b/Jellyfin.Data/Entities/LibraryRoot.cs @@ -1,202 +1,192 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class LibraryRoot - { - partial void Init(); + public partial class LibraryRoot + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected LibraryRoot() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected LibraryRoot() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static LibraryRoot CreateLibraryRootUnsafe() - { - return new LibraryRoot(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static LibraryRoot CreateLibraryRootUnsafe() + { + return new LibraryRoot(); + } - /// - /// Public constructor with required data - /// - /// Absolute Path - public LibraryRoot(string path) - { - if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); - this.Path = path; + /// + /// Public constructor with required data + /// + /// Absolute Path + public LibraryRoot(string path) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// Absolute Path - public static LibraryRoot Create(string path) - { - return new LibraryRoot(path); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// Absolute Path + public static LibraryRoot Create(string path) + { + return new LibraryRoot(path); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Path - /// - protected string _Path; - /// - /// When provided in a partial class, allows value of Path to be changed before setting. - /// - partial void SetPath(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Path to be changed before returning. - /// - partial void GetPath(ref string result); - - /// - /// Required, Max length = 65535 - /// Absolute Path - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string Path - { - get - { - string value = _Path; - GetPath(ref value); - return (_Path = value); - } - set - { - string oldValue = _Path; - SetPath(oldValue, ref value); - if (oldValue != value) + protected set { - _Path = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for NetworkPath - /// - protected string _NetworkPath; - /// - /// When provided in a partial class, allows value of NetworkPath to be changed before setting. - /// - partial void SetNetworkPath(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of NetworkPath to be changed before returning. - /// - partial void GetNetworkPath(ref string result); + /// + /// Backing field for Path + /// + protected string _Path; + /// + /// When provided in a partial class, allows value of Path to be changed before setting. + /// + partial void SetPath(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Path to be changed before returning. + /// + partial void GetPath(ref string result); - /// - /// Max length = 65535 - /// Absolute network path, for example for transcoding sattelites. - /// - [MaxLength(65535)] - [StringLength(65535)] - public string NetworkPath - { - get - { - string value = _NetworkPath; - GetNetworkPath(ref value); - return (_NetworkPath = value); - } - set - { - string oldValue = _NetworkPath; - SetNetworkPath(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Max length = 65535 + /// Absolute Path + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get { - _NetworkPath = value; + string value = _Path; + GetPath(ref value); + return (_Path = value); } - } - } + set + { + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for NetworkPath + /// + protected string _NetworkPath; + /// + /// When provided in a partial class, allows value of NetworkPath to be changed before setting. + /// + partial void SetNetworkPath(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of NetworkPath to be changed before returning. + /// + partial void GetNetworkPath(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Max length = 65535 + /// Absolute network path, for example for transcoding sattelites. + /// + [MaxLength(65535)] + [StringLength(65535)] + public string NetworkPath + { + get + { + string value = _NetworkPath; + GetNetworkPath(ref value); + return (_NetworkPath = value); + } + set + { + string oldValue = _NetworkPath; + SetNetworkPath(oldValue, ref value); + if (oldValue != value) + { + _NetworkPath = value; + } + } + } - /// - /// Required - /// - public virtual global::Jellyfin.Data.Entities.Library Library { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// + /// Required + /// + [ForeignKey("Library_Id")] + public virtual Library Library { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/MediaFile.cs b/Jellyfin.Data/Entities/MediaFile.cs index 2a47a96325..719539e5c2 100644 --- a/Jellyfin.Data/Entities/MediaFile.cs +++ b/Jellyfin.Data/Entities/MediaFile.cs @@ -1,209 +1,200 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MediaFile - { - partial void Init(); + public partial class MediaFile + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MediaFile() - { - MediaFileStreams = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MediaFile() + { + MediaFileStreams = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MediaFile CreateMediaFileUnsafe() - { - return new MediaFile(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MediaFile CreateMediaFileUnsafe() + { + return new MediaFile(); + } - /// - /// Public constructor with required data - /// - /// Relative to the LibraryRoot - /// - /// - public MediaFile(string path, global::Jellyfin.Data.Enums.MediaFileKind kind, global::Jellyfin.Data.Entities.Release _release0) - { - if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); - this.Path = path; + /// + /// Public constructor with required data + /// + /// Relative to the LibraryRoot + /// + /// + public MediaFile(string path, Enums.MediaFileKind kind, Release _release0) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; - this.Kind = kind; + this.Kind = kind; - if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); - _release0.MediaFiles.Add(this); + if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); + _release0.MediaFiles.Add(this); - this.MediaFileStreams = new System.Collections.Generic.HashSet(); + this.MediaFileStreams = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// Relative to the LibraryRoot - /// - /// - public static MediaFile Create(string path, global::Jellyfin.Data.Enums.MediaFileKind kind, global::Jellyfin.Data.Entities.Release _release0) - { - return new MediaFile(path, kind, _release0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// Relative to the LibraryRoot + /// + /// + public static MediaFile Create(string path, Enums.MediaFileKind kind, Release _release0) + { + return new MediaFile(path, kind, _release0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Path - /// - protected string _Path; - /// - /// When provided in a partial class, allows value of Path to be changed before setting. - /// - partial void SetPath(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Path to be changed before returning. - /// - partial void GetPath(ref string result); - - /// - /// Required, Max length = 65535 - /// Relative to the LibraryRoot - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string Path - { - get - { - string value = _Path; - GetPath(ref value); - return (_Path = value); - } - set - { - string oldValue = _Path; - SetPath(oldValue, ref value); - if (oldValue != value) + protected set { - _Path = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Kind - /// - protected global::Jellyfin.Data.Enums.MediaFileKind _Kind; - /// - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// - partial void SetKind(global::Jellyfin.Data.Enums.MediaFileKind oldValue, ref global::Jellyfin.Data.Enums.MediaFileKind newValue); - /// - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// - partial void GetKind(ref global::Jellyfin.Data.Enums.MediaFileKind result); + /// + /// Backing field for Path + /// + protected string _Path; + /// + /// When provided in a partial class, allows value of Path to be changed before setting. + /// + partial void SetPath(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Path to be changed before returning. + /// + partial void GetPath(ref string result); - /// - /// Required - /// - [Required] - public global::Jellyfin.Data.Enums.MediaFileKind Kind - { - get - { - global::Jellyfin.Data.Enums.MediaFileKind value = _Kind; - GetKind(ref value); - return (_Kind = value); - } - set - { - global::Jellyfin.Data.Enums.MediaFileKind oldValue = _Kind; - SetKind(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Max length = 65535 + /// Relative to the LibraryRoot + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get { - _Kind = value; + string value = _Path; + GetPath(ref value); + return (_Path = value); } - } - } + set + { + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Kind + /// + protected Enums.MediaFileKind _Kind; + /// + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// + partial void SetKind(Enums.MediaFileKind oldValue, ref Enums.MediaFileKind newValue); + /// + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// + partial void GetKind(ref Enums.MediaFileKind result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public Enums.MediaFileKind Kind + { + get + { + Enums.MediaFileKind value = _Kind; + GetKind(ref value); + return (_Kind = value); + } + set + { + Enums.MediaFileKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + } + } + } - public virtual ICollection MediaFileStreams { get; protected set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + [ForeignKey("MediaFileStream_MediaFileStreams_Id")] + public virtual ICollection MediaFileStreams { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/MediaFileStream.cs b/Jellyfin.Data/Entities/MediaFileStream.cs index 6593d3cf75..7b3399731a 100644 --- a/Jellyfin.Data/Entities/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/MediaFileStream.cs @@ -1,160 +1,149 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MediaFileStream - { - partial void Init(); + public partial class MediaFileStream + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MediaFileStream() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MediaFileStream() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MediaFileStream CreateMediaFileStreamUnsafe() - { - return new MediaFileStream(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MediaFileStream CreateMediaFileStreamUnsafe() + { + return new MediaFileStream(); + } - /// - /// Public constructor with required data - /// - /// - /// - public MediaFileStream(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) - { - this.StreamNumber = streamnumber; + /// + /// Public constructor with required data + /// + /// + /// + public MediaFileStream(int streamnumber, MediaFile _mediafile0) + { + this.StreamNumber = streamnumber; - if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0)); - _mediafile0.MediaFileStreams.Add(this); + if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0)); + _mediafile0.MediaFileStreams.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static MediaFileStream Create(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) - { - return new MediaFileStream(streamnumber, _mediafile0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static MediaFileStream Create(int streamnumber, MediaFile _mediafile0) + { + return new MediaFileStream(streamnumber, _mediafile0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for StreamNumber - /// - protected int _StreamNumber; - /// - /// When provided in a partial class, allows value of StreamNumber to be changed before setting. - /// - partial void SetStreamNumber(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of StreamNumber to be changed before returning. - /// - partial void GetStreamNumber(ref int result); - - /// - /// Required - /// - [Required] - public int StreamNumber - { - get - { - int value = _StreamNumber; - GetStreamNumber(ref value); - return (_StreamNumber = value); - } - set - { - int oldValue = _StreamNumber; - SetStreamNumber(oldValue, ref value); - if (oldValue != value) + protected set { - _StreamNumber = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for StreamNumber + /// + protected int _StreamNumber; + /// + /// When provided in a partial class, allows value of StreamNumber to be changed before setting. + /// + partial void SetStreamNumber(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of StreamNumber to be changed before returning. + /// + partial void GetStreamNumber(ref int result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public int StreamNumber + { + get + { + int value = _StreamNumber; + GetStreamNumber(ref value); + return (_StreamNumber = value); + } + set + { + int oldValue = _StreamNumber; + SetStreamNumber(oldValue, ref value); + if (oldValue != value) + { + _StreamNumber = value; + } + } + } - } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Metadata.cs b/Jellyfin.Data/Entities/Metadata.cs index 6057017e94..467ee68226 100644 --- a/Jellyfin.Data/Entities/Metadata.cs +++ b/Jellyfin.Data/Entities/Metadata.cs @@ -1,385 +1,380 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public abstract partial class Metadata - { - partial void Init(); + public abstract partial class Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to being abstract. - /// - protected Metadata() - { - PersonRoles = new System.Collections.Generic.HashSet(); - Genres = new System.Collections.Generic.HashSet(); - Artwork = new System.Collections.Generic.HashSet(); - Ratings = new System.Collections.Generic.HashSet(); - Sources = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to being abstract. + /// + protected Metadata() + { + PersonRoles = new HashSet(); + Genres = new HashSet(); + Artwork = new HashSet(); + Ratings = new HashSet(); + Sources = new HashSet(); - Init(); - } + Init(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - protected Metadata(string title, string language, DateTime dateadded, DateTime datemodified) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + protected Metadata(string title, string language, DateTime dateadded, DateTime datemodified) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - this.PersonRoles = new System.Collections.Generic.HashSet(); - this.Genres = new System.Collections.Generic.HashSet(); - this.Artwork = new System.Collections.Generic.HashSet(); - this.Ratings = new System.Collections.Generic.HashSet(); - this.Sources = new System.Collections.Generic.HashSet(); + this.PersonRoles = new HashSet(); + this.Genres = new HashSet(); + this.Artwork = new HashSet(); + this.Ratings = new HashSet(); + this.Sources = new HashSet(); - Init(); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Title - /// - protected string _Title; - /// - /// When provided in a partial class, allows value of Title to be changed before setting. - /// - partial void SetTitle(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Title to be changed before returning. - /// - partial void GetTitle(ref string result); - - /// - /// Required, Max length = 1024 - /// The title or name of the object - /// - [Required] - [MaxLength(1024)] - [StringLength(1024)] - public string Title - { - get - { - string value = _Title; - GetTitle(ref value); - return (_Title = value); - } - set - { - string oldValue = _Title; - SetTitle(oldValue, ref value); - if (oldValue != value) + protected set { - _Title = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for OriginalTitle - /// - protected string _OriginalTitle; - /// - /// When provided in a partial class, allows value of OriginalTitle to be changed before setting. - /// - partial void SetOriginalTitle(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of OriginalTitle to be changed before returning. - /// - partial void GetOriginalTitle(ref string result); + /// + /// Backing field for Title + /// + protected string _Title; + /// + /// When provided in a partial class, allows value of Title to be changed before setting. + /// + partial void SetTitle(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Title to be changed before returning. + /// + partial void GetTitle(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string OriginalTitle - { - get - { - string value = _OriginalTitle; - GetOriginalTitle(ref value); - return (_OriginalTitle = value); - } - set - { - string oldValue = _OriginalTitle; - SetOriginalTitle(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Max length = 1024 + /// The title or name of the object + /// + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Title + { + get { - _OriginalTitle = value; + string value = _Title; + GetTitle(ref value); + return (_Title = value); } - } - } - - /// - /// Backing field for SortTitle - /// - protected string _SortTitle; - /// - /// When provided in a partial class, allows value of SortTitle to be changed before setting. - /// - partial void SetSortTitle(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of SortTitle to be changed before returning. - /// - partial void GetSortTitle(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string SortTitle - { - get - { - string value = _SortTitle; - GetSortTitle(ref value); - return (_SortTitle = value); - } - set - { - string oldValue = _SortTitle; - SetSortTitle(oldValue, ref value); - if (oldValue != value) + set { - _SortTitle = value; + string oldValue = _Title; + SetTitle(oldValue, ref value); + if (oldValue != value) + { + _Title = value; + } } - } - } + } - /// - /// Backing field for Language - /// - protected string _Language; - /// - /// When provided in a partial class, allows value of Language to be changed before setting. - /// - partial void SetLanguage(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Language to be changed before returning. - /// - partial void GetLanguage(ref string result); + /// + /// Backing field for OriginalTitle + /// + protected string _OriginalTitle; + /// + /// When provided in a partial class, allows value of OriginalTitle to be changed before setting. + /// + partial void SetOriginalTitle(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of OriginalTitle to be changed before returning. + /// + partial void GetOriginalTitle(ref string result); - /// - /// Required, Min length = 3, Max length = 3 - /// ISO-639-3 3-character language codes - /// - [Required] - [MinLength(3)] - [MaxLength(3)] - [StringLength(3)] - public string Language - { - get - { - string value = _Language; - GetLanguage(ref value); - return (_Language = value); - } - set - { - string oldValue = _Language; - SetLanguage(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string OriginalTitle + { + get { - _Language = value; + string value = _OriginalTitle; + GetOriginalTitle(ref value); + return (_OriginalTitle = value); } - } - } - - /// - /// Backing field for ReleaseDate - /// - protected DateTimeOffset? _ReleaseDate; - /// - /// When provided in a partial class, allows value of ReleaseDate to be changed before setting. - /// - partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// - /// When provided in a partial class, allows value of ReleaseDate to be changed before returning. - /// - partial void GetReleaseDate(ref DateTimeOffset? result); - - public DateTimeOffset? ReleaseDate - { - get - { - DateTimeOffset? value = _ReleaseDate; - GetReleaseDate(ref value); - return (_ReleaseDate = value); - } - set - { - DateTimeOffset? oldValue = _ReleaseDate; - SetReleaseDate(oldValue, ref value); - if (oldValue != value) + set { - _ReleaseDate = value; + string oldValue = _OriginalTitle; + SetOriginalTitle(oldValue, ref value); + if (oldValue != value) + { + _OriginalTitle = value; + } } - } - } + } - /// - /// Backing field for DateAdded - /// - protected DateTime _DateAdded; - /// - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// - partial void GetDateAdded(ref DateTime result); + /// + /// Backing field for SortTitle + /// + protected string _SortTitle; + /// + /// When provided in a partial class, allows value of SortTitle to be changed before setting. + /// + partial void SetSortTitle(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of SortTitle to be changed before returning. + /// + partial void GetSortTitle(ref string result); - /// - /// Required - /// - [Required] - public DateTime DateAdded - { - get - { - DateTime value = _DateAdded; - GetDateAdded(ref value); - return (_DateAdded = value); - } - internal set - { - DateTime oldValue = _DateAdded; - SetDateAdded(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string SortTitle + { + get { - _DateAdded = value; + string value = _SortTitle; + GetSortTitle(ref value); + return (_SortTitle = value); } - } - } - - /// - /// Backing field for DateModified - /// - protected DateTime _DateModified; - /// - /// When provided in a partial class, allows value of DateModified to be changed before setting. - /// - partial void SetDateModified(DateTime oldValue, ref DateTime newValue); - /// - /// When provided in a partial class, allows value of DateModified to be changed before returning. - /// - partial void GetDateModified(ref DateTime result); - - /// - /// Required - /// - [Required] - public DateTime DateModified - { - get - { - DateTime value = _DateModified; - GetDateModified(ref value); - return (_DateModified = value); - } - internal set - { - DateTime oldValue = _DateModified; - SetDateModified(oldValue, ref value); - if (oldValue != value) + set { - _DateModified = value; + string oldValue = _SortTitle; + SetSortTitle(oldValue, ref value); + if (oldValue != value) + { + _SortTitle = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Language + /// + protected string _Language; + /// + /// When provided in a partial class, allows value of Language to be changed before setting. + /// + partial void SetLanguage(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Language to be changed before returning. + /// + partial void GetLanguage(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// + [Required] + [MinLength(3)] + [MaxLength(3)] + [StringLength(3)] + public string Language + { + get + { + string value = _Language; + GetLanguage(ref value); + return (_Language = value); + } + set + { + string oldValue = _Language; + SetLanguage(oldValue, ref value); + if (oldValue != value) + { + _Language = value; + } + } + } - public virtual ICollection PersonRoles { get; protected set; } + /// + /// Backing field for ReleaseDate + /// + protected DateTimeOffset? _ReleaseDate; + /// + /// When provided in a partial class, allows value of ReleaseDate to be changed before setting. + /// + partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// + /// When provided in a partial class, allows value of ReleaseDate to be changed before returning. + /// + partial void GetReleaseDate(ref DateTimeOffset? result); - public virtual ICollection Genres { get; protected set; } + public DateTimeOffset? ReleaseDate + { + get + { + DateTimeOffset? value = _ReleaseDate; + GetReleaseDate(ref value); + return (_ReleaseDate = value); + } + set + { + DateTimeOffset? oldValue = _ReleaseDate; + SetReleaseDate(oldValue, ref value); + if (oldValue != value) + { + _ReleaseDate = value; + } + } + } - public virtual ICollection Artwork { get; protected set; } + /// + /// Backing field for DateAdded + /// + protected DateTime _DateAdded; + /// + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// + partial void GetDateAdded(ref DateTime result); - public virtual ICollection Ratings { get; protected set; } + /// + /// Required + /// + [Required] + public DateTime DateAdded + { + get + { + DateTime value = _DateAdded; + GetDateAdded(ref value); + return (_DateAdded = value); + } + internal set + { + DateTime oldValue = _DateAdded; + SetDateAdded(oldValue, ref value); + if (oldValue != value) + { + _DateAdded = value; + } + } + } - public virtual ICollection Sources { get; protected set; } + /// + /// Backing field for DateModified + /// + protected DateTime _DateModified; + /// + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// + partial void GetDateModified(ref DateTime result); - } + /// + /// Required + /// + [Required] + public DateTime DateModified + { + get + { + DateTime value = _DateModified; + GetDateModified(ref value); + return (_DateModified = value); + } + internal set + { + DateTime oldValue = _DateModified; + SetDateModified(oldValue, ref value); + if (oldValue != value) + { + _DateModified = value; + } + } + } + + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection PersonRoles { get; protected set; } + + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection Genres { get; protected set; } + + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection Artwork { get; protected set; } + + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection Ratings { get; protected set; } + + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection Sources { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/MetadataProvider.cs b/Jellyfin.Data/Entities/MetadataProvider.cs index 3a8f5854eb..4e4f107fb9 100644 --- a/Jellyfin.Data/Entities/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/MetadataProvider.cs @@ -1,158 +1,147 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MetadataProvider - { - partial void Init(); + public partial class MetadataProvider + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MetadataProvider() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MetadataProvider() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MetadataProvider CreateMetadataProviderUnsafe() - { - return new MetadataProvider(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MetadataProvider CreateMetadataProviderUnsafe() + { + return new MetadataProvider(); + } - /// - /// Public constructor with required data - /// - /// - public MetadataProvider(string name) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + /// + /// Public constructor with required data + /// + /// + public MetadataProvider(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - public static MetadataProvider Create(string name) - { - return new MetadataProvider(name); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + public static MetadataProvider Create(string name) + { + return new MetadataProvider(name); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Required, Max length = 1024 - /// - [Required] - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required, Max length = 1024 + /// + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/MetadataProviderId.cs b/Jellyfin.Data/Entities/MetadataProviderId.cs index 87ff19e26d..926f223dea 100644 --- a/Jellyfin.Data/Entities/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/MetadataProviderId.cs @@ -1,189 +1,179 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MetadataProviderId - { - partial void Init(); + public partial class MetadataProviderId + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MetadataProviderId() - { - // NOTE: This class has one-to-one associations with MetadataProviderId. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MetadataProviderId() + { + // NOTE: This class has one-to-one associations with MetadataProviderId. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MetadataProviderId CreateMetadataProviderIdUnsafe() - { - return new MetadataProviderId(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MetadataProviderId CreateMetadataProviderIdUnsafe() + { + return new MetadataProviderId(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// - public MetadataProviderId(string providerid, global::Jellyfin.Data.Entities.Metadata _metadata0, global::Jellyfin.Data.Entities.Person _person1, global::Jellyfin.Data.Entities.PersonRole _personrole2, global::Jellyfin.Data.Entities.RatingSource _ratingsource3) - { - // NOTE: This class has one-to-one associations with MetadataProviderId. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + public MetadataProviderId(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3) + { + // NOTE: This class has one-to-one associations with MetadataProviderId. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - if (string.IsNullOrEmpty(providerid)) throw new ArgumentNullException(nameof(providerid)); - this.ProviderId = providerid; + if (string.IsNullOrEmpty(providerid)) throw new ArgumentNullException(nameof(providerid)); + this.ProviderId = providerid; - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Sources.Add(this); + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Sources.Add(this); - if (_person1 == null) throw new ArgumentNullException(nameof(_person1)); - _person1.Sources.Add(this); + if (_person1 == null) throw new ArgumentNullException(nameof(_person1)); + _person1.Sources.Add(this); - if (_personrole2 == null) throw new ArgumentNullException(nameof(_personrole2)); - _personrole2.Sources.Add(this); + if (_personrole2 == null) throw new ArgumentNullException(nameof(_personrole2)); + _personrole2.Sources.Add(this); - if (_ratingsource3 == null) throw new ArgumentNullException(nameof(_ratingsource3)); - _ratingsource3.Source = this; + if (_ratingsource3 == null) throw new ArgumentNullException(nameof(_ratingsource3)); + _ratingsource3.Source = this; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - public static MetadataProviderId Create(string providerid, global::Jellyfin.Data.Entities.Metadata _metadata0, global::Jellyfin.Data.Entities.Person _person1, global::Jellyfin.Data.Entities.PersonRole _personrole2, global::Jellyfin.Data.Entities.RatingSource _ratingsource3) - { - return new MetadataProviderId(providerid, _metadata0, _person1, _personrole2, _ratingsource3); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + public static MetadataProviderId Create(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3) + { + return new MetadataProviderId(providerid, _metadata0, _person1, _personrole2, _ratingsource3); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for ProviderId - /// - protected string _ProviderId; - /// - /// When provided in a partial class, allows value of ProviderId to be changed before setting. - /// - partial void SetProviderId(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of ProviderId to be changed before returning. - /// - partial void GetProviderId(ref string result); - - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string ProviderId - { - get - { - string value = _ProviderId; - GetProviderId(ref value); - return (_ProviderId = value); - } - set - { - string oldValue = _ProviderId; - SetProviderId(oldValue, ref value); - if (oldValue != value) + protected set { - _ProviderId = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for ProviderId + /// + protected string _ProviderId; + /// + /// When provided in a partial class, allows value of ProviderId to be changed before setting. + /// + partial void SetProviderId(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of ProviderId to be changed before returning. + /// + partial void GetProviderId(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string ProviderId + { + get + { + string value = _ProviderId; + GetProviderId(ref value); + return (_ProviderId = value); + } + set + { + string oldValue = _ProviderId; + SetProviderId(oldValue, ref value); + if (oldValue != value) + { + _ProviderId = value; + } + } + } - /// - /// Required - /// - public virtual global::Jellyfin.Data.Entities.MetadataProvider MetadataProvider { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// + /// Required + /// + [ForeignKey("MetadataProvider_Id")] + public virtual MetadataProvider MetadataProvider { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/Movie.cs b/Jellyfin.Data/Entities/Movie.cs index dfcc05a943..b359b42fcd 100644 --- a/Jellyfin.Data/Entities/Movie.cs +++ b/Jellyfin.Data/Entities/Movie.cs @@ -1,84 +1,69 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Movie: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Movie : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Movie(): base() - { - Releases = new System.Collections.Generic.HashSet(); - MovieMetadata = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Movie() + { + Releases = new HashSet(); + MovieMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Movie CreateMovieUnsafe() - { - return new Movie(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Movie CreateMovieUnsafe() + { + return new Movie(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public Movie(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public Movie(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.Releases = new System.Collections.Generic.HashSet(); - this.MovieMetadata = new System.Collections.Generic.HashSet(); + this.Releases = new HashSet(); + this.MovieMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static Movie Create(Guid urlid, DateTime dateadded) - { - return new Movie(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static Movie Create(Guid urlid, DateTime dateadded) + { + return new Movie(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } - public virtual ICollection MovieMetadata { get; protected set; } + [ForeignKey("MovieMetadata_MovieMetadata_Id")] + public virtual ICollection MovieMetadata { get; protected set; } - } + } } diff --git a/Jellyfin.Data/Entities/MovieMetadata.cs b/Jellyfin.Data/Entities/MovieMetadata.cs index bd847da8fa..319ae94e5a 100644 --- a/Jellyfin.Data/Entities/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/MovieMetadata.cs @@ -1,239 +1,223 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MovieMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class MovieMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MovieMetadata(): base() - { - Studios = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MovieMetadata() + { + Studios = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MovieMetadata CreateMovieMetadataUnsafe() - { - return new MovieMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MovieMetadata CreateMovieMetadataUnsafe() + { + return new MovieMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public MovieMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Movie _movie0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public MovieMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); - _movie0.MovieMetadata.Add(this); + if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); + _movie0.MovieMetadata.Add(this); - this.Studios = new System.Collections.Generic.HashSet(); + this.Studios = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Movie _movie0) - { - return new MovieMetadata(title, language, dateadded, datemodified, _movie0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0) + { + return new MovieMetadata(title, language, dateadded, datemodified, _movie0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Outline - /// - protected string _Outline; - /// - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// - partial void SetOutline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// - partial void GetOutline(ref string result); + /// + /// Backing field for Outline + /// + protected string _Outline; + /// + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// + partial void SetOutline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// + partial void GetOutline(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Outline - { - get - { - string value = _Outline; - GetOutline(ref value); - return (_Outline = value); - } - set - { - string oldValue = _Outline; - SetOutline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } - - /// - /// Backing field for Plot - /// - protected string _Plot; - /// - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// - partial void SetPlot(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// - partial void GetPlot(ref string result); - - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string Plot - { - get - { - string value = _Plot; - GetPlot(ref value); - return (_Plot = value); - } - set - { - string oldValue = _Plot; - SetPlot(oldValue, ref value); - if (oldValue != value) + set { - _Plot = value; + string oldValue = _Outline; + SetOutline(oldValue, ref value); + if (oldValue != value) + { + _Outline = value; + } } - } - } + } - /// - /// Backing field for Tagline - /// - protected string _Tagline; - /// - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// - partial void SetTagline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// - partial void GetTagline(ref string result); + /// + /// Backing field for Plot + /// + protected string _Plot; + /// + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// + partial void SetPlot(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// + partial void GetPlot(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Tagline - { - get - { - string value = _Tagline; - GetTagline(ref value); - return (_Tagline = value); - } - set - { - string oldValue = _Tagline; - SetTagline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get { - _Tagline = value; + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); } - } - } - - /// - /// Backing field for Country - /// - protected string _Country; - /// - /// When provided in a partial class, allows value of Country to be changed before setting. - /// - partial void SetCountry(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Country to be changed before returning. - /// - partial void GetCountry(ref string result); - - /// - /// Max length = 2 - /// - [MaxLength(2)] - [StringLength(2)] - public string Country - { - get - { - string value = _Country; - GetCountry(ref value); - return (_Country = value); - } - set - { - string oldValue = _Country; - SetCountry(oldValue, ref value); - if (oldValue != value) + set { - _Country = value; + string oldValue = _Plot; + SetPlot(oldValue, ref value); + if (oldValue != value) + { + _Plot = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for Tagline + /// + protected string _Tagline; + /// + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// + partial void SetTagline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// + partial void GetTagline(ref string result); - public virtual ICollection Studios { get; protected set; } + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Tagline + { + get + { + string value = _Tagline; + GetTagline(ref value); + return (_Tagline = value); + } + set + { + string oldValue = _Tagline; + SetTagline(oldValue, ref value); + if (oldValue != value) + { + _Tagline = value; + } + } + } - } + /// + /// Backing field for Country + /// + protected string _Country; + /// + /// When provided in a partial class, allows value of Country to be changed before setting. + /// + partial void SetCountry(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Country to be changed before returning. + /// + partial void GetCountry(ref string result); + + /// + /// Max length = 2 + /// + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get + { + string value = _Country; + GetCountry(ref value); + return (_Country = value); + } + set + { + string oldValue = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Company_Studios_Id")] + public virtual ICollection Studios { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/MusicAlbum.cs b/Jellyfin.Data/Entities/MusicAlbum.cs index 417f2595bd..00cb8fe007 100644 --- a/Jellyfin.Data/Entities/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/MusicAlbum.cs @@ -1,84 +1,68 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MusicAlbum: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class MusicAlbum : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MusicAlbum(): base() - { - MusicAlbumMetadata = new System.Collections.Generic.HashSet(); - Tracks = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MusicAlbum() + { + MusicAlbumMetadata = new HashSet(); + Tracks = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MusicAlbum CreateMusicAlbumUnsafe() - { - return new MusicAlbum(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MusicAlbum CreateMusicAlbumUnsafe() + { + return new MusicAlbum(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public MusicAlbum(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public MusicAlbum(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.MusicAlbumMetadata = new System.Collections.Generic.HashSet(); - this.Tracks = new System.Collections.Generic.HashSet(); + this.MusicAlbumMetadata = new HashSet(); + this.Tracks = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static MusicAlbum Create(Guid urlid, DateTime dateadded) - { - return new MusicAlbum(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static MusicAlbum Create(Guid urlid, DateTime dateadded) + { + return new MusicAlbum(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id")] + public virtual ICollection MusicAlbumMetadata { get; protected set; } - public virtual ICollection MusicAlbumMetadata { get; protected set; } + [ForeignKey("Track_Tracks_Id")] + public virtual ICollection Tracks { get; protected set; } - public virtual ICollection Tracks { get; protected set; } - - } + } } diff --git a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs index cd72ecba51..b52ca65646 100644 --- a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs @@ -1,202 +1,187 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MusicAlbumMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class MusicAlbumMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected MusicAlbumMetadata(): base() - { - Labels = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected MusicAlbumMetadata() + { + Labels = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe() - { - return new MusicAlbumMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe() + { + return new MusicAlbumMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public MusicAlbumMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public MusicAlbumMetadata(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); - _musicalbum0.MusicAlbumMetadata.Add(this); + if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.MusicAlbumMetadata.Add(this); - this.Labels = new System.Collections.Generic.HashSet(); + this.Labels = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static MusicAlbumMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) - { - return new MusicAlbumMetadata(title, language, dateadded, datemodified, _musicalbum0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static MusicAlbumMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0) + { + return new MusicAlbumMetadata(title, language, dateadded, datemodified, _musicalbum0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Barcode - /// - protected string _Barcode; - /// - /// When provided in a partial class, allows value of Barcode to be changed before setting. - /// - partial void SetBarcode(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Barcode to be changed before returning. - /// - partial void GetBarcode(ref string result); + /// + /// Backing field for Barcode + /// + protected string _Barcode; + /// + /// When provided in a partial class, allows value of Barcode to be changed before setting. + /// + partial void SetBarcode(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Barcode to be changed before returning. + /// + partial void GetBarcode(ref string result); - /// - /// Max length = 255 - /// - [MaxLength(255)] - [StringLength(255)] - public string Barcode - { - get - { - string value = _Barcode; - GetBarcode(ref value); - return (_Barcode = value); - } - set - { - string oldValue = _Barcode; - SetBarcode(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 255 + /// + [MaxLength(255)] + [StringLength(255)] + public string Barcode + { + get { - _Barcode = value; + string value = _Barcode; + GetBarcode(ref value); + return (_Barcode = value); } - } - } - - /// - /// Backing field for LabelNumber - /// - protected string _LabelNumber; - /// - /// When provided in a partial class, allows value of LabelNumber to be changed before setting. - /// - partial void SetLabelNumber(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of LabelNumber to be changed before returning. - /// - partial void GetLabelNumber(ref string result); - - /// - /// Max length = 255 - /// - [MaxLength(255)] - [StringLength(255)] - public string LabelNumber - { - get - { - string value = _LabelNumber; - GetLabelNumber(ref value); - return (_LabelNumber = value); - } - set - { - string oldValue = _LabelNumber; - SetLabelNumber(oldValue, ref value); - if (oldValue != value) + set { - _LabelNumber = value; + string oldValue = _Barcode; + SetBarcode(oldValue, ref value); + if (oldValue != value) + { + _Barcode = value; + } } - } - } + } - /// - /// Backing field for Country - /// - protected string _Country; - /// - /// When provided in a partial class, allows value of Country to be changed before setting. - /// - partial void SetCountry(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Country to be changed before returning. - /// - partial void GetCountry(ref string result); + /// + /// Backing field for LabelNumber + /// + protected string _LabelNumber; + /// + /// When provided in a partial class, allows value of LabelNumber to be changed before setting. + /// + partial void SetLabelNumber(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of LabelNumber to be changed before returning. + /// + partial void GetLabelNumber(ref string result); - /// - /// Max length = 2 - /// - [MaxLength(2)] - [StringLength(2)] - public string Country - { - get - { - string value = _Country; - GetCountry(ref value); - return (_Country = value); - } - set - { - string oldValue = _Country; - SetCountry(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 255 + /// + [MaxLength(255)] + [StringLength(255)] + public string LabelNumber + { + get { - _Country = value; + string value = _LabelNumber; + GetLabelNumber(ref value); + return (_LabelNumber = value); } - } - } + set + { + string oldValue = _LabelNumber; + SetLabelNumber(oldValue, ref value); + if (oldValue != value) + { + _LabelNumber = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for Country + /// + protected string _Country; + /// + /// When provided in a partial class, allows value of Country to be changed before setting. + /// + partial void SetCountry(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Country to be changed before returning. + /// + partial void GetCountry(ref string result); - public virtual ICollection Labels { get; protected set; } + /// + /// Max length = 2 + /// + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get + { + string value = _Country; + GetCountry(ref value); + return (_Country = value); + } + set + { + string oldValue = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } + } + } - } + /************************************************************************* + * Navigation properties + *************************************************************************/ + + [ForeignKey("Company_Labels_Id")] + public virtual ICollection Labels { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index a717fc83fe..0b5b52cbd0 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,152 +1,144 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Permission - { - partial void Init(); + public partial class Permission + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Permission() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Permission() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Permission CreatePermissionUnsafe() - { - return new Permission(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Permission CreatePermissionUnsafe() + { + return new Permission(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - public Permission(global::Jellyfin.Data.Enums.PermissionKind kind, bool value, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - this.Kind = kind; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + public Permission(Enums.PermissionKind kind, bool value, User _user0, Group _group1) + { + this.Kind = kind; - this.Value = value; + this.Value = value; - if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.Permissions.Add(this); + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Permissions.Add(this); - if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.GroupPermissions.Add(this); + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.GroupPermissions.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - public static Permission Create(global::Jellyfin.Data.Enums.PermissionKind kind, bool value, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - return new Permission(kind, value, _user0, _group1); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + public static Permission Create(Enums.PermissionKind kind, bool value, User _user0, Group _group1) + { + return new Permission(kind, value, _user0, _group1); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id { get; protected set; } + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } - /// - /// Backing field for Kind - /// - protected global::Jellyfin.Data.Enums.PermissionKind _Kind; - /// - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// - partial void SetKind(global::Jellyfin.Data.Enums.PermissionKind oldValue, ref global::Jellyfin.Data.Enums.PermissionKind newValue); - /// - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// - partial void GetKind(ref global::Jellyfin.Data.Enums.PermissionKind result); + /// + /// Backing field for Kind + /// + protected Enums.PermissionKind _Kind; + /// + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// + partial void SetKind(Enums.PermissionKind oldValue, ref Enums.PermissionKind newValue); + /// + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// + partial void GetKind(ref Enums.PermissionKind result); - /// - /// Required - /// - [Required] - public global::Jellyfin.Data.Enums.PermissionKind Kind - { - get - { - global::Jellyfin.Data.Enums.PermissionKind value = _Kind; - GetKind(ref value); - return (_Kind = value); - } - set - { - global::Jellyfin.Data.Enums.PermissionKind oldValue = _Kind; - SetKind(oldValue, ref value); - if (oldValue != value) + /// + /// Required + /// + [Required] + public Enums.PermissionKind Kind + { + get { - _Kind = value; - OnPropertyChanged(); + Enums.PermissionKind value = _Kind; + GetKind(ref value); + return (_Kind = value); } - } - } + set + { + Enums.PermissionKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + OnPropertyChanged(); + } + } + } - /// - /// Required - /// - [Required] - public bool Value { get; set; } + /// + /// Required + /// + [Required] + public bool Value { get; set; } - /// - /// Concurrency token - /// - [Timestamp] - public Byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual event PropertyChangedEventHandler PropertyChanged; + /************************************************************************* + * Navigation properties + *************************************************************************/ - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } + public virtual event PropertyChangedEventHandler PropertyChanged; - } + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + } } diff --git a/Jellyfin.Data/Entities/PermissionKind.cs b/Jellyfin.Data/Entities/PermissionKind.cs deleted file mode 100644 index 971298674a..0000000000 --- a/Jellyfin.Data/Entities/PermissionKind.cs +++ /dev/null @@ -1,40 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - -namespace Jellyfin.Data.Enums -{ - public enum PermissionKind : Int32 - { - IsAdministrator, - IsHidden, - IsDisabled, - BlockUnrateditems, - EnbleSharedDeviceControl, - EnableRemoteAccess, - EnableLiveTvManagement, - EnableLiveTvAccess, - EnableMediaPlayback, - EnableAudioPlaybackTranscoding, - EnableVideoPlaybackTranscoding, - EnableContentDeletion, - EnableContentDownloading, - EnableSyncTranscoding, - EnableMediaConversion, - EnableAllDevices, - EnableAllChannels, - EnableAllFolders, - EnablePublicSharing, - AccessSchedules - } -} diff --git a/Jellyfin.Data/Entities/Person.cs b/Jellyfin.Data/Entities/Person.cs index 3437b9581d..d893b7e394 100644 --- a/Jellyfin.Data/Entities/Person.cs +++ b/Jellyfin.Data/Entities/Person.cs @@ -1,312 +1,302 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Person - { - partial void Init(); + public partial class Person + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Person() - { - Sources = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Person() + { + Sources = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Person CreatePersonUnsafe() - { - return new Person(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Person CreatePersonUnsafe() + { + return new Person(); + } - /// - /// Public constructor with required data - /// - /// - /// - public Person(Guid urlid, string name, DateTime dateadded, DateTime datemodified) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// + /// + public Person(Guid urlid, string name, DateTime dateadded, DateTime datemodified) + { + this.UrlId = urlid; - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - this.Sources = new System.Collections.Generic.HashSet(); + this.Sources = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) - { - return new Person(urlid, name, dateadded, datemodified); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) + { + return new Person(urlid, name, dateadded, datemodified); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for UrlId - /// - protected Guid _UrlId; - /// - /// When provided in a partial class, allows value of UrlId to be changed before setting. - /// - partial void SetUrlId(Guid oldValue, ref Guid newValue); - /// - /// When provided in a partial class, allows value of UrlId to be changed before returning. - /// - partial void GetUrlId(ref Guid result); - - /// - /// Required - /// - [Required] - public Guid UrlId - { - get - { - Guid value = _UrlId; - GetUrlId(ref value); - return (_UrlId = value); - } - set - { - Guid oldValue = _UrlId; - SetUrlId(oldValue, ref value); - if (oldValue != value) + protected set { - _UrlId = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); + /// + /// Backing field for UrlId + /// + protected Guid _UrlId; + /// + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// + partial void GetUrlId(ref Guid result); - /// - /// Required, Max length = 1024 - /// - [Required] - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + /// + /// Required + /// + [Required] + public Guid UrlId + { + get { - _Name = value; + Guid value = _UrlId; + GetUrlId(ref value); + return (_UrlId = value); } - } - } - - /// - /// Backing field for SourceId - /// - protected string _SourceId; - /// - /// When provided in a partial class, allows value of SourceId to be changed before setting. - /// - partial void SetSourceId(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of SourceId to be changed before returning. - /// - partial void GetSourceId(ref string result); - - /// - /// Max length = 255 - /// - [MaxLength(255)] - [StringLength(255)] - public string SourceId - { - get - { - string value = _SourceId; - GetSourceId(ref value); - return (_SourceId = value); - } - set - { - string oldValue = _SourceId; - SetSourceId(oldValue, ref value); - if (oldValue != value) + set { - _SourceId = value; + Guid oldValue = _UrlId; + SetUrlId(oldValue, ref value); + if (oldValue != value) + { + _UrlId = value; + } } - } - } + } - /// - /// Backing field for DateAdded - /// - protected DateTime _DateAdded; - /// - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// - partial void GetDateAdded(ref DateTime result); + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /// - /// Required - /// - [Required] - public DateTime DateAdded - { - get - { - DateTime value = _DateAdded; - GetDateAdded(ref value); - return (_DateAdded = value); - } - internal set - { - DateTime oldValue = _DateAdded; - SetDateAdded(oldValue, ref value); - if (oldValue != value) + /// + /// Required, Max length = 1024 + /// + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _DateAdded = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } - - /// - /// Backing field for DateModified - /// - protected DateTime _DateModified; - /// - /// When provided in a partial class, allows value of DateModified to be changed before setting. - /// - partial void SetDateModified(DateTime oldValue, ref DateTime newValue); - /// - /// When provided in a partial class, allows value of DateModified to be changed before returning. - /// - partial void GetDateModified(ref DateTime result); - - /// - /// Required - /// - [Required] - public DateTime DateModified - { - get - { - DateTime value = _DateModified; - GetDateModified(ref value); - return (_DateModified = value); - } - internal set - { - DateTime oldValue = _DateModified; - SetDateModified(oldValue, ref value); - if (oldValue != value) + set { - _DateModified = value; + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for SourceId + /// + protected string _SourceId; + /// + /// When provided in a partial class, allows value of SourceId to be changed before setting. + /// + partial void SetSourceId(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of SourceId to be changed before returning. + /// + partial void GetSourceId(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Max length = 255 + /// + [MaxLength(255)] + [StringLength(255)] + public string SourceId + { + get + { + string value = _SourceId; + GetSourceId(ref value); + return (_SourceId = value); + } + set + { + string oldValue = _SourceId; + SetSourceId(oldValue, ref value); + if (oldValue != value) + { + _SourceId = value; + } + } + } - public virtual ICollection Sources { get; protected set; } + /// + /// Backing field for DateAdded + /// + protected DateTime _DateAdded; + /// + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// + partial void GetDateAdded(ref DateTime result); - } + /// + /// Required + /// + [Required] + public DateTime DateAdded + { + get + { + DateTime value = _DateAdded; + GetDateAdded(ref value); + return (_DateAdded = value); + } + internal set + { + DateTime oldValue = _DateAdded; + SetDateAdded(oldValue, ref value); + if (oldValue != value) + { + _DateAdded = value; + } + } + } + + /// + /// Backing field for DateModified + /// + protected DateTime _DateModified; + /// + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// + partial void GetDateModified(ref DateTime result); + + /// + /// Required + /// + [Required] + public DateTime DateModified + { + get + { + DateTime value = _DateModified; + GetDateModified(ref value); + return (_DateModified = value); + } + internal set + { + DateTime oldValue = _DateModified; + SetDateModified(oldValue, ref value); + if (oldValue != value) + { + _DateModified = value; + } + } + } + + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MetadataProviderId_Sources_Id")] + public virtual ICollection Sources { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/PersonRole.cs b/Jellyfin.Data/Entities/PersonRole.cs index d8e2dbc11a..9bd12c7fb0 100644 --- a/Jellyfin.Data/Entities/PersonRole.cs +++ b/Jellyfin.Data/Entities/PersonRole.cs @@ -1,215 +1,209 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class PersonRole - { - partial void Init(); + public partial class PersonRole + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected PersonRole() - { - // NOTE: This class has one-to-one associations with PersonRole. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected PersonRole() + { + // NOTE: This class has one-to-one associations with PersonRole. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - Sources = new System.Collections.Generic.HashSet(); + Sources = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static PersonRole CreatePersonRoleUnsafe() - { - return new PersonRole(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static PersonRole CreatePersonRoleUnsafe() + { + return new PersonRole(); + } - /// - /// Public constructor with required data - /// - /// - /// - public PersonRole(global::Jellyfin.Data.Enums.PersonRoleType type, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - // NOTE: This class has one-to-one associations with PersonRole. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// + /// + public PersonRole(Enums.PersonRoleType type, Metadata _metadata0) + { + // NOTE: This class has one-to-one associations with PersonRole. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - this.Type = type; + this.Type = type; - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.PersonRoles.Add(this); + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.PersonRoles.Add(this); - this.Sources = new System.Collections.Generic.HashSet(); + this.Sources = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static PersonRole Create(global::Jellyfin.Data.Enums.PersonRoleType type, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new PersonRole(type, _metadata0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static PersonRole Create(Enums.PersonRoleType type, Metadata _metadata0) + { + return new PersonRole(type, _metadata0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Role - /// - protected string _Role; - /// - /// When provided in a partial class, allows value of Role to be changed before setting. - /// - partial void SetRole(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Role to be changed before returning. - /// - partial void GetRole(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Role - { - get - { - string value = _Role; - GetRole(ref value); - return (_Role = value); - } - set - { - string oldValue = _Role; - SetRole(oldValue, ref value); - if (oldValue != value) + protected set { - _Role = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Type - /// - protected global::Jellyfin.Data.Enums.PersonRoleType _Type; - /// - /// When provided in a partial class, allows value of Type to be changed before setting. - /// - partial void SetType(global::Jellyfin.Data.Enums.PersonRoleType oldValue, ref global::Jellyfin.Data.Enums.PersonRoleType newValue); - /// - /// When provided in a partial class, allows value of Type to be changed before returning. - /// - partial void GetType(ref global::Jellyfin.Data.Enums.PersonRoleType result); + /// + /// Backing field for Role + /// + protected string _Role; + /// + /// When provided in a partial class, allows value of Role to be changed before setting. + /// + partial void SetRole(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Role to be changed before returning. + /// + partial void GetRole(ref string result); - /// - /// Required - /// - [Required] - public global::Jellyfin.Data.Enums.PersonRoleType Type - { - get - { - global::Jellyfin.Data.Enums.PersonRoleType value = _Type; - GetType(ref value); - return (_Type = value); - } - set - { - global::Jellyfin.Data.Enums.PersonRoleType oldValue = _Type; - SetType(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Role + { + get { - _Type = value; + string value = _Role; + GetRole(ref value); + return (_Role = value); } - } - } + set + { + string oldValue = _Role; + SetRole(oldValue, ref value); + if (oldValue != value) + { + _Role = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Type + /// + protected Enums.PersonRoleType _Type; + /// + /// When provided in a partial class, allows value of Type to be changed before setting. + /// + partial void SetType(Enums.PersonRoleType oldValue, ref Enums.PersonRoleType newValue); + /// + /// When provided in a partial class, allows value of Type to be changed before returning. + /// + partial void GetType(ref Enums.PersonRoleType result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public Enums.PersonRoleType Type + { + get + { + Enums.PersonRoleType value = _Type; + GetType(ref value); + return (_Type = value); + } + set + { + Enums.PersonRoleType oldValue = _Type; + SetType(oldValue, ref value); + if (oldValue != value) + { + _Type = value; + } + } + } - /// - /// Required - /// - public virtual global::Jellyfin.Data.Entities.Person Person { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - public virtual global::Jellyfin.Data.Entities.Artwork Artwork { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection Sources { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ - } + /// + /// Required + /// + [ForeignKey("Person_Id")] + + public virtual Person Person { get; set; } + + [ForeignKey("Artwork_Artwork_Id")] + public virtual Artwork Artwork { get; set; } + + [ForeignKey("MetadataProviderId_Sources_Id")] + public virtual ICollection Sources { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/Photo.cs b/Jellyfin.Data/Entities/Photo.cs index 16c97fef54..7abe628913 100644 --- a/Jellyfin.Data/Entities/Photo.cs +++ b/Jellyfin.Data/Entities/Photo.cs @@ -1,84 +1,68 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Photo: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Photo : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Photo(): base() - { - PhotoMetadata = new System.Collections.Generic.HashSet(); - Releases = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Photo() + { + PhotoMetadata = new HashSet(); + Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Photo CreatePhotoUnsafe() - { - return new Photo(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Photo CreatePhotoUnsafe() + { + return new Photo(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public Photo(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public Photo(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.PhotoMetadata = new System.Collections.Generic.HashSet(); - this.Releases = new System.Collections.Generic.HashSet(); + this.PhotoMetadata = new HashSet(); + this.Releases = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static Photo Create(Guid urlid, DateTime dateadded) - { - return new Photo(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static Photo Create(Guid urlid, DateTime dateadded) + { + return new Photo(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("PhotoMetadata_PhotoMetadata_Id")] + public virtual ICollection PhotoMetadata { get; protected set; } - public virtual ICollection PhotoMetadata { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } - public virtual ICollection Releases { get; protected set; } - - } + } } diff --git a/Jellyfin.Data/Entities/PhotoMetadata.cs b/Jellyfin.Data/Entities/PhotoMetadata.cs index 9c47d022e9..c5502f707a 100644 --- a/Jellyfin.Data/Entities/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/PhotoMetadata.cs @@ -1,86 +1,68 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class PhotoMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class PhotoMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected PhotoMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected PhotoMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static PhotoMetadata CreatePhotoMetadataUnsafe() - { - return new PhotoMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static PhotoMetadata CreatePhotoMetadataUnsafe() + { + return new PhotoMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public PhotoMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Photo _photo0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public PhotoMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_photo0 == null) throw new ArgumentNullException(nameof(_photo0)); - _photo0.PhotoMetadata.Add(this); + if (_photo0 == null) throw new ArgumentNullException(nameof(_photo0)); + _photo0.PhotoMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static PhotoMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Photo _photo0) - { - return new PhotoMetadata(title, language, dateadded, datemodified, _photo0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static PhotoMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Photo _photo0) + { + return new PhotoMetadata(title, language, dateadded, datemodified, _photo0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - } + } } diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 3d69ea2f3a..505f52e6b0 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -1,117 +1,107 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Preference - { - partial void Init(); + public partial class Preference + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Preference() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Preference() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Preference CreatePreferenceUnsafe() - { - return new Preference(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Preference CreatePreferenceUnsafe() + { + return new Preference(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - public Preference(global::Jellyfin.Data.Enums.PreferenceKind kind, string value, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - this.Kind = kind; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + public Preference(Enums.PreferenceKind kind, string value, User _user0, Group _group1) + { + this.Kind = kind; - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); - this.Value = value; + if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value)); + this.Value = value; - if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.Preferences.Add(this); + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Preferences.Add(this); - if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.Preferences.Add(this); + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.Preferences.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - public static Preference Create(global::Jellyfin.Data.Enums.PreferenceKind kind, string value, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - return new Preference(kind, value, _user0, _group1); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + public static Preference Create(Enums.PreferenceKind kind, string value, User _user0, Group _group1) + { + return new Preference(kind, value, _user0, _group1); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id { get; protected set; } + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } - /// - /// Required - /// - [Required] - public global::Jellyfin.Data.Enums.PreferenceKind Kind { get; set; } + /// + /// Required + /// + [Required] + public Enums.PreferenceKind Kind { get; set; } - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string Value { get; set; } + /// + /// Required, Max length = 65535 + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Value { get; set; } - /// - /// Concurrency token - /// - [Timestamp] - public Byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - } + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/PreferenceKind.cs b/Jellyfin.Data/Entities/PreferenceKind.cs deleted file mode 100644 index e6673afb1b..0000000000 --- a/Jellyfin.Data/Entities/PreferenceKind.cs +++ /dev/null @@ -1,27 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - -namespace Jellyfin.Data.Enums -{ - public enum PreferenceKind : Int32 - { - MaxParentalRating, - BlockedTags, - RemoteClientBitrateLimit, - EnabledDevices, - EnabledChannels, - EnabledFolders, - EnableContentDeletionFromFolders - } -} diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs index e50a01489c..6197bd97b7 100644 --- a/Jellyfin.Data/Entities/ProviderMapping.cs +++ b/Jellyfin.Data/Entities/ProviderMapping.cs @@ -1,133 +1,123 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class ProviderMapping - { - partial void Init(); + public partial class ProviderMapping + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected ProviderMapping() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected ProviderMapping() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static ProviderMapping CreateProviderMappingUnsafe() - { - return new ProviderMapping(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static ProviderMapping CreateProviderMappingUnsafe() + { + return new ProviderMapping(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// - public ProviderMapping(string providername, string providersecrets, string providerdata, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - if (string.IsNullOrEmpty(providername)) throw new ArgumentNullException(nameof(providername)); - this.ProviderName = providername; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + public ProviderMapping(string providername, string providersecrets, string providerdata, User _user0, Group _group1) + { + if (string.IsNullOrEmpty(providername)) throw new ArgumentNullException(nameof(providername)); + this.ProviderName = providername; - if (string.IsNullOrEmpty(providersecrets)) throw new ArgumentNullException(nameof(providersecrets)); - this.ProviderSecrets = providersecrets; + if (string.IsNullOrEmpty(providersecrets)) throw new ArgumentNullException(nameof(providersecrets)); + this.ProviderSecrets = providersecrets; - if (string.IsNullOrEmpty(providerdata)) throw new ArgumentNullException(nameof(providerdata)); - this.ProviderData = providerdata; + if (string.IsNullOrEmpty(providerdata)) throw new ArgumentNullException(nameof(providerdata)); + this.ProviderData = providerdata; - if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.ProviderMappings.Add(this); + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.ProviderMappings.Add(this); - if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.ProviderMappings.Add(this); + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.ProviderMappings.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - public static ProviderMapping Create(string providername, string providersecrets, string providerdata, global::Jellyfin.Data.Entities.User _user0, global::Jellyfin.Data.Entities.Group _group1) - { - return new ProviderMapping(providername, providersecrets, providerdata, _user0, _group1); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + public static ProviderMapping Create(string providername, string providersecrets, string providerdata, User _user0, Group _group1) + { + return new ProviderMapping(providername, providersecrets, providerdata, _user0, _group1); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id { get; protected set; } + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string ProviderName { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string ProviderName { get; set; } - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderSecrets { get; set; } + /// + /// Required, Max length = 65535 + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderSecrets { get; set; } - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderData { get; set; } + /// + /// Required, Max length = 65535 + /// + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderData { get; set; } - /// - /// Concurrency token - /// - [Timestamp] - public Byte[] Timestamp { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - } + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } } diff --git a/Jellyfin.Data/Entities/Rating.cs b/Jellyfin.Data/Entities/Rating.cs index b1098a1d7d..f70ea8b338 100644 --- a/Jellyfin.Data/Entities/Rating.cs +++ b/Jellyfin.Data/Entities/Rating.cs @@ -1,197 +1,187 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Rating - { - partial void Init(); + public partial class Rating + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Rating() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Rating() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Rating CreateRatingUnsafe() - { - return new Rating(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Rating CreateRatingUnsafe() + { + return new Rating(); + } - /// - /// Public constructor with required data - /// - /// - /// - public Rating(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - this.Value = value; + /// + /// Public constructor with required data + /// + /// + /// + public Rating(double value, Metadata _metadata0) + { + this.Value = value; - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Ratings.Add(this); + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Ratings.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - public static Rating Create(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new Rating(value, _metadata0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + public static Rating Create(double value, Metadata _metadata0) + { + return new Rating(value, _metadata0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Value - /// - protected double _Value; - /// - /// When provided in a partial class, allows value of Value to be changed before setting. - /// - partial void SetValue(double oldValue, ref double newValue); - /// - /// When provided in a partial class, allows value of Value to be changed before returning. - /// - partial void GetValue(ref double result); - - /// - /// Required - /// - [Required] - public double Value - { - get - { - double value = _Value; - GetValue(ref value); - return (_Value = value); - } - set - { - double oldValue = _Value; - SetValue(oldValue, ref value); - if (oldValue != value) + protected set { - _Value = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for Votes - /// - protected int? _Votes; - /// - /// When provided in a partial class, allows value of Votes to be changed before setting. - /// - partial void SetVotes(int? oldValue, ref int? newValue); - /// - /// When provided in a partial class, allows value of Votes to be changed before returning. - /// - partial void GetVotes(ref int? result); + /// + /// Backing field for Value + /// + protected double _Value; + /// + /// When provided in a partial class, allows value of Value to be changed before setting. + /// + partial void SetValue(double oldValue, ref double newValue); + /// + /// When provided in a partial class, allows value of Value to be changed before returning. + /// + partial void GetValue(ref double result); - public int? Votes - { - get - { - int? value = _Votes; - GetVotes(ref value); - return (_Votes = value); - } - set - { - int? oldValue = _Votes; - SetVotes(oldValue, ref value); - if (oldValue != value) + /// + /// Required + /// + [Required] + public double Value + { + get { - _Votes = value; + double value = _Value; + GetValue(ref value); + return (_Value = value); } - } - } + set + { + double oldValue = _Value; + SetValue(oldValue, ref value); + if (oldValue != value) + { + _Value = value; + } + } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Votes + /// + protected int? _Votes; + /// + /// When provided in a partial class, allows value of Votes to be changed before setting. + /// + partial void SetVotes(int? oldValue, ref int? newValue); + /// + /// When provided in a partial class, allows value of Votes to be changed before returning. + /// + partial void GetVotes(ref int? result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + public int? Votes + { + get + { + int? value = _Votes; + GetVotes(ref value); + return (_Votes = value); + } + set + { + int? oldValue = _Votes; + SetVotes(oldValue, ref value); + if (oldValue != value) + { + _Votes = value; + } + } + } - /// - /// If this is NULL it's the internal user rating. - /// - public virtual global::Jellyfin.Data.Entities.RatingSource RatingType { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - } + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// + /// If this is NULL it's the internal user rating. + /// + [ForeignKey("RatingSource_RatingType_Id")] + public virtual RatingSource RatingType { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/RatingSource.cs b/Jellyfin.Data/Entities/RatingSource.cs index 32d5634c2b..070f1ae27e 100644 --- a/Jellyfin.Data/Entities/RatingSource.cs +++ b/Jellyfin.Data/Entities/RatingSource.cs @@ -1,242 +1,231 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - /// - /// This is the entity to store review ratings, not age ratings - /// - public partial class RatingSource - { - partial void Init(); + /// + /// This is the entity to store review ratings, not age ratings + /// + public partial class RatingSource + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected RatingSource() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected RatingSource() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static RatingSource CreateRatingSourceUnsafe() - { - return new RatingSource(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static RatingSource CreateRatingSourceUnsafe() + { + return new RatingSource(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - public RatingSource(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) - { - this.MaximumValue = maximumvalue; + /// + /// Public constructor with required data + /// + /// + /// + /// + public RatingSource(double maximumvalue, double minimumvalue, Rating _rating0) + { + this.MaximumValue = maximumvalue; - this.MinimumValue = minimumvalue; + this.MinimumValue = minimumvalue; - if (_rating0 == null) throw new ArgumentNullException(nameof(_rating0)); - _rating0.RatingType = this; + if (_rating0 == null) throw new ArgumentNullException(nameof(_rating0)); + _rating0.RatingType = this; - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - public static RatingSource Create(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) - { - return new RatingSource(maximumvalue, minimumvalue, _rating0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + public static RatingSource Create(double maximumvalue, double minimumvalue, Rating _rating0) + { + return new RatingSource(maximumvalue, minimumvalue, _rating0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Backing field for MaximumValue - /// - protected double _MaximumValue; - /// - /// When provided in a partial class, allows value of MaximumValue to be changed before setting. - /// - partial void SetMaximumValue(double oldValue, ref double newValue); - /// - /// When provided in a partial class, allows value of MaximumValue to be changed before returning. - /// - partial void GetMaximumValue(ref double result); + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /// - /// Required - /// - [Required] - public double MaximumValue - { - get - { - double value = _MaximumValue; - GetMaximumValue(ref value); - return (_MaximumValue = value); - } - set - { - double oldValue = _MaximumValue; - SetMaximumValue(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _MaximumValue = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } - - /// - /// Backing field for MinimumValue - /// - protected double _MinimumValue; - /// - /// When provided in a partial class, allows value of MinimumValue to be changed before setting. - /// - partial void SetMinimumValue(double oldValue, ref double newValue); - /// - /// When provided in a partial class, allows value of MinimumValue to be changed before returning. - /// - partial void GetMinimumValue(ref double result); - - /// - /// Required - /// - [Required] - public double MinimumValue - { - get - { - double value = _MinimumValue; - GetMinimumValue(ref value); - return (_MinimumValue = value); - } - set - { - double oldValue = _MinimumValue; - SetMinimumValue(oldValue, ref value); - if (oldValue != value) + set { - _MinimumValue = value; + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for MaximumValue + /// + protected double _MaximumValue; + /// + /// When provided in a partial class, allows value of MaximumValue to be changed before setting. + /// + partial void SetMaximumValue(double oldValue, ref double newValue); + /// + /// When provided in a partial class, allows value of MaximumValue to be changed before returning. + /// + partial void GetMaximumValue(ref double result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required + /// + [Required] + public double MaximumValue + { + get + { + double value = _MaximumValue; + GetMaximumValue(ref value); + return (_MaximumValue = value); + } + set + { + double oldValue = _MaximumValue; + SetMaximumValue(oldValue, ref value); + if (oldValue != value) + { + _MaximumValue = value; + } + } + } - public virtual global::Jellyfin.Data.Entities.MetadataProviderId Source { get; set; } + /// + /// Backing field for MinimumValue + /// + protected double _MinimumValue; + /// + /// When provided in a partial class, allows value of MinimumValue to be changed before setting. + /// + partial void SetMinimumValue(double oldValue, ref double newValue); + /// + /// When provided in a partial class, allows value of MinimumValue to be changed before returning. + /// + partial void GetMinimumValue(ref double result); - } + /// + /// Required + /// + [Required] + public double MinimumValue + { + get + { + double value = _MinimumValue; + GetMinimumValue(ref value); + return (_MinimumValue = value); + } + set + { + double oldValue = _MinimumValue; + SetMinimumValue(oldValue, ref value); + if (oldValue != value) + { + _MinimumValue = value; + } + } + } + + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MetadataProviderId_Source_Id")] + public virtual MetadataProviderId Source { get; set; } + + } } diff --git a/Jellyfin.Data/Entities/Release.cs b/Jellyfin.Data/Entities/Release.cs index e02f70be89..d1928fcf7e 100644 --- a/Jellyfin.Data/Entities/Release.cs +++ b/Jellyfin.Data/Entities/Release.cs @@ -1,197 +1,188 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Release - { - partial void Init(); + public partial class Release + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Release() - { - MediaFiles = new System.Collections.Generic.HashSet(); - Chapters = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Release() + { + MediaFiles = new HashSet(); + Chapters = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Release CreateReleaseUnsafe() - { - return new Release(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Release CreateReleaseUnsafe() + { + return new Release(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// - /// - /// - public Release(string name, global::Jellyfin.Data.Entities.Movie _movie0, global::Jellyfin.Data.Entities.Episode _episode1, global::Jellyfin.Data.Entities.Track _track2, global::Jellyfin.Data.Entities.CustomItem _customitem3, global::Jellyfin.Data.Entities.Book _book4, global::Jellyfin.Data.Entities.Photo _photo5) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + /// + /// + public Release(string name, Movie _movie0, Episode _episode1, Track _track2, CustomItem _customitem3, Book _book4, Photo _photo5) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; - if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); - _movie0.Releases.Add(this); + if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); + _movie0.Releases.Add(this); - if (_episode1 == null) throw new ArgumentNullException(nameof(_episode1)); - _episode1.Releases.Add(this); + if (_episode1 == null) throw new ArgumentNullException(nameof(_episode1)); + _episode1.Releases.Add(this); - if (_track2 == null) throw new ArgumentNullException(nameof(_track2)); - _track2.Releases.Add(this); + if (_track2 == null) throw new ArgumentNullException(nameof(_track2)); + _track2.Releases.Add(this); - if (_customitem3 == null) throw new ArgumentNullException(nameof(_customitem3)); - _customitem3.Releases.Add(this); + if (_customitem3 == null) throw new ArgumentNullException(nameof(_customitem3)); + _customitem3.Releases.Add(this); - if (_book4 == null) throw new ArgumentNullException(nameof(_book4)); - _book4.Releases.Add(this); + if (_book4 == null) throw new ArgumentNullException(nameof(_book4)); + _book4.Releases.Add(this); - if (_photo5 == null) throw new ArgumentNullException(nameof(_photo5)); - _photo5.Releases.Add(this); + if (_photo5 == null) throw new ArgumentNullException(nameof(_photo5)); + _photo5.Releases.Add(this); - this.MediaFiles = new System.Collections.Generic.HashSet(); - this.Chapters = new System.Collections.Generic.HashSet(); + this.MediaFiles = new HashSet(); + this.Chapters = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - /// - /// - public static Release Create(string name, global::Jellyfin.Data.Entities.Movie _movie0, global::Jellyfin.Data.Entities.Episode _episode1, global::Jellyfin.Data.Entities.Track _track2, global::Jellyfin.Data.Entities.CustomItem _customitem3, global::Jellyfin.Data.Entities.Book _book4, global::Jellyfin.Data.Entities.Photo _photo5) - { - return new Release(name, _movie0, _episode1, _track2, _customitem3, _book4, _photo5); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + /// + /// + public static Release Create(string name, Movie _movie0, Episode _episode1, Track _track2, CustomItem _customitem3, Book _book4, Photo _photo5) + { + return new Release(name, _movie0, _episode1, _track2, _customitem3, _book4, _photo5); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Id - /// - internal int _Id; - /// - /// When provided in a partial class, allows value of Id to be changed before setting. - /// - partial void SetId(int oldValue, ref int newValue); - /// - /// When provided in a partial class, allows value of Id to be changed before returning. - /// - partial void GetId(ref int result); + /// + /// Backing field for Id + /// + internal int _Id; + /// + /// When provided in a partial class, allows value of Id to be changed before setting. + /// + partial void SetId(int oldValue, ref int newValue); + /// + /// When provided in a partial class, allows value of Id to be changed before returning. + /// + partial void GetId(ref int result); - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public int Id - { - get - { - int value = _Id; - GetId(ref value); - return (_Id = value); - } - protected set - { - int oldValue = _Id; - SetId(oldValue, ref value); - if (oldValue != value) + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// - /// Backing field for Name - /// - protected string _Name; - /// - /// When provided in a partial class, allows value of Name to be changed before setting. - /// - partial void SetName(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Name to be changed before returning. - /// - partial void GetName(ref string result); - - /// - /// Required, Max length = 1024 - /// - [Required] - [MaxLength(1024)] - [StringLength(1024)] - public string Name - { - get - { - string value = _Name; - GetName(ref value); - return (_Name = value); - } - set - { - string oldValue = _Name; - SetName(oldValue, ref value); - if (oldValue != value) + protected set { - _Name = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// + /// Backing field for Name + /// + protected string _Name; + /// + /// When provided in a partial class, allows value of Name to be changed before setting. + /// + partial void SetName(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Name to be changed before returning. + /// + partial void GetName(ref string result); - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Required, Max length = 1024 + /// + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - public virtual ICollection MediaFiles { get; protected set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - public virtual ICollection Chapters { get; protected set; } + public void OnSavingChanges() + { + RowVersion++; + } - } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MediaFile_MediaFiles_Id")] + public virtual ICollection MediaFiles { get; protected set; } + + [ForeignKey("Chapter_Chapters_Id")] + public virtual ICollection Chapters { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/Season.cs b/Jellyfin.Data/Entities/Season.cs index fdfdf24091..96e89cde05 100644 --- a/Jellyfin.Data/Entities/Season.cs +++ b/Jellyfin.Data/Entities/Season.cs @@ -1,127 +1,111 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Season: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Season : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Season(): base() - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Season() + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - SeasonMetadata = new System.Collections.Generic.HashSet(); - Episodes = new System.Collections.Generic.HashSet(); + SeasonMetadata = new HashSet(); + Episodes = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Season CreateSeasonUnsafe() - { - return new Season(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Season CreateSeasonUnsafe() + { + return new Season(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public Season(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Series _series0) - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public Season(Guid urlid, DateTime dateadded, Series _series0) + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - this.UrlId = urlid; + this.UrlId = urlid; - if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); - _series0.Seasons.Add(this); + if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.Seasons.Add(this); - this.SeasonMetadata = new System.Collections.Generic.HashSet(); - this.Episodes = new System.Collections.Generic.HashSet(); + this.SeasonMetadata = new HashSet(); + this.Episodes = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public static Season Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Series _series0) - { - return new Season(urlid, dateadded, _series0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public static Season Create(Guid urlid, DateTime dateadded, Series _series0) + { + return new Season(urlid, dateadded, _series0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for SeasonNumber - /// - protected int? _SeasonNumber; - /// - /// When provided in a partial class, allows value of SeasonNumber to be changed before setting. - /// - partial void SetSeasonNumber(int? oldValue, ref int? newValue); - /// - /// When provided in a partial class, allows value of SeasonNumber to be changed before returning. - /// - partial void GetSeasonNumber(ref int? result); + /// + /// Backing field for SeasonNumber + /// + protected int? _SeasonNumber; + /// + /// When provided in a partial class, allows value of SeasonNumber to be changed before setting. + /// + partial void SetSeasonNumber(int? oldValue, ref int? newValue); + /// + /// When provided in a partial class, allows value of SeasonNumber to be changed before returning. + /// + partial void GetSeasonNumber(ref int? result); - public int? SeasonNumber - { - get - { - int? value = _SeasonNumber; - GetSeasonNumber(ref value); - return (_SeasonNumber = value); - } - set - { - int? oldValue = _SeasonNumber; - SetSeasonNumber(oldValue, ref value); - if (oldValue != value) + public int? SeasonNumber + { + get { - _SeasonNumber = value; + int? value = _SeasonNumber; + GetSeasonNumber(ref value); + return (_SeasonNumber = value); } - } - } + set + { + int? oldValue = _SeasonNumber; + SetSeasonNumber(oldValue, ref value); + if (oldValue != value) + { + _SeasonNumber = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("SeasonMetadata_SeasonMetadata_Id")] + public virtual ICollection SeasonMetadata { get; protected set; } - public virtual ICollection SeasonMetadata { get; protected set; } + [ForeignKey("Episode_Episodes_Id")] + public virtual ICollection Episodes { get; protected set; } - public virtual ICollection Episodes { get; protected set; } - - } + } } diff --git a/Jellyfin.Data/Entities/SeasonMetadata.cs b/Jellyfin.Data/Entities/SeasonMetadata.cs index 5939cbbca1..64ecbfbfac 100644 --- a/Jellyfin.Data/Entities/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/SeasonMetadata.cs @@ -1,123 +1,106 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class SeasonMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class SeasonMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected SeasonMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected SeasonMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static SeasonMetadata CreateSeasonMetadataUnsafe() - { - return new SeasonMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static SeasonMetadata CreateSeasonMetadataUnsafe() + { + return new SeasonMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public SeasonMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Season _season0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public SeasonMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); - _season0.SeasonMetadata.Add(this); + if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.SeasonMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static SeasonMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Season _season0) - { - return new SeasonMetadata(title, language, dateadded, datemodified, _season0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static SeasonMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0) + { + return new SeasonMetadata(title, language, dateadded, datemodified, _season0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Outline - /// - protected string _Outline; - /// - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// - partial void SetOutline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// - partial void GetOutline(ref string result); + /// + /// Backing field for Outline + /// + protected string _Outline; + /// + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// + partial void SetOutline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// + partial void GetOutline(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Outline - { - get - { - string value = _Outline; - GetOutline(ref value); - return (_Outline = value); - } - set - { - string oldValue = _Outline; - SetOutline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } + set + { + string oldValue = _Outline; + SetOutline(oldValue, ref value); + if (oldValue != value) + { + _Outline = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - } + } } diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs index a57064824c..097b9958e3 100644 --- a/Jellyfin.Data/Entities/Series.cs +++ b/Jellyfin.Data/Entities/Series.cs @@ -1,183 +1,167 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Series: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Series : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Series(): base() - { - SeriesMetadata = new System.Collections.Generic.HashSet(); - Seasons = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Series() + { + SeriesMetadata = new HashSet(); + Seasons = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Series CreateSeriesUnsafe() - { - return new Series(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Series CreateSeriesUnsafe() + { + return new Series(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public Series(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public Series(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.SeriesMetadata = new System.Collections.Generic.HashSet(); - this.Seasons = new System.Collections.Generic.HashSet(); + this.SeriesMetadata = new HashSet(); + this.Seasons = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - public static Series Create(Guid urlid, DateTime dateadded) - { - return new Series(urlid, dateadded); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + public static Series Create(Guid urlid, DateTime dateadded) + { + return new Series(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for AirsDayOfWeek - /// - protected global::Jellyfin.Data.Enums.Weekday? _AirsDayOfWeek; - /// - /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting. - /// - partial void SetAirsDayOfWeek(global::Jellyfin.Data.Enums.Weekday? oldValue, ref global::Jellyfin.Data.Enums.Weekday? newValue); - /// - /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning. - /// - partial void GetAirsDayOfWeek(ref global::Jellyfin.Data.Enums.Weekday? result); + /// + /// Backing field for AirsDayOfWeek + /// + protected Enums.Weekday? _AirsDayOfWeek; + /// + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting. + /// + partial void SetAirsDayOfWeek(Enums.Weekday? oldValue, ref Enums.Weekday? newValue); + /// + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning. + /// + partial void GetAirsDayOfWeek(ref Enums.Weekday? result); - public global::Jellyfin.Data.Enums.Weekday? AirsDayOfWeek - { - get - { - global::Jellyfin.Data.Enums.Weekday? value = _AirsDayOfWeek; - GetAirsDayOfWeek(ref value); - return (_AirsDayOfWeek = value); - } - set - { - global::Jellyfin.Data.Enums.Weekday? oldValue = _AirsDayOfWeek; - SetAirsDayOfWeek(oldValue, ref value); - if (oldValue != value) + public Enums.Weekday? AirsDayOfWeek + { + get { - _AirsDayOfWeek = value; + Enums.Weekday? value = _AirsDayOfWeek; + GetAirsDayOfWeek(ref value); + return (_AirsDayOfWeek = value); } - } - } - - /// - /// Backing field for AirsTime - /// - protected DateTimeOffset? _AirsTime; - /// - /// When provided in a partial class, allows value of AirsTime to be changed before setting. - /// - partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// - /// When provided in a partial class, allows value of AirsTime to be changed before returning. - /// - partial void GetAirsTime(ref DateTimeOffset? result); - - /// - /// The time the show airs, ignore the date portion - /// - public DateTimeOffset? AirsTime - { - get - { - DateTimeOffset? value = _AirsTime; - GetAirsTime(ref value); - return (_AirsTime = value); - } - set - { - DateTimeOffset? oldValue = _AirsTime; - SetAirsTime(oldValue, ref value); - if (oldValue != value) + set { - _AirsTime = value; + Enums.Weekday? oldValue = _AirsDayOfWeek; + SetAirsDayOfWeek(oldValue, ref value); + if (oldValue != value) + { + _AirsDayOfWeek = value; + } } - } - } + } - /// - /// Backing field for FirstAired - /// - protected DateTimeOffset? _FirstAired; - /// - /// When provided in a partial class, allows value of FirstAired to be changed before setting. - /// - partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// - /// When provided in a partial class, allows value of FirstAired to be changed before returning. - /// - partial void GetFirstAired(ref DateTimeOffset? result); + /// + /// Backing field for AirsTime + /// + protected DateTimeOffset? _AirsTime; + /// + /// When provided in a partial class, allows value of AirsTime to be changed before setting. + /// + partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// + /// When provided in a partial class, allows value of AirsTime to be changed before returning. + /// + partial void GetAirsTime(ref DateTimeOffset? result); - public DateTimeOffset? FirstAired - { - get - { - DateTimeOffset? value = _FirstAired; - GetFirstAired(ref value); - return (_FirstAired = value); - } - set - { - DateTimeOffset? oldValue = _FirstAired; - SetFirstAired(oldValue, ref value); - if (oldValue != value) + /// + /// The time the show airs, ignore the date portion + /// + public DateTimeOffset? AirsTime + { + get { - _FirstAired = value; + DateTimeOffset? value = _AirsTime; + GetAirsTime(ref value); + return (_AirsTime = value); } - } - } + set + { + DateTimeOffset? oldValue = _AirsTime; + SetAirsTime(oldValue, ref value); + if (oldValue != value) + { + _AirsTime = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for FirstAired + /// + protected DateTimeOffset? _FirstAired; + /// + /// When provided in a partial class, allows value of FirstAired to be changed before setting. + /// + partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// + /// When provided in a partial class, allows value of FirstAired to be changed before returning. + /// + partial void GetFirstAired(ref DateTimeOffset? result); - public virtual ICollection SeriesMetadata { get; protected set; } + public DateTimeOffset? FirstAired + { + get + { + DateTimeOffset? value = _FirstAired; + GetFirstAired(ref value); + return (_FirstAired = value); + } + set + { + DateTimeOffset? oldValue = _FirstAired; + SetFirstAired(oldValue, ref value); + if (oldValue != value) + { + _FirstAired = value; + } + } + } - public virtual ICollection Seasons { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("SeriesMetadata_SeriesMetadata_Id")] + public virtual ICollection SeriesMetadata { get; protected set; } - } + [ForeignKey("Season_Seasons_Id")] + public virtual ICollection Seasons { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/SeriesMetadata.cs b/Jellyfin.Data/Entities/SeriesMetadata.cs index 9a91371dfe..52691783f6 100644 --- a/Jellyfin.Data/Entities/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/SeriesMetadata.cs @@ -1,239 +1,223 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class SeriesMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class SeriesMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected SeriesMetadata(): base() - { - Networks = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected SeriesMetadata() + { + Networks = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static SeriesMetadata CreateSeriesMetadataUnsafe() - { - return new SeriesMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static SeriesMetadata CreateSeriesMetadataUnsafe() + { + return new SeriesMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public SeriesMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Series _series0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public SeriesMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); - _series0.SeriesMetadata.Add(this); + if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.SeriesMetadata.Add(this); - this.Networks = new System.Collections.Generic.HashSet(); + this.Networks = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static SeriesMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Series _series0) - { - return new SeriesMetadata(title, language, dateadded, datemodified, _series0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static SeriesMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0) + { + return new SeriesMetadata(title, language, dateadded, datemodified, _series0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for Outline - /// - protected string _Outline; - /// - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// - partial void SetOutline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// - partial void GetOutline(ref string result); + /// + /// Backing field for Outline + /// + protected string _Outline; + /// + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// + partial void SetOutline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// + partial void GetOutline(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Outline - { - get - { - string value = _Outline; - GetOutline(ref value); - return (_Outline = value); - } - set - { - string oldValue = _Outline; - SetOutline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } - - /// - /// Backing field for Plot - /// - protected string _Plot; - /// - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// - partial void SetPlot(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// - partial void GetPlot(ref string result); - - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string Plot - { - get - { - string value = _Plot; - GetPlot(ref value); - return (_Plot = value); - } - set - { - string oldValue = _Plot; - SetPlot(oldValue, ref value); - if (oldValue != value) + set { - _Plot = value; + string oldValue = _Outline; + SetOutline(oldValue, ref value); + if (oldValue != value) + { + _Outline = value; + } } - } - } + } - /// - /// Backing field for Tagline - /// - protected string _Tagline; - /// - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// - partial void SetTagline(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// - partial void GetTagline(ref string result); + /// + /// Backing field for Plot + /// + protected string _Plot; + /// + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// + partial void SetPlot(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// + partial void GetPlot(ref string result); - /// - /// Max length = 1024 - /// - [MaxLength(1024)] - [StringLength(1024)] - public string Tagline - { - get - { - string value = _Tagline; - GetTagline(ref value); - return (_Tagline = value); - } - set - { - string oldValue = _Tagline; - SetTagline(oldValue, ref value); - if (oldValue != value) + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get { - _Tagline = value; + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); } - } - } - - /// - /// Backing field for Country - /// - protected string _Country; - /// - /// When provided in a partial class, allows value of Country to be changed before setting. - /// - partial void SetCountry(string oldValue, ref string newValue); - /// - /// When provided in a partial class, allows value of Country to be changed before returning. - /// - partial void GetCountry(ref string result); - - /// - /// Max length = 2 - /// - [MaxLength(2)] - [StringLength(2)] - public string Country - { - get - { - string value = _Country; - GetCountry(ref value); - return (_Country = value); - } - set - { - string oldValue = _Country; - SetCountry(oldValue, ref value); - if (oldValue != value) + set { - _Country = value; + string oldValue = _Plot; + SetPlot(oldValue, ref value); + if (oldValue != value) + { + _Plot = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// + /// Backing field for Tagline + /// + protected string _Tagline; + /// + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// + partial void SetTagline(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// + partial void GetTagline(ref string result); - public virtual ICollection Networks { get; protected set; } + /// + /// Max length = 1024 + /// + [MaxLength(1024)] + [StringLength(1024)] + public string Tagline + { + get + { + string value = _Tagline; + GetTagline(ref value); + return (_Tagline = value); + } + set + { + string oldValue = _Tagline; + SetTagline(oldValue, ref value); + if (oldValue != value) + { + _Tagline = value; + } + } + } - } + /// + /// Backing field for Country + /// + protected string _Country; + /// + /// When provided in a partial class, allows value of Country to be changed before setting. + /// + partial void SetCountry(string oldValue, ref string newValue); + /// + /// When provided in a partial class, allows value of Country to be changed before returning. + /// + partial void GetCountry(ref string result); + + /// + /// Max length = 2 + /// + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get + { + string value = _Country; + GetCountry(ref value); + return (_Country = value); + } + set + { + string oldValue = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Company_Networks_Id")] + public virtual ICollection Networks { get; protected set; } + + } } diff --git a/Jellyfin.Data/Entities/Track.cs b/Jellyfin.Data/Entities/Track.cs index 1d3ad372fb..079d73d2bf 100644 --- a/Jellyfin.Data/Entities/Track.cs +++ b/Jellyfin.Data/Entities/Track.cs @@ -1,127 +1,112 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Track: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + public partial class Track : LibraryItem + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Track(): base() - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Track() + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - Releases = new System.Collections.Generic.HashSet(); - TrackMetadata = new System.Collections.Generic.HashSet(); + Releases = new HashSet(); + TrackMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static Track CreateTrackUnsafe() - { - return new Track(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static Track CreateTrackUnsafe() + { + return new Track(); + } - /// - /// Public constructor with required data - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public Track(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) - { - // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. - // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. + /// + /// Public constructor with required data + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public Track(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0) + { + // NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem. + // One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other. - this.UrlId = urlid; + this.UrlId = urlid; - if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); - _musicalbum0.Tracks.Add(this); + if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.Tracks.Add(this); - this.Releases = new System.Collections.Generic.HashSet(); - this.TrackMetadata = new System.Collections.Generic.HashSet(); + this.Releases = new HashSet(); + this.TrackMetadata = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// - public static Track Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) - { - return new Track(urlid, dateadded, _musicalbum0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// + public static Track Create(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0) + { + return new Track(urlid, dateadded, _musicalbum0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Backing field for TrackNumber - /// - protected int? _TrackNumber; - /// - /// When provided in a partial class, allows value of TrackNumber to be changed before setting. - /// - partial void SetTrackNumber(int? oldValue, ref int? newValue); - /// - /// When provided in a partial class, allows value of TrackNumber to be changed before returning. - /// - partial void GetTrackNumber(ref int? result); + /// + /// Backing field for TrackNumber + /// + protected int? _TrackNumber; + /// + /// When provided in a partial class, allows value of TrackNumber to be changed before setting. + /// + partial void SetTrackNumber(int? oldValue, ref int? newValue); + /// + /// When provided in a partial class, allows value of TrackNumber to be changed before returning. + /// + partial void GetTrackNumber(ref int? result); - public int? TrackNumber - { - get - { - int? value = _TrackNumber; - GetTrackNumber(ref value); - return (_TrackNumber = value); - } - set - { - int? oldValue = _TrackNumber; - SetTrackNumber(oldValue, ref value); - if (oldValue != value) + public int? TrackNumber + { + get { - _TrackNumber = value; + int? value = _TrackNumber; + GetTrackNumber(ref value); + return (_TrackNumber = value); } - } - } + set + { + int? oldValue = _TrackNumber; + SetTrackNumber(oldValue, ref value); + if (oldValue != value) + { + _TrackNumber = value; + } + } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection Releases { get; protected set; } - public virtual ICollection TrackMetadata { get; protected set; } + [ForeignKey("TrackMetadata_TrackMetadata_Id")] + public virtual ICollection TrackMetadata { get; protected set; } - } + } } diff --git a/Jellyfin.Data/Entities/TrackMetadata.cs b/Jellyfin.Data/Entities/TrackMetadata.cs index f4c61459c8..86c9161f6e 100644 --- a/Jellyfin.Data/Entities/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/TrackMetadata.cs @@ -1,86 +1,68 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class TrackMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + public partial class TrackMetadata : Metadata + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected TrackMetadata(): base() - { - Init(); - } + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected TrackMetadata() + { + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static TrackMetadata CreateTrackMetadataUnsafe() - { - return new TrackMetadata(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static TrackMetadata CreateTrackMetadataUnsafe() + { + return new TrackMetadata(); + } - /// - /// Public constructor with required data - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public TrackMetadata(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Track _track0) - { - if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); - this.Title = title; + /// + /// Public constructor with required data + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public TrackMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title)); + this.Title = title; - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; - if (_track0 == null) throw new ArgumentNullException(nameof(_track0)); - _track0.TrackMetadata.Add(this); + if (_track0 == null) throw new ArgumentNullException(nameof(_track0)); + _track0.TrackMetadata.Add(this); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The title or name of the object - /// ISO-639-3 3-character language codes - /// - public static TrackMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, global::Jellyfin.Data.Entities.Track _track0) - { - return new TrackMetadata(title, language, dateadded, datemodified, _track0); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The title or name of the object + /// ISO-639-3 3-character language codes + /// + public static TrackMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Track _track0) + { + return new TrackMetadata(title, language, dateadded, datemodified, _track0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - } + } } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 2ee3c8f4f2..a81d5215bb 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,242 +1,235 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class User - { - partial void Init(); + public partial class User + { + partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected User() - { - Groups = new System.Collections.Generic.HashSet(); - Permissions = new System.Collections.Generic.HashSet(); - ProviderMappings = new System.Collections.Generic.HashSet(); - Preferences = new System.Collections.Generic.HashSet(); + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected User() + { + Groups = new HashSet(); + Permissions = new HashSet(); + ProviderMappings = new HashSet(); + Preferences = new HashSet(); - Init(); - } + Init(); + } - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static User CreateUserUnsafe() - { - return new User(); - } + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static User CreateUserUnsafe() + { + return new User(); + } - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// - /// - /// - public User(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack) - { - if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username)); - this.Username = username; + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + /// + /// + public User(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack) + { + if (string.IsNullOrEmpty(username)) throw new ArgumentNullException(nameof(username)); + this.Username = username; - this.MustUpdatePassword = mustupdatepassword; + this.MustUpdatePassword = mustupdatepassword; - if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference)); - this.AudioLanguagePreference = audiolanguagepreference; + if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference)); + this.AudioLanguagePreference = audiolanguagepreference; - if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid)); - this.AuthenticationProviderId = authenticationproviderid; + if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid)); + this.AuthenticationProviderId = authenticationproviderid; - this.InvalidLoginAttemptCount = invalidloginattemptcount; + this.InvalidLoginAttemptCount = invalidloginattemptcount; - if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode)); - this.SubtitleMode = subtitlemode; + if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode)); + this.SubtitleMode = subtitlemode; - this.PlayDefaultAudioTrack = playdefaultaudiotrack; + this.PlayDefaultAudioTrack = playdefaultaudiotrack; - this.Groups = new System.Collections.Generic.HashSet(); - this.Permissions = new System.Collections.Generic.HashSet(); - this.ProviderMappings = new System.Collections.Generic.HashSet(); - this.Preferences = new System.Collections.Generic.HashSet(); + this.Groups = new HashSet(); + this.Permissions = new HashSet(); + this.ProviderMappings = new HashSet(); + this.Preferences = new HashSet(); - Init(); - } + Init(); + } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - /// - /// - public static User Create(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack) - { - return new User(username, mustupdatepassword, audiolanguagepreference, authenticationproviderid, invalidloginattemptcount, subtitlemode, playdefaultaudiotrack); - } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + /// + /// + public static User Create(string username, bool mustupdatepassword, string audiolanguagepreference, string authenticationproviderid, int invalidloginattemptcount, string subtitlemode, bool playdefaultaudiotrack) + { + return new User(username, mustupdatepassword, audiolanguagepreference, authenticationproviderid, invalidloginattemptcount, subtitlemode, playdefaultaudiotrack); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// - /// Identity, Indexed, Required - /// - [Key] - [Required] - public Guid Id { get; protected set; } + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } - /// - /// Required - /// - [ConcurrencyCheck] - [Required] - public byte[] LastLoginTimestamp { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Username { get; set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string Username { get; set; } + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string Password { get; set; } - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string Password { get; set; } + /// + /// Required + /// + [Required] + public bool MustUpdatePassword { get; set; } - /// - /// Required - /// - [Required] - public bool MustUpdatePassword { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AudioLanguagePreference { get; set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string AudioLanguagePreference { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AuthenticationProviderId { get; set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string AuthenticationProviderId { get; set; } + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string GroupedFolders { get; set; } - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string GroupedFolders { get; set; } + /// + /// Required + /// + [Required] + public int InvalidLoginAttemptCount { get; set; } - /// - /// Required - /// - [Required] - public int InvalidLoginAttemptCount { get; set; } + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string LatestItemExcludes { get; set; } - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string LatestItemExcludes { get; set; } + public int? LoginAttemptsBeforeLockout { get; set; } - public int? LoginAttemptsBeforeLockout { get; set; } + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string MyMediaExcludes { get; set; } - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string MyMediaExcludes { get; set; } + /// + /// Max length = 65535 + /// + [MaxLength(65535)] + [StringLength(65535)] + public string OrderedViews { get; set; } - /// - /// Max length = 65535 - /// - [MaxLength(65535)] - [StringLength(65535)] - public string OrderedViews { get; set; } + /// + /// Required, Max length = 255 + /// + [Required] + [MaxLength(255)] + [StringLength(255)] + public string SubtitleMode { get; set; } - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string SubtitleMode { get; set; } + /// + /// Required + /// + [Required] + public bool PlayDefaultAudioTrack { get; set; } - /// - /// Required - /// - [Required] - public bool PlayDefaultAudioTrack { get; set; } + /// + /// Max length = 255 + /// + [MaxLength(255)] + [StringLength(255)] + public string SubtitleLanguagePrefernce { get; set; } - /// - /// Max length = 255 - /// - [MaxLength(255)] - [StringLength(255)] - public string SubtitleLanguagePrefernce { get; set; } + public bool? DisplayMissingEpisodes { get; set; } - public bool? DisplayMissingEpisodes { get; set; } + public bool? DisplayCollectionsView { get; set; } - public bool? DisplayCollectionsView { get; set; } + public bool? HidePlayedInLatest { get; set; } - public bool? HidePlayedInLatest { get; set; } + public bool? RememberAudioSelections { get; set; } - public bool? RememberAudioSelections { get; set; } + public bool? RememberSubtitleSelections { get; set; } - public bool? RememberSubtitleSelections { get; set; } + public bool? EnableNextEpisodeAutoPlay { get; set; } - public bool? EnableNextEpisodeAutoPlay { get; set; } + public bool? EnableUserPreferenceAccess { get; set; } - public bool? EnableUserPreferenceAccess { get; set; } + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection Groups { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Group_Groups_Id")] + public virtual ICollection Groups { get; protected set; } - public virtual ICollection Permissions { get; protected set; } + [ForeignKey("Permission_Permissions_Id")] + public virtual ICollection Permissions { get; protected set; } - public virtual ICollection ProviderMappings { get; protected set; } + [ForeignKey("ProviderMapping_ProviderMappings_Id")] + public virtual ICollection ProviderMappings { get; protected set; } - public virtual ICollection Preferences { get; protected set; } + [ForeignKey("Preference_Preferences_Id")] + public virtual ICollection Preferences { get; protected set; } - } + } } diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs index 52e33048e2..6b69d68b28 100644 --- a/Jellyfin.Data/Enums/ArtKind.cs +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -1,25 +1,11 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - namespace Jellyfin.Data.Enums { - public enum ArtKind : Int32 - { - Other, - Poster, - Banner, - Thumbnail, - Logo - } + public enum ArtKind + { + Other, + Poster, + Banner, + Thumbnail, + Logo + } } diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs index 34d1b20f59..12f48c5589 100644 --- a/Jellyfin.Data/Enums/MediaFileKind.cs +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -1,25 +1,11 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - namespace Jellyfin.Data.Enums { - public enum MediaFileKind : Int32 - { - Main, - Sidecar, - AdditionalPart, - AlternativeFormat, - AdditionalStream - } + public enum MediaFileKind + { + Main, + Sidecar, + AdditionalPart, + AlternativeFormat, + AdditionalStream + } } diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs new file mode 100644 index 0000000000..1506471e86 --- /dev/null +++ b/Jellyfin.Data/Enums/PermissionKind.cs @@ -0,0 +1,26 @@ +namespace Jellyfin.Data.Enums +{ + public enum PermissionKind + { + IsAdministrator, + IsHidden, + IsDisabled, + BlockUnrateditems, + EnbleSharedDeviceControl, + EnableRemoteAccess, + EnableLiveTvManagement, + EnableLiveTvAccess, + EnableMediaPlayback, + EnableAudioPlaybackTranscoding, + EnableVideoPlaybackTranscoding, + EnableContentDeletion, + EnableContentDownloading, + EnableSyncTranscoding, + EnableMediaConversion, + EnableAllDevices, + EnableAllChannels, + EnableAllFolders, + EnablePublicSharing, + AccessSchedules + } +} diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs index f5c8f43c51..6e52f2c851 100644 --- a/Jellyfin.Data/Enums/PersonRoleType.cs +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -1,32 +1,18 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - namespace Jellyfin.Data.Enums { - public enum PersonRoleType : Int32 - { - Other, - Director, - Artist, - OriginalArtist, - Actor, - VoiceActor, - Producer, - Remixer, - Conductor, - Composer, - Author, - Editor - } + public enum PersonRoleType + { + Other, + Director, + Artist, + OriginalArtist, + Actor, + VoiceActor, + Producer, + Remixer, + Conductor, + Composer, + Author, + Editor + } } diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs new file mode 100644 index 0000000000..cd2cb791af --- /dev/null +++ b/Jellyfin.Data/Enums/PreferenceKind.cs @@ -0,0 +1,13 @@ +namespace Jellyfin.Data.Enums +{ + public enum PreferenceKind + { + MaxParentalRating, + BlockedTags, + RemoteClientBitrateLimit, + EnabledDevices, + EnabledChannels, + EnabledFolders, + EnableContentDeletionFromFolders + } +} diff --git a/Jellyfin.Data/Enums/Weekday.cs b/Jellyfin.Data/Enums/Weekday.cs index ce0c6e4ce8..b80a03a330 100644 --- a/Jellyfin.Data/Enums/Weekday.cs +++ b/Jellyfin.Data/Enums/Weekday.cs @@ -1,27 +1,13 @@ -//------------------------------------------------------------------------------ -// -// This code was generated from a template. -// -// Manual changes to this file may cause unexpected behavior in your application. -// Manual changes to this file will be overwritten if the code is regenerated. -// -// Produced by Entity Framework Visual Editor -// https://github.com/msawczyn/EFDesigner -// -//------------------------------------------------------------------------------ - -using System; - namespace Jellyfin.Data.Enums { - public enum Weekday : Int32 - { - Sunday, - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday - } + public enum Weekday + { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday + } } diff --git a/Jellyfin.Data/ISavingChanges.cs b/Jellyfin.Data/ISavingChanges.cs new file mode 100644 index 0000000000..f392dae6a0 --- /dev/null +++ b/Jellyfin.Data/ISavingChanges.cs @@ -0,0 +1,9 @@ +#pragma warning disable CS1591 + +namespace Jellyfin.Data +{ + public interface ISavingChanges + { + void OnSavingChanges(); + } +} diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 73ea593b0b..9157c3ead9 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -1,12 +1,26 @@ - netstandard2.0 + netstandard2.0;netstandard2.1 + false + true + + ../jellyfin.ruleset + + + + + + + + + + - - + + diff --git a/Jellyfin.Data/Structs/.gitkeep b/Jellyfin.Data/Structs/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs new file mode 100644 index 0000000000..65ceee32bf --- /dev/null +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -0,0 +1,102 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; + +namespace Jellyfin.Server.Implementations.Activity +{ + /// + /// Manages the storage and retrieval of instances. + /// + public class ActivityManager : IActivityManager + { + private readonly JellyfinDbProvider _provider; + + /// + /// Initializes a new instance of the class. + /// + /// The Jellyfin database provider. + public ActivityManager(JellyfinDbProvider provider) + { + _provider = provider; + } + + /// + public event EventHandler> EntryCreated; + + /// + public void Create(ActivityLog entry) + { + using var dbContext = _provider.CreateContext(); + dbContext.ActivityLogs.Add(entry); + dbContext.SaveChanges(); + + EntryCreated?.Invoke(this, new GenericEventArgs(ConvertToOldModel(entry))); + } + + /// + public async Task CreateAsync(ActivityLog entry) + { + using var dbContext = _provider.CreateContext(); + await dbContext.ActivityLogs.AddAsync(entry); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + + EntryCreated?.Invoke(this, new GenericEventArgs(ConvertToOldModel(entry))); + } + + /// + public QueryResult GetPagedResult( + Func, IQueryable> func, + int? startIndex, + int? limit) + { + using var dbContext = _provider.CreateContext(); + + var query = func(dbContext.ActivityLogs.OrderByDescending(entry => entry.DateCreated)); + + if (startIndex.HasValue) + { + query = query.Skip(startIndex.Value); + } + + if (limit.HasValue) + { + query = query.Take(limit.Value); + } + + // This converts the objects from the new database model to the old for compatibility with the existing API. + var list = query.Select(ConvertToOldModel).ToList(); + + return new QueryResult + { + Items = list, + TotalRecordCount = func(dbContext.ActivityLogs).Count() + }; + } + + /// + public QueryResult GetPagedResult(int? startIndex, int? limit) + { + return GetPagedResult(logs => logs, startIndex, limit); + } + + private static ActivityLogEntry ConvertToOldModel(ActivityLog entry) + { + return new ActivityLogEntry + { + Id = entry.Id, + Name = entry.Name, + Overview = entry.Overview, + ShortOverview = entry.ShortOverview, + Type = entry.Type, + ItemId = entry.ItemId, + UserId = entry.UserId, + Date = entry.DateCreated, + Severity = entry.LogSeverity + }; + } + } +} diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj new file mode 100644 index 0000000000..8486fc2dfb --- /dev/null +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -0,0 +1,45 @@ + + + + netcoreapp3.1 + false + true + true + + + + ../jellyfin.ruleset + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs new file mode 100644 index 0000000000..ec09a619f2 --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -0,0 +1,120 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1201 // Constuctors should not follow properties +#pragma warning disable SA1516 // Elements should be followed by a blank line +#pragma warning disable SA1623 // Property's documentation should begin with gets or sets +#pragma warning disable SA1629 // Documentation should end with a period +#pragma warning disable SA1648 // Inheritdoc should be used with inheriting class + +using System.Linq; +using Jellyfin.Data; +using Jellyfin.Data.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Jellyfin.Server.Implementations +{ + /// + public partial class JellyfinDb : DbContext + { + public virtual DbSet ActivityLogs { get; set; } + /*public virtual DbSet Artwork { get; set; } + public virtual DbSet Books { get; set; } + public virtual DbSet BookMetadata { get; set; } + public virtual DbSet Chapters { get; set; } + public virtual DbSet Collections { get; set; } + public virtual DbSet CollectionItems { get; set; } + public virtual DbSet Companies { get; set; } + public virtual DbSet CompanyMetadata { get; set; } + public virtual DbSet CustomItems { get; set; } + public virtual DbSet CustomItemMetadata { get; set; } + public virtual DbSet Episodes { get; set; } + public virtual DbSet EpisodeMetadata { get; set; } + public virtual DbSet Genres { get; set; } + public virtual DbSet Groups { get; set; } + public virtual DbSet Libraries { get; set; } + public virtual DbSet LibraryItems { get; set; } + public virtual DbSet LibraryRoot { get; set; } + public virtual DbSet MediaFiles { get; set; } + public virtual DbSet MediaFileStream { get; set; } + public virtual DbSet Metadata { get; set; } + public virtual DbSet MetadataProviders { get; set; } + public virtual DbSet MetadataProviderIds { get; set; } + public virtual DbSet Movies { get; set; } + public virtual DbSet MovieMetadata { get; set; } + public virtual DbSet MusicAlbums { get; set; } + public virtual DbSet MusicAlbumMetadata { get; set; } + public virtual DbSet Permissions { get; set; } + public virtual DbSet People { get; set; } + public virtual DbSet PersonRoles { get; set; } + public virtual DbSet Photo { get; set; } + public virtual DbSet PhotoMetadata { get; set; } + public virtual DbSet Preferences { get; set; } + public virtual DbSet ProviderMappings { get; set; } + public virtual DbSet Ratings { get; set; } + + /// + /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to + /// store review ratings, not age ratings + /// + public virtual DbSet RatingSources { get; set; } + public virtual DbSet Releases { get; set; } + public virtual DbSet Seasons { get; set; } + public virtual DbSet SeasonMetadata { get; set; } + public virtual DbSet Series { get; set; } + public virtual DbSet SeriesMetadata { get; set; } + public virtual DbSet Tracks { get; set; } + public virtual DbSet TrackMetadata { get; set; } + public virtual DbSet Users { get; set; } */ + + /// + /// Gets or sets the default connection string. + /// + public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; + + /// + public JellyfinDb(DbContextOptions options) : base(options) + { + } + + partial void CustomInit(DbContextOptionsBuilder optionsBuilder); + + /// + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + CustomInit(optionsBuilder); + } + + partial void OnModelCreatingImpl(ModelBuilder modelBuilder); + partial void OnModelCreatedImpl(ModelBuilder modelBuilder); + + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + OnModelCreatingImpl(modelBuilder); + + modelBuilder.HasDefaultSchema("jellyfin"); + + /*modelBuilder.Entity().HasIndex(t => t.Kind); + + modelBuilder.Entity().HasIndex(t => t.Name) + .IsUnique(); + + modelBuilder.Entity().HasIndex(t => t.UrlId) + .IsUnique();*/ + + OnModelCreatedImpl(modelBuilder); + } + + public override int SaveChanges() + { + foreach (var saveEntity in ChangeTracker.Entries() + .Where(e => e.State == EntityState.Modified) + .OfType()) + { + saveEntity.OnSavingChanges(); + } + + return base.SaveChanges(); + } + } +} diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs new file mode 100644 index 0000000000..eab531d386 --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Jellyfin.Server.Implementations +{ + /// + /// Factory class for generating new instances. + /// + public class JellyfinDbProvider + { + private readonly IServiceProvider _serviceProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The application's service provider. + public JellyfinDbProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + serviceProvider.GetService().Database.Migrate(); + } + + /// + /// Creates a new context. + /// + /// The newly created context. + public JellyfinDb CreateContext() + { + return _serviceProvider.GetRequiredService(); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs new file mode 100644 index 0000000000..98a83b7450 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs @@ -0,0 +1,72 @@ +#pragma warning disable CS1591 + +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20200514181226_AddActivityLog")] + partial class AddActivityLog + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs new file mode 100644 index 0000000000..5e0b454d8b --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs @@ -0,0 +1,46 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddActivityLog : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "jellyfin"); + + migrationBuilder.CreateTable( + name: "ActivityLogs", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 512, nullable: false), + Overview = table.Column(maxLength: 512, nullable: true), + ShortOverview = table.Column(maxLength: 512, nullable: true), + Type = table.Column(maxLength: 256, nullable: false), + UserId = table.Column(nullable: false), + ItemId = table.Column(maxLength: 256, nullable: true), + DateCreated = table.Column(nullable: false), + LogSeverity = table.Column(nullable: false), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ActivityLogs", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ActivityLogs", + schema: "jellyfin"); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs new file mode 100644 index 0000000000..72a4a8c3b6 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Jellyfin.Server.Implementations.Migrations +{ + /// + /// The design time factory for . + /// This is only used for the creation of migrations and not during runtime. + /// + internal class DesignTimeJellyfinDbFactory : IDesignTimeDbContextFactory + { + public JellyfinDb CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlite("Data Source=jellyfin.db"); + + return new JellyfinDb(optionsBuilder.Options); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs new file mode 100644 index 0000000000..1e7ffd2359 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -0,0 +1,66 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + partial class JellyfinDbModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index f678e714c1..331a32c737 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,12 +1,17 @@ using System; using System.Collections.Generic; +using System.IO; using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; using Jellyfin.Drawing.Skia; +using Jellyfin.Server.Implementations; +using Jellyfin.Server.Implementations.Activity; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Activity; using MediaBrowser.Model.IO; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -56,6 +61,15 @@ namespace Jellyfin.Server Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}."); } + // TODO: Set up scoping and use AddDbContextPool + serviceCollection.AddDbContext( + options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), + ServiceLifetime.Transient); + + serviceCollection.AddSingleton(); + + serviceCollection.AddSingleton(); + base.RegisterServices(serviceCollection); } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 88114d9994..c93aa837e7 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -41,8 +41,8 @@ - - + + @@ -60,6 +60,7 @@ + diff --git a/Jellyfin.Server/Migrations/IMigrationRoutine.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs index eab995d67e..b79fdeac03 100644 --- a/Jellyfin.Server/Migrations/IMigrationRoutine.cs +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -21,8 +21,6 @@ namespace Jellyfin.Server.Migrations /// /// Execute the migration routine. /// - /// Host that hosts current version. - /// Host logger. - public void Perform(CoreAppHost host, ILogger logger); + public void Perform(); } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index b5ea04dcac..473f62737e 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations @@ -13,10 +14,12 @@ namespace Jellyfin.Server.Migrations /// /// The list of known migrations, in order of applicability. /// - internal static readonly IMigrationRoutine[] Migrations = + private static readonly Type[] _migrationTypes = { - new Routines.DisableTranscodingThrottling(), - new Routines.CreateUserLoggingConfigFile() + typeof(Routines.DisableTranscodingThrottling), + typeof(Routines.CreateUserLoggingConfigFile), + typeof(Routines.MigrateActivityLogDb), + typeof(Routines.RemoveDuplicateExtras) }; /// @@ -27,6 +30,10 @@ namespace Jellyfin.Server.Migrations public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { var logger = loggerFactory.CreateLogger(); + var migrations = _migrationTypes + .Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m)) + .OfType() + .ToArray(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration(MigrationsListStore.StoreKey); if (!host.ServerConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) @@ -34,16 +41,16 @@ namespace Jellyfin.Server.Migrations // If startup wizard is not finished, this is a fresh install. // Don't run any migrations, just mark all of them as applied. logger.LogInformation("Marking all known migrations as applied because this is a fresh install"); - migrationOptions.Applied.AddRange(Migrations.Select(m => (m.Id, m.Name))); + migrationOptions.Applied.AddRange(migrations.Select(m => (m.Id, m.Name))); host.ServerConfigurationManager.SaveConfiguration(MigrationsListStore.StoreKey, migrationOptions); return; } var appliedMigrationIds = migrationOptions.Applied.Select(m => m.Id).ToHashSet(); - for (var i = 0; i < Migrations.Length; i++) + for (var i = 0; i < migrations.Length; i++) { - var migrationRoutine = Migrations[i]; + var migrationRoutine = migrations[i]; if (appliedMigrationIds.Contains(migrationRoutine.Id)) { logger.LogDebug("Skipping migration '{Name}' since it is already applied", migrationRoutine.Name); @@ -54,7 +61,7 @@ namespace Jellyfin.Server.Migrations try { - migrationRoutine.Perform(host, logger); + migrationRoutine.Perform(); } catch (Exception ex) { diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 3bc32c0478..89514c89b7 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -36,6 +36,13 @@ namespace Jellyfin.Server.Migrations.Routines @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", }; + private readonly IApplicationPaths _appPaths; + + public CreateUserLoggingConfigFile(IApplicationPaths appPaths) + { + _appPaths = appPaths; + } + /// public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}"); @@ -43,9 +50,9 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "CreateLoggingConfigHeirarchy"; /// - public void Perform(CoreAppHost host, ILogger logger) + public void Perform() { - var logDirectory = host.Resolve().ConfigurationDirectoryPath; + var logDirectory = _appPaths.ConfigurationDirectoryPath; var existingConfigPath = Path.Combine(logDirectory, "logging.json"); // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json' diff --git a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index 6f8e4a8ffb..b2e957d5bb 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -10,6 +10,15 @@ namespace Jellyfin.Server.Migrations.Routines /// internal class DisableTranscodingThrottling : IMigrationRoutine { + private readonly ILogger _logger; + private readonly IConfigurationManager _configManager; + + public DisableTranscodingThrottling(ILogger logger, IConfigurationManager configManager) + { + _logger = logger; + _configManager = configManager; + } + /// public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); @@ -17,16 +26,16 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "DisableTranscodingThrottling"; /// - public void Perform(CoreAppHost host, ILogger logger) + public void Perform() { // Set EnableThrottling to false since it wasn't used before and may introduce issues - var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + var encoding = _configManager.GetConfiguration("encoding"); if (encoding.EnableThrottling) { - logger.LogInformation("Disabling transcoding throttling during migration"); + _logger.LogInformation("Disabling transcoding throttling during migration"); encoding.EnableThrottling = false; - host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + _configManager.SaveConfiguration("encoding", encoding); } } } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs new file mode 100644 index 0000000000..b3cc297082 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Emby.Server.Implementations.Data; +using Jellyfin.Data.Entities; +using Jellyfin.Server.Implementations; +using MediaBrowser.Controller; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// The migration routine for migrating the activity log database to EF Core. + /// + public class MigrateActivityLogDb : IMigrationRoutine + { + private const string DbFilename = "activitylog.db"; + + private readonly ILogger _logger; + private readonly JellyfinDbProvider _provider; + private readonly IServerApplicationPaths _paths; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The server application paths. + /// The database provider. + public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, JellyfinDbProvider provider) + { + _logger = logger; + _provider = provider; + _paths = paths; + } + + /// + public Guid Id => Guid.Parse("3793eb59-bc8c-456c-8b9f-bd5a62a42978"); + + /// + public string Name => "MigrateActivityLogDatabase"; + + /// + public void Perform() + { + var logLevelDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "None", LogLevel.None }, + { "Trace", LogLevel.Trace }, + { "Debug", LogLevel.Debug }, + { "Information", LogLevel.Information }, + { "Info", LogLevel.Information }, + { "Warn", LogLevel.Warning }, + { "Warning", LogLevel.Warning }, + { "Error", LogLevel.Error }, + { "Critical", LogLevel.Critical } + }; + + var dataPath = _paths.DataPath; + using (var connection = SQLite3.Open( + Path.Combine(dataPath, DbFilename), + ConnectionFlags.ReadOnly, + null)) + { + _logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin."); + using var dbContext = _provider.CreateContext(); + + var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id ASC"); + + // Make sure that the database is empty in case of failed migration due to power outages, etc. + dbContext.ActivityLogs.RemoveRange(dbContext.ActivityLogs); + dbContext.SaveChanges(); + // Reset the autoincrement counter + dbContext.Database.ExecuteSqlRaw("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'ActivityLog';"); + dbContext.SaveChanges(); + + var newEntries = queryResult.Select(entry => + { + if (!logLevelDictionary.TryGetValue(entry[8].ToString(), out var severity)) + { + severity = LogLevel.Trace; + } + + var newEntry = new ActivityLog( + entry[1].ToString(), + entry[4].ToString(), + entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString())) + { + DateCreated = entry[7].ReadDateTime(), + LogSeverity = severity + }; + + if (entry[2].SQLiteType != SQLiteType.Null) + { + newEntry.Overview = entry[2].ToString(); + } + + if (entry[3].SQLiteType != SQLiteType.Null) + { + newEntry.ShortOverview = entry[3].ToString(); + } + + if (entry[5].SQLiteType != SQLiteType.Null) + { + newEntry.ItemId = entry[5].ToString(); + } + + return newEntry; + }); + + dbContext.ActivityLogs.AddRange(newEntries); + dbContext.SaveChanges(); + } + + try + { + File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old")); + + var journalPath = Path.Combine(dataPath, DbFilename + "-journal"); + if (File.Exists(journalPath)) + { + File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal")); + } + } + catch (IOException e) + { + _logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); + } + } + } +} diff --git a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs new file mode 100644 index 0000000000..e955363881 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs @@ -0,0 +1,79 @@ +using System; +using System.Globalization; +using System.IO; + +using MediaBrowser.Controller; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// + /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// + internal class RemoveDuplicateExtras : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _paths; + + public RemoveDuplicateExtras(ILogger logger, IServerApplicationPaths paths) + { + _logger = logger; + _paths = paths; + } + + /// + public Guid Id => Guid.Parse("{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}"); + + /// + public string Name => "RemoveDuplicateExtras"; + + /// + public void Perform() + { + var dataPath = _paths.DataPath; + var dbPath = Path.Combine(dataPath, DbFilename); + using (var connection = SQLite3.Open( + dbPath, + ConnectionFlags.ReadWrite, + null)) + { + // Query the database for the ids of duplicate extras + var queryResult = connection.Query("SELECT t1.Path FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video'"); + var bads = string.Join(", ", queryResult.SelectScalarString()); + + // Do nothing if no duplicate extras were detected + if (bads.Length == 0) + { + _logger.LogInformation("No duplicate extras detected, skipping migration."); + return; + } + + // Back up the database before deleting any entries + for (int i = 1; ; i++) + { + var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i); + if (!File.Exists(bakPath)) + { + try + { + File.Copy(dbPath, bakPath); + _logger.LogInformation("Library database backed up to {BackupPath}", bakPath); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannot make a backup of {Library} at path {BackupPath}", DbFilename, bakPath); + throw; + } + } + } + + // Delete all duplicate extras + _logger.LogInformation("Removing found duplicated extras for the following items: {DuplicateExtras}", bads); + connection.Execute("DELETE FROM TypedBaseItems WHERE rowid IN (SELECT t1.rowid FROM TypedBaseItems AS t1, TypedBaseItems AS t2 WHERE t1.Path=t2.Path AND t1.Type!=t2.Type AND t1.Type='MediaBrowser.Controller.Entities.Video')"); + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 9635cc6ec2..b9895386f5 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -10,14 +10,11 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using CommandLine; -using Emby.Drawing; using Emby.Server.Implementations; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Networking; -using Jellyfin.Drawing.Skia; using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Extensions; using MediaBrowser.WebDashboard.Api; using Microsoft.AspNetCore.Hosting; @@ -161,23 +158,7 @@ namespace Jellyfin.Server ApplicationHost.LogEnvironmentInfo(_logger, appPaths); - // Make sure we have all the code pages we can get - // Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - // Increase the max http request limit - // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. - ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); - - // Disable the "Expect: 100-Continue" header by default - // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c - ServicePointManager.Expect100Continue = false; - - Batteries_V2.Init(); - if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) - { - _logger.LogWarning("Failed to enable shared cache for SQLite"); - } + PerformStaticInitialization(); var appHost = new CoreAppHost( appPaths, @@ -205,7 +186,7 @@ namespace Jellyfin.Server ServiceCollection serviceCollection = new ServiceCollection(); appHost.Init(serviceCollection); - var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); + var webHost = new WebHostBuilder().ConfigureWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = webHost.Services; @@ -250,14 +231,49 @@ namespace Jellyfin.Server } } - private static IWebHostBuilder CreateWebHostBuilder( + /// + /// Call static initialization methods for the application. + /// + public static void PerformStaticInitialization() + { + // Make sure we have all the code pages we can get + // Ref: https://docs.microsoft.com/en-us/dotnet/api/system.text.codepagesencodingprovider.instance?view=netcore-3.0#remarks + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + // Increase the max http request limit + // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); + + // Disable the "Expect: 100-Continue" header by default + // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c + ServicePointManager.Expect100Continue = false; + + Batteries_V2.Init(); + if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) + { + _logger.LogWarning("Failed to enable shared cache for SQLite"); + } + } + + /// + /// Configure the web host builder. + /// + /// The builder to configure. + /// The application host. + /// The application service collection. + /// The command line options passed to the application. + /// The application configuration. + /// The application paths. + /// The configured web host builder. + public static IWebHostBuilder ConfigureWebHostBuilder( + this IWebHostBuilder builder, ApplicationHost appHost, IServiceCollection serviceCollection, StartupOptions commandLineOpts, IConfiguration startupConfig, IApplicationPaths appPaths) { - return new WebHostBuilder() + return builder .UseKestrel((builderContext, options) => { var addresses = appHost.ServerConfigurationManager @@ -278,8 +294,7 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); - - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.ListenWithHttps) { options.Listen(address, appHost.HttpsPort, listenOptions => { @@ -289,11 +304,18 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - options.Listen(address, appHost.HttpsPort, listenOptions => + try { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.Listen(address, appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted."); + } } } } @@ -302,7 +324,7 @@ namespace Jellyfin.Server _logger.LogInformation("Kestrel listening on all interfaces"); options.ListenAnyIP(appHost.HttpPort); - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.ListenWithHttps) { options.ListenAnyIP(appHost.HttpsPort, listenOptions => { @@ -312,11 +334,18 @@ namespace Jellyfin.Server } else if (builderContext.HostingEnvironment.IsDevelopment()) { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => + try { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.ListenAnyIP(appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "Failed to listen to HTTPS using the ASP.NET Core HTTPS development certificate. Please ensure it has been installed and set as trusted."); + } } } }) @@ -496,7 +525,9 @@ namespace Jellyfin.Server /// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist /// already. /// - private static async Task InitLoggingConfigFile(IApplicationPaths appPaths) + /// The application paths. + /// A task representing the creation of the configuration file, or a completed task if the file already exists. + public static async Task InitLoggingConfigFile(IApplicationPaths appPaths) { // Do nothing if the config file already exists string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); @@ -516,7 +547,13 @@ namespace Jellyfin.Server await resource.CopyToAsync(dst).ConfigureAwait(false); } - private static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths) + /// + /// Create the application configuration. + /// + /// The command line options passed to the program. + /// The application paths. + /// The application configuration. + public static IConfiguration CreateAppConfiguration(StartupOptions commandLineOpts, IApplicationPaths appPaths) { return new ConfigurationBuilder() .ConfigureAppConfiguration(commandLineOpts, appPaths) diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 8bcfd13504..5f9a5c1618 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -64,7 +64,6 @@ namespace Jellyfin.Server app.UseResponseCompression(); // TODO app.UseMiddleware(); - app.Use(serverApplicationHost.ExecuteWebsocketHandlerAsync); // TODO use when old API is removed: app.UseAuthentication(); app.UseJellyfinApiSwagger(); diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 1a1d86362a..2cd68ac1b4 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Api public abstract class BaseApiService : IService, IRequiresRequest { public BaseApiService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory) { @@ -34,7 +34,7 @@ namespace MediaBrowser.Api /// Gets the logger. /// /// The logger. - protected ILogger Logger { get; } + protected ILogger Logger { get; } /// /// Gets or sets the server configuration manager. diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 7004a2559e..53eb9667d8 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -1,5 +1,4 @@ using System.IO; -using System.Threading.Tasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; @@ -116,11 +115,6 @@ namespace MediaBrowser.Api.Devices return _deviceManager.GetDeviceOptions(request.Id); } - public object Get(GetCameraUploads request) - { - return ToOptimizedResult(_deviceManager.GetCameraUploadHistory(request.DeviceId)); - } - public void Delete(DeleteDevice request) { var sessions = _authRepo.Get(new AuthenticationInfoQuery @@ -134,35 +128,5 @@ namespace MediaBrowser.Api.Devices _sessionManager.Logout(session); } } - - public Task Post(PostCameraUpload request) - { - var deviceId = Request.QueryString["DeviceId"]; - var album = Request.QueryString["Album"]; - var id = Request.QueryString["Id"]; - var name = Request.QueryString["Name"]; - var req = Request.Response.HttpContext.Request; - - if (req.HasFormContentType) - { - var file = req.Form.Files.Count == 0 ? null : req.Form.Files[0]; - - return _deviceManager.AcceptCameraUpload(deviceId, file.OpenReadStream(), new LocalFileInfo - { - MimeType = file.ContentType, - Album = album, - Name = name, - Id = id - }); - } - - return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo - { - MimeType = Request.ContentType, - Album = album, - Name = name, - Id = id - }); - } } } diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 222bb34d31..23bf547125 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -265,17 +265,20 @@ namespace MediaBrowser.Api.Images { Url = url, BufferContent = false - }).ConfigureAwait(false); - var ext = result.ContentType.Split('/').Last(); + var ext = result.ContentType.Split('/')[^1]; var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); - using (var stream = result.Content) + var stream = result.Content; + await using (stream.ConfigureAwait(false)) { - using var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - await stream.CopyToAsync(filestream).ConfigureAwait(false); + var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await using (filestream.ConfigureAwait(false)) + { + await stream.CopyToAsync(filestream).ConfigureAwait(false); + } } Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index 0bbe7e1cfa..68e3dfa59c 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -299,22 +299,26 @@ namespace MediaBrowser.Api { var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false); - var ext = result.ContentType.Split('/').Last(); + var ext = result.ContentType.Split('/')[^1]; var fullCachePath = GetFullCachePath(urlHash + "." + ext); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); - using (var stream = result.Content) + var stream = result.Content; + + await using (stream.ConfigureAwait(false)) { - using var fileStream = new FileStream( + var fileStream = new FileStream( fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - - await stream.CopyToAsync(fileStream).ConfigureAwait(false); + await using (fileStream.ConfigureAwait(false)) + { + await stream.CopyToAsync(fileStream).ConfigureAwait(false); + } } Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index a54640b2fd..c0146dfee6 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -319,11 +319,14 @@ namespace MediaBrowser.Api.Library private readonly ILocalizationManager _localization; private readonly ILibraryMonitor _libraryMonitor; + private readonly ILogger _moviesServiceLogger; + /// /// Initializes a new instance of the class. /// public LibraryService( ILogger logger, + ILogger moviesServiceLogger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IProviderManager providerManager, @@ -344,6 +347,7 @@ namespace MediaBrowser.Api.Library _activityManager = activityManager; _localization = localization; _libraryMonitor = libraryMonitor; + _moviesServiceLogger = moviesServiceLogger; } private string[] GetRepresentativeItemTypes(string contentType) @@ -543,7 +547,7 @@ namespace MediaBrowser.Api.Library if (item is Movie || (program != null && program.IsMovie) || item is Trailer) { return new MoviesService( - Logger, + _moviesServiceLogger, ServerConfigurationManager, ResultFactory, _userManager, @@ -759,13 +763,12 @@ namespace MediaBrowser.Api.Library { try { - _activityManager.Create(new ActivityLogEntry + _activityManager.Create(new Jellyfin.Data.Entities.ActivityLog( + string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name), + "UserDownloadingContent", + auth.UserId) { - Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name), - Type = "UserDownloadingContent", ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device), - UserId = auth.UserId - }); } catch diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 46da8b9099..cdd0276340 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -82,7 +82,7 @@ namespace MediaBrowser.Api.Movies /// Initializes a new instance of the class. /// public MoviesService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index 8adf9c6216..0b53342359 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -33,13 +33,18 @@ namespace MediaBrowser.Api.Movies /// private readonly ILibraryManager _libraryManager; + /// + /// The logger for the created instances. + /// + private readonly ILogger _logger; + private readonly IDtoService _dtoService; private readonly ILocalizationManager _localizationManager; private readonly IJsonSerializer _json; private readonly IAuthorizationContext _authContext; public TrailersService( - ILogger logger, + ILoggerFactory loggerFactory, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, @@ -48,7 +53,7 @@ namespace MediaBrowser.Api.Movies ILocalizationManager localizationManager, IJsonSerializer json, IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) + : base(loggerFactory.CreateLogger(), serverConfigurationManager, httpResultFactory) { _userManager = userManager; _libraryManager = libraryManager; @@ -56,6 +61,7 @@ namespace MediaBrowser.Api.Movies _localizationManager = localizationManager; _json = json; _authContext = authContext; + _logger = loggerFactory.CreateLogger(); } public object Get(Getrailers request) @@ -66,7 +72,7 @@ namespace MediaBrowser.Api.Movies getItems.IncludeItemTypes = "Trailer"; return new ItemsService( - Logger, + _logger, ServerConfigurationManager, ResultFactory, _userManager, diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 928ca16128..f796aa486d 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.Api.Playback /// Initializes a new instance of the class. /// protected BaseStreamingService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 52962366c6..627421aac6 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls public abstract class BaseHlsService : BaseStreamingService { public BaseHlsService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, @@ -209,24 +209,28 @@ namespace MediaBrowser.Api.Playback.Hls try { // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using var fileStream = GetPlaylistFileStream(playlist); - using var reader = new StreamReader(fileStream); - var count = 0; - - while (!reader.EndOfStream) + var fileStream = GetPlaylistFileStream(playlist); + await using (fileStream.ConfigureAwait(false)) { - var line = reader.ReadLine(); + using var reader = new StreamReader(fileStream); + var count = 0; - if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) + while (!reader.EndOfStream) { - count++; - if (count >= segmentCount) + var line = await reader.ReadLineAsync().ConfigureAwait(false); + + if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) { - Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist); - return; + count++; + if (count >= segmentCount) + { + Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist); + return; + } } } } + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } catch (IOException) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 7f74e85e9b..061316cb86 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Api.Playback.Hls public class DynamicHlsService : BaseHlsService { public DynamicHlsService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index db24eaca6e..e2d771ec65 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.Api.Playback private readonly IAuthorizationContext _authContext; public MediaInfoService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IMediaSourceManager mediaSourceManager, diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 8d1e3a3f23..34c7986ca5 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Playback.Progressive public class AudioService : BaseProgressiveStreamingService { public AudioService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IHttpClient httpClient, diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index ed68219c9f..c7bf055fba 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Api.Playback.Progressive protected IHttpClient HttpClient { get; private set; } public BaseProgressiveStreamingService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IHttpClient httpClient, diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index cebd4b49a1..a3b319d44b 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -75,9 +75,11 @@ namespace MediaBrowser.Api.Playback public class UniversalAudioService : BaseApiService { private readonly EncodingHelper _encodingHelper; + private readonly ILoggerFactory _loggerFactory; public UniversalAudioService( ILogger logger, + ILoggerFactory loggerFactory, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IHttpClient httpClient, @@ -108,6 +110,7 @@ namespace MediaBrowser.Api.Playback AuthorizationContext = authorizationContext; NetworkManager = networkManager; _encodingHelper = encodingHelper; + _loggerFactory = loggerFactory; } protected IHttpClient HttpClient { get; private set; } @@ -233,7 +236,7 @@ namespace MediaBrowser.Api.Playback AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId; var mediaInfoService = new MediaInfoService( - Logger, + _loggerFactory.CreateLogger(), ServerConfigurationManager, ResultFactory, MediaSourceManager, @@ -277,7 +280,7 @@ namespace MediaBrowser.Api.Playback if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { var service = new DynamicHlsService( - Logger, + _loggerFactory.CreateLogger(), ServerConfigurationManager, ResultFactory, UserManager, @@ -331,7 +334,7 @@ namespace MediaBrowser.Api.Playback else { var service = new AudioService( - Logger, + _loggerFactory.CreateLogger(), ServerConfigurationManager, ResultFactory, HttpClient, diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs index d882aac887..0e74c92679 100644 --- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs @@ -31,46 +31,46 @@ namespace MediaBrowser.Api.Sessions { _sessionManager = sessionManager; - _sessionManager.SessionStarted += _sessionManager_SessionStarted; - _sessionManager.SessionEnded += _sessionManager_SessionEnded; - _sessionManager.PlaybackStart += _sessionManager_PlaybackStart; - _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped; - _sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress; - _sessionManager.CapabilitiesChanged += _sessionManager_CapabilitiesChanged; - _sessionManager.SessionActivity += _sessionManager_SessionActivity; + _sessionManager.SessionStarted += OnSessionManagerSessionStarted; + _sessionManager.SessionEnded += OnSessionManagerSessionEnded; + _sessionManager.PlaybackStart += OnSessionManagerPlaybackStart; + _sessionManager.PlaybackStopped += OnSessionManagerPlaybackStopped; + _sessionManager.PlaybackProgress += OnSessionManagerPlaybackProgress; + _sessionManager.CapabilitiesChanged += OnSessionManagerCapabilitiesChanged; + _sessionManager.SessionActivity += OnSessionManagerSessionActivity; } - void _sessionManager_SessionActivity(object sender, SessionEventArgs e) + private void OnSessionManagerSessionActivity(object sender, SessionEventArgs e) { SendData(false); } - void _sessionManager_CapabilitiesChanged(object sender, SessionEventArgs e) + private void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e) { SendData(true); } - void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e) + private void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e) { SendData(!e.IsAutomated); } - void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e) + private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e) { SendData(true); } - void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) + private void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e) { SendData(true); } - void _sessionManager_SessionEnded(object sender, SessionEventArgs e) + private void OnSessionManagerSessionEnded(object sender, SessionEventArgs e) { SendData(true); } - void _sessionManager_SessionStarted(object sender, SessionEventArgs e) + private void OnSessionManagerSessionStarted(object sender, SessionEventArgs e) { SendData(true); } @@ -84,15 +84,16 @@ namespace MediaBrowser.Api.Sessions return Task.FromResult(_sessionManager.Sessions); } + /// protected override void Dispose(bool dispose) { - _sessionManager.SessionStarted -= _sessionManager_SessionStarted; - _sessionManager.SessionEnded -= _sessionManager_SessionEnded; - _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart; - _sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped; - _sessionManager.PlaybackProgress -= _sessionManager_PlaybackProgress; - _sessionManager.CapabilitiesChanged -= _sessionManager_CapabilitiesChanged; - _sessionManager.SessionActivity -= _sessionManager_SessionActivity; + _sessionManager.SessionStarted -= OnSessionManagerSessionStarted; + _sessionManager.SessionEnded -= OnSessionManagerSessionEnded; + _sessionManager.PlaybackStart -= OnSessionManagerPlaybackStart; + _sessionManager.PlaybackStopped -= OnSessionManagerPlaybackStopped; + _sessionManager.PlaybackProgress -= OnSessionManagerPlaybackProgress; + _sessionManager.CapabilitiesChanged -= OnSessionManagerCapabilitiesChanged; + _sessionManager.SessionActivity -= OnSessionManagerSessionActivity; base.Dispose(dispose); } diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index f95fa7ca0b..a6bacad4fc 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -1,5 +1,7 @@ using System; using System.Globalization; +using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; @@ -53,7 +55,10 @@ namespace MediaBrowser.Api.System (DateTime?)null : DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - var result = _activityManager.GetActivityLogEntries(minDate, request.HasUserId, request.StartIndex, request.Limit); + var filterFunc = new Func, IQueryable>( + entries => entries.Where(entry => entry.DateCreated >= minDate)); + + var result = _activityManager.GetPagedResult(filterFunc, request.StartIndex, request.Limit); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index f8b6ee65d6..8e4860be4b 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; @@ -10,7 +10,7 @@ namespace MediaBrowser.Api.System /// /// Class SessionInfoWebSocketListener /// - public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> + public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener { /// /// Gets the name. @@ -26,10 +26,10 @@ namespace MediaBrowser.Api.System public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger) { _activityManager = activityManager; - _activityManager.EntryCreated += _activityManager_EntryCreated; + _activityManager.EntryCreated += OnEntryCreated; } - void _activityManager_EntryCreated(object sender, GenericEventArgs e) + private void OnEntryCreated(object sender, GenericEventArgs e) { SendData(true); } @@ -38,15 +38,15 @@ namespace MediaBrowser.Api.System /// Gets the data to send. /// /// Task{SystemInfo}. - protected override Task> GetDataToSend() + protected override Task GetDataToSend() { - return Task.FromResult(new List()); + return Task.FromResult(Array.Empty()); } - + /// protected override void Dispose(bool dispose) { - _activityManager.EntryCreated -= _activityManager_EntryCreated; + _activityManager.EntryCreated -= OnEntryCreated; base.Dispose(dispose); } diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 3d08d5437c..bef91d54df 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Api.UserLibrary public class ArtistsService : BaseItemsByNameService { public ArtistsService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index c4a52d5f52..559082ff48 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Api.UserLibrary /// The user data repository. /// The dto service. protected BaseItemsByNameService( - ILogger logger, + ILogger> logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index c4d44042b1..f3c0441e12 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.Api.UserLibrary /// The localization. /// The dto service. public ItemsService( - ILogger logger, + ILogger logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory, IUserManager userManager, diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 78fc6c6941..7d4d5fcf97 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Api } [Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")] - public class GetPublicUsers : IReturn + public class GetPublicUsers : IReturn { } @@ -266,22 +266,38 @@ namespace MediaBrowser.Api _authContext = authContext; } + /// + /// Gets the public available Users information + /// + /// The request. + /// System.Object. public object Get(GetPublicUsers request) { - // If the startup wizard hasn't been completed then just return all users - if (!ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + var result = _userManager + .Users + .Where(item => !item.Policy.IsDisabled); + + if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) { - return Get(new GetUsers + var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; + result = result.Where(item => !item.Policy.IsHidden); + + if (!string.IsNullOrWhiteSpace(deviceId)) { - IsDisabled = false - }); + result = result.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); + } + + if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + result = result.Where(i => i.Policy.EnableRemoteAccess); + } } - return Get(new GetUsers - { - IsHidden = false, - IsDisabled = false - }, true, true); + return ToOptimizedResult(result + .OrderBy(u => u.Name) + .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp)) + .ToArray() + ); } /// diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 69864106c7..a597b90524 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -17,8 +17,8 @@ - - + + diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 77d5676310..ef3f43c759 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -1,6 +1,4 @@ using System; -using System.IO; -using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Events; @@ -11,11 +9,6 @@ namespace MediaBrowser.Controller.Devices { public interface IDeviceManager { - /// - /// Occurs when [camera image uploaded]. - /// - event EventHandler> CameraImageUploaded; - /// /// Saves the capabilities. /// @@ -45,22 +38,6 @@ namespace MediaBrowser.Controller.Devices /// IEnumerable<DeviceInfo>. QueryResult GetDevices(DeviceQuery query); - /// - /// Gets the upload history. - /// - /// The device identifier. - /// ContentUploadHistory. - ContentUploadHistory GetCameraUploadHistory(string deviceId); - - /// - /// Accepts the upload. - /// - /// The device identifier. - /// The stream. - /// The file. - /// Task. - Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file); - /// /// Determines whether this instance [can access device] the specified user identifier. /// diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 04ba0fabcb..d1d6c74b86 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -39,10 +39,9 @@ namespace MediaBrowser.Controller int HttpsPort { get; } /// - /// Gets a value indicating whether [supports HTTPS]. + /// Gets a value indicating whether the server should listen on an HTTPS port. /// - /// true if [supports HTTPS]; otherwise, false. - bool EnableHttps { get; } + bool ListenWithHttps { get; } /// /// Gets a value indicating whether this instance has update available. @@ -57,34 +56,50 @@ namespace MediaBrowser.Controller string FriendlyName { get; } /// - /// Gets the local ip address. + /// Gets all the local IP addresses of this API instance. Each address is validated by sending a 'ping' request + /// to the API that should exist at the address. /// - /// The local ip address. + /// A cancellation token that can be used to cancel the task. + /// A list containing all the local IP addresses of the server. Task> GetLocalIpAddresses(CancellationToken cancellationToken); /// - /// Gets the local API URL. + /// Gets a local (LAN) URL that can be used to access the API. The hostname used is the first valid configured + /// IP address that can be found via . HTTPS will be preferred when available. /// - /// Token to cancel the request if needed. - /// Whether to force usage of plain HTTP protocol. - /// The local API URL. - Task GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false); + /// A cancellation token that can be used to cancel the task. + /// The server URL. + Task GetLocalApiUrl(CancellationToken cancellationToken); /// - /// Gets the local API URL. + /// Gets a localhost URL that can be used to access the API using the loop-back IP address (127.0.0.1) + /// over HTTP (not HTTPS). /// - /// The hostname. - /// Whether to force usage of plain HTTP protocol. - /// The local API URL. - string GetLocalApiUrl(ReadOnlySpan hostname, bool forceHttp = false); + /// The API URL. + string GetLoopbackHttpApiUrl(); /// - /// Gets the local API URL. + /// Gets a local (LAN) URL that can be used to access the API. HTTPS will be preferred when available. /// - /// The IP address. - /// Whether to force usage of plain HTTP protocol. - /// The local API URL. - string GetLocalApiUrl(IPAddress address, bool forceHttp = false); + /// The IP address to use as the hostname in the URL. + /// The API URL. + string GetLocalApiUrl(IPAddress address); + + /// + /// Gets a local (LAN) URL that can be used to access the API. + /// Note: if passing non-null scheme or port it is up to the caller to ensure they form the correct pair. + /// + /// The hostname to use in the URL. + /// + /// The scheme to use for the URL. If null, the scheme will be selected automatically, + /// preferring HTTPS, if available. + /// + /// + /// The port to use for the URL. If null, the port will be selected automatically, + /// preferring the HTTPS port, if available. + /// + /// The API URL. + string GetLocalApiUrl(ReadOnlySpan hostname, string scheme = null, int? port = null); /// /// Open a URL in an external browser window. @@ -101,7 +116,5 @@ namespace MediaBrowser.Controller string ReverseVirtualPath(string path); Task ExecuteHttpHandlerAsync(HttpContext context, Func next); - - Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next); } } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index be7b4ce59d..ec6cb35eb9 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -143,6 +143,14 @@ namespace MediaBrowser.Controller.Library /// UserDto. UserDto GetUserDto(User user, string remoteEndPoint = null); + /// + /// Gets the user public dto. + /// + /// Ther user.\ + /// The remote end point. + /// A public UserDto, aka a UserDto stripped of personal data. + PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null); + /// /// Authenticates the user. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 4e7d027374..223bbe1ded 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index b710318eee..1162bff130 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -77,8 +77,6 @@ namespace MediaBrowser.Controller.Net return Task.CompletedTask; } - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// /// Starts sending messages over a web socket /// @@ -87,12 +85,12 @@ namespace MediaBrowser.Controller.Net { var vals = message.Data.Split(','); - var dueTimeMs = long.Parse(vals[0], UsCulture); - var periodMs = long.Parse(vals[1], UsCulture); + var dueTimeMs = long.Parse(vals[0], CultureInfo.InvariantCulture); + var periodMs = long.Parse(vals[1], CultureInfo.InvariantCulture); var cancellationTokenSource = new CancellationTokenSource(); - Logger.LogDebug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); + Logger.LogDebug("WS {1} begin transmitting to {0}", message.Connection.RemoteEndPoint, GetType().Name); var state = new TStateType { @@ -154,7 +152,6 @@ namespace MediaBrowser.Controller.Net { MessageType = Name, Data = data - }, cancellationToken).ConfigureAwait(false); state.DateLastSendUtc = DateTime.UtcNow; @@ -197,7 +194,7 @@ namespace MediaBrowser.Controller.Net /// The connection. private void DisposeConnection(Tuple connection) { - Logger.LogDebug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); + Logger.LogDebug("WS {1} stop transmitting to {0}", connection.Item1.RemoteEndPoint, GetType().Name); // TODO disposing the connection seems to break websockets in subtle ways, so what is the purpose of this function really... // connection.Item1.Dispose(); @@ -242,6 +239,7 @@ namespace MediaBrowser.Controller.Net public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } } diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 806478864d..efb5f4ac3f 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Events; using MediaBrowser.Model.Services; @@ -9,9 +8,9 @@ using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { /// - /// Interface IHttpServer + /// Interface IHttpServer. /// - public interface IHttpServer : IDisposable + public interface IHttpServer { /// /// Gets the URL prefix. @@ -19,11 +18,6 @@ namespace MediaBrowser.Controller.Net /// The URL prefix. string[] UrlPrefixes { get; } - /// - /// Stops this instance. - /// - void Stop(); - /// /// Occurs when [web socket connected]. /// @@ -39,23 +33,18 @@ namespace MediaBrowser.Controller.Net /// string GlobalResponse { get; set; } - /// - /// Sends the http context to the socket listener - /// - /// - /// - Task ProcessWebSocketRequest(HttpContext ctx); - /// /// The HTTP request handler /// - /// - /// - /// - /// - /// + /// /// - Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, - CancellationToken cancellationToken); + Task RequestHandler(HttpContext context); + + /// + /// Get the default CORS headers + /// + /// + /// + IDictionary GetDefaultCorsHeaders(IRequest req); } } diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 31eb7ccb75..09e43c683f 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,4 +1,7 @@ +#nullable enable + using System; +using System.Net; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; @@ -7,18 +10,12 @@ using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { - public interface IWebSocketConnection : IDisposable + public interface IWebSocketConnection { /// /// Occurs when [closed]. /// - event EventHandler Closed; - - /// - /// Gets the id. - /// - /// The id. - Guid Id { get; } + event EventHandler? Closed; /// /// Gets the last activity date. @@ -26,22 +23,17 @@ namespace MediaBrowser.Controller.Net /// The last activity date. DateTime LastActivityDate { get; } - /// - /// Gets or sets the URL. - /// - /// The URL. - string Url { get; set; } /// /// Gets or sets the query string. /// /// The query string. - IQueryCollection QueryString { get; set; } + IQueryCollection QueryString { get; } /// /// Gets or sets the receive action. /// /// The receive action. - Func OnReceive { get; set; } + Func? OnReceive { get; set; } /// /// Gets the state. @@ -53,7 +45,7 @@ namespace MediaBrowser.Controller.Net /// Gets the remote end point. /// /// The remote end point. - string RemoteEndPoint { get; } + IPAddress? RemoteEndPoint { get; } /// /// Sends a message asynchronously. @@ -65,21 +57,6 @@ namespace MediaBrowser.Controller.Net /// message Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken); - /// - /// Sends a message asynchronously. - /// - /// The buffer. - /// The cancellation token. - /// Task. - Task SendAsync(byte[] buffer, CancellationToken cancellationToken); - - /// - /// Sends a message asynchronously. - /// - /// The text. - /// The cancellation token. - /// Task. - /// buffer - Task SendAsync(string text, CancellationToken cancellationToken); + Task ProcessAsync(CancellationToken cancellationToken = default); } } diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index a59c96ac70..04450085bf 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; @@ -20,6 +21,6 @@ namespace MediaBrowser.Controller.Session /// /// Sends the message. /// - Task SendMessage(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken); + Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index f1f10a3a3d..2ba7c9fec0 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -10,13 +10,23 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Session { /// - /// Class SessionInfo + /// Class SessionInfo. /// - public class SessionInfo : IDisposable + public sealed class SessionInfo : IDisposable { - private ISessionManager _sessionManager; + // 1 second + private const long ProgressIncrement = 10000000; + + private readonly ISessionManager _sessionManager; private readonly ILogger _logger; + + private readonly object _progressLock = new object(); + private Timer _progressTimer; + private PlaybackProgressInfo _lastProgressInfo; + + private bool _disposed = false; + public SessionInfo(ISessionManager sessionManager, ILogger logger) { _sessionManager = sessionManager; @@ -97,8 +107,6 @@ namespace MediaBrowser.Controller.Session /// The name of the device. public string DeviceName { get; set; } - public string DeviceType { get; set; } - /// /// Gets or sets the now playing item. /// @@ -128,22 +136,6 @@ namespace MediaBrowser.Controller.Session [JsonIgnore] public ISessionController[] SessionControllers { get; set; } - /// - /// Gets or sets the supported commands. - /// - /// The supported commands. - public string[] SupportedCommands - { - get - { - if (Capabilities == null) - { - return new string[] { }; - } - return Capabilities.SupportedCommands; - } - } - public TranscodingInfo TranscodingInfo { get; set; } /// @@ -215,6 +207,14 @@ namespace MediaBrowser.Controller.Session } } + public QueueItem[] NowPlayingQueue { get; set; } + + public bool HasCustomDeviceName { get; set; } + + public string PlaylistItemId { get; set; } + + public string UserPrimaryImageTag { get; set; } + public Tuple EnsureController(Func factory) { var controllers = SessionControllers.ToList(); @@ -258,10 +258,6 @@ namespace MediaBrowser.Controller.Session return false; } - private readonly object _progressLock = new object(); - private Timer _progressTimer; - private PlaybackProgressInfo _lastProgressInfo; - public void StartAutomaticProgress(PlaybackProgressInfo progressInfo) { if (_disposed) @@ -284,9 +280,6 @@ namespace MediaBrowser.Controller.Session } } - // 1 second - private const long ProgressIncrement = 10000000; - private async void OnProgressTimerCallback(object state) { if (_disposed) @@ -345,8 +338,7 @@ namespace MediaBrowser.Controller.Session } } - private bool _disposed = false; - + /// public void Dispose() { _disposed = true; @@ -358,30 +350,12 @@ namespace MediaBrowser.Controller.Session foreach (var controller in controllers) { - var disposable = controller as IDisposable; - - if (disposable != null) + if (controller is IDisposable disposable) { _logger.LogDebug("Disposing session controller {0}", disposable.GetType().Name); - - try - { - disposable.Dispose(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error disposing session controller"); - } + disposable.Dispose(); } } - - _sessionManager = null; } - - public QueueItem[] NowPlayingQueue { get; set; } - public bool HasCustomDeviceName { get; set; } - public string PlaylistItemId { get; set; } - public string ServerId { get; set; } - public string UserPrimaryImageTag { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index 0b2f1d231e..a2ea0766a8 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -278,5 +278,19 @@ namespace MediaBrowser.MediaEncoding.Probing /// The disposition. [JsonPropertyName("disposition")] public IReadOnlyDictionary Disposition { get; set; } + + /// + /// Gets or sets the color transfer. + /// + /// The color transfer. + [JsonPropertyName("color_transfer")] + public string ColorTransfer { get; set; } + + /// + /// Gets or sets the color primaries. + /// + /// The color primaries. + [JsonPropertyName("color_primaries")] + public string ColorPrimaries { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b24d97f4ef..d3f8094b94 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -695,6 +695,16 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.RefFrames = streamInfo.Refs; } + + if (!string.IsNullOrEmpty(streamInfo.ColorTransfer)) + { + stream.ColorTransfer = streamInfo.ColorTransfer; + } + + if (!string.IsNullOrEmpty(streamInfo.ColorPrimaries)) + { + stream.ColorPrimaries = streamInfo.ColorPrimaries; + } } else { diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs index 2e328ba63e..de35acbba9 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs @@ -7,14 +7,25 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles { + /// + /// Subtitle writer for the WebVTT format. + /// public class VttWriter : ISubtitleWriter { + /// public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) { using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { writer.WriteLine("WEBVTT"); writer.WriteLine(string.Empty); + writer.WriteLine("REGION"); + writer.WriteLine("id:subtitle"); + writer.WriteLine("width:80%"); + writer.WriteLine("lines:3"); + writer.WriteLine("regionanchor:50%,100%"); + writer.WriteLine("viewportanchor:50%,90%"); + writer.WriteLine(string.Empty); foreach (var trackEvent in info.TrackEvents) { cancellationToken.ThrowIfCancellationRequested(); @@ -22,13 +33,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks); var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks); - // make sure the start and end times are different and seqential + // make sure the start and end times are different and sequential if (endTime.TotalMilliseconds <= startTime.TotalMilliseconds) { endTime = startTime.Add(TimeSpan.FromMilliseconds(1)); } - writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff}", startTime, endTime); + writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff} region:subtitle", startTime, endTime); var text = trackEvent.Text; diff --git a/MediaBrowser.Model/Activity/ActivityLogEntry.cs b/MediaBrowser.Model/Activity/ActivityLogEntry.cs index 80f01b66ee..5ab904394e 100644 --- a/MediaBrowser.Model/Activity/ActivityLogEntry.cs +++ b/MediaBrowser.Model/Activity/ActivityLogEntry.cs @@ -59,6 +59,7 @@ namespace MediaBrowser.Model.Activity /// Gets or sets the user primary image tag. /// /// The user primary image tag. + [Obsolete("UserPrimaryImageTag is not used.")] public string UserPrimaryImageTag { get; set; } /// diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index f336f5272c..9dab5e77b7 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -1,6 +1,9 @@ #pragma warning disable CS1591 using System; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; @@ -10,10 +13,15 @@ namespace MediaBrowser.Model.Activity { event EventHandler> EntryCreated; - void Create(ActivityLogEntry entry); + void Create(ActivityLog entry); - QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); + Task CreateAsync(ActivityLog entry); - QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); + QueryResult GetPagedResult(int? startIndex, int? limit); + + QueryResult GetPagedResult( + Func, IQueryable> func, + int? startIndex, + int? limit); } } diff --git a/MediaBrowser.Model/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs deleted file mode 100644 index 66144ec478..0000000000 --- a/MediaBrowser.Model/Activity/IActivityRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using MediaBrowser.Model.Querying; - -namespace MediaBrowser.Model.Activity -{ - public interface IActivityRepository - { - void Create(ActivityLogEntry entry); - - QueryResult GetActivityLogEntries(DateTime? minDate, bool? z, int? startIndex, int? limit); - } -} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 063ccd9b9a..1f5981f101 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -49,17 +49,24 @@ namespace MediaBrowser.Model.Configuration public int HttpsPortNumber { get; set; } /// - /// Gets or sets a value indicating whether [use HTTPS]. + /// Gets or sets a value indicating whether to use HTTPS. /// - /// true if [use HTTPS]; otherwise, false. + /// + /// In order for HTTPS to be used, in addition to setting this to true, valid values must also be + /// provided for and . + /// public bool EnableHttps { get; set; } + public bool EnableNormalizedItemByNameIds { get; set; } /// - /// Gets or sets the value pointing to the file system where the ssl certificate is located.. + /// Gets or sets the filesystem path of an X.509 certificate to use for SSL. /// - /// The value pointing to the file system where the ssl certificate is located.. public string CertificatePath { get; set; } + + /// + /// Gets or sets the password required to access the X.509 certificate data in the file specified by . + /// public string CertificatePassword { get; set; } /// @@ -69,8 +76,9 @@ namespace MediaBrowser.Model.Configuration public bool IsPortAuthorized { get; set; } public bool AutoRunWebApp { get; set; } + public bool EnableRemoteAccess { get; set; } - public bool CameraUploadUpgraded { get; set; } + public bool CollectionsUpgraded { get; set; } /// @@ -86,6 +94,7 @@ namespace MediaBrowser.Model.Configuration /// /// The metadata path. public string MetadataPath { get; set; } + public string MetadataNetworkPath { get; set; } /// @@ -208,15 +217,26 @@ namespace MediaBrowser.Model.Configuration public int RemoteClientBitrateLimit { get; set; } public bool EnableFolderView { get; set; } + public bool EnableGroupingIntoCollections { get; set; } + public bool DisplaySpecialsWithinSeasons { get; set; } + public string[] LocalNetworkSubnets { get; set; } + public string[] LocalNetworkAddresses { get; set; } + public string[] CodecsUsed { get; set; } + public bool IgnoreVirtualInterfaces { get; set; } + public bool EnableExternalContentInSuggestions { get; set; } + + /// + /// Gets or sets a value indicating whether the server should force connections over HTTPS. + /// public bool RequireHttps { get; set; } - public bool IsBehindProxy { get; set; } + public bool EnableNewOmdbSupport { get; set; } public string[] RemoteIPFilter { get; set; } diff --git a/MediaBrowser.Model/Devices/DeviceOptions.cs b/MediaBrowser.Model/Devices/DeviceOptions.cs new file mode 100644 index 0000000000..8b77fd7fc3 --- /dev/null +++ b/MediaBrowser.Model/Devices/DeviceOptions.cs @@ -0,0 +1,9 @@ +#pragma warning disable CS1591 + +namespace MediaBrowser.Model.Devices +{ + public class DeviceOptions + { + public string CustomName { get; set; } + } +} diff --git a/MediaBrowser.Model/Devices/DevicesOptions.cs b/MediaBrowser.Model/Devices/DevicesOptions.cs deleted file mode 100644 index 02570650e9..0000000000 --- a/MediaBrowser.Model/Devices/DevicesOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Devices -{ - public class DevicesOptions - { - public string[] EnabledCameraUploadDevices { get; set; } - public string CameraUploadPath { get; set; } - public bool EnableCameraUploadSubfolders { get; set; } - - public DevicesOptions() - { - EnabledCameraUploadDevices = Array.Empty(); - } - } - - public class DeviceOptions - { - public string CustomName { get; set; } - } -} diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs index 756e500dd7..7bb961deb2 100644 --- a/MediaBrowser.Model/Dlna/CodecProfile.cs +++ b/MediaBrowser.Model/Dlna/CodecProfile.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 using System; +using System.Linq; using System.Xml.Serialization; -using MediaBrowser.Model.Extensions; namespace MediaBrowser.Model.Dlna { @@ -57,7 +57,7 @@ namespace MediaBrowser.Model.Dlna foreach (var val in codec) { - if (ListHelper.ContainsIgnoreCase(codecs, val)) + if (codecs.Contains(val, StringComparer.OrdinalIgnoreCase)) { return true; } diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 7423efaf65..0c3bd88829 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 using System; +using System.Linq; using System.Globalization; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna @@ -167,9 +167,7 @@ namespace MediaBrowser.Model.Dlna switch (condition.Condition) { case ProfileConditionType.EqualsAny: - { - return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue); - } + return expected.Split('|').Contains(currentValue, StringComparer.OrdinalIgnoreCase); case ProfileConditionType.Equals: return string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase); case ProfileConditionType.NotEquals: diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index e6691c5139..cc2417a709 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 using System; +using System.Linq; using System.Xml.Serialization; -using MediaBrowser.Model.Extensions; namespace MediaBrowser.Model.Dlna { @@ -45,7 +45,7 @@ namespace MediaBrowser.Model.Dlna public static bool ContainsContainer(string profileContainers, string inputContainer) { var isNegativeList = false; - if (profileContainers != null && profileContainers.StartsWith("-")) + if (profileContainers != null && profileContainers.StartsWith("-", StringComparison.Ordinal)) { isNegativeList = true; profileContainers = profileContainers.Substring(1); @@ -72,7 +72,7 @@ namespace MediaBrowser.Model.Dlna foreach (var container in allInputContainers) { - if (ListHelper.ContainsIgnoreCase(profileContainers, container)) + if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase)) { return false; } @@ -86,7 +86,7 @@ namespace MediaBrowser.Model.Dlna foreach (var container in allInputContainers) { - if (ListHelper.ContainsIgnoreCase(profileContainers, container)) + if (profileContainers.Contains(container, StringComparer.OrdinalIgnoreCase)) { return true; } diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 0cefbbe012..3813ac5ebb 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 using System; +using System.Linq; using System.Xml.Serialization; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna @@ -93,14 +93,14 @@ namespace MediaBrowser.Model.Dlna public DeviceProfile() { - DirectPlayProfiles = new DirectPlayProfile[] { }; - TranscodingProfiles = new TranscodingProfile[] { }; - ResponseProfiles = new ResponseProfile[] { }; - CodecProfiles = new CodecProfile[] { }; - ContainerProfiles = new ContainerProfile[] { }; + DirectPlayProfiles = Array.Empty(); + TranscodingProfiles = Array.Empty(); + ResponseProfiles = Array.Empty(); + CodecProfiles = Array.Empty(); + ContainerProfiles = Array.Empty(); SubtitleProfiles = Array.Empty(); - XmlRootAttributes = new XmlAttribute[] { }; + XmlRootAttributes = Array.Empty(); SupportedMediaTypes = "Audio,Photo,Video"; MaxStreamingBitrate = 8000000; @@ -129,13 +129,14 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty)) + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } return i; } + return null; } @@ -155,7 +156,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty)) + if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } @@ -185,7 +186,7 @@ namespace MediaBrowser.Model.Dlna } var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty)) + if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } @@ -288,13 +289,13 @@ namespace MediaBrowser.Model.Dlna } var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty)) + if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } var videoCodecs = i.GetVideoCodecs(); - if (videoCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec ?? string.Empty)) + if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs index 6a8f655ac5..9c28019aad 100644 --- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs +++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs @@ -1,7 +1,8 @@ #pragma warning disable CS1591 +using System; +using System.Linq; using System.Xml.Serialization; -using MediaBrowser.Model.Extensions; namespace MediaBrowser.Model.Dlna { @@ -40,7 +41,7 @@ namespace MediaBrowser.Model.Dlna } var languages = GetLanguages(); - return languages.Length == 0 || ListHelper.ContainsIgnoreCase(languages, subLanguage); + return languages.Length == 0 || languages.Contains(subLanguage, StringComparer.OrdinalIgnoreCase); } } } diff --git a/MediaBrowser.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs new file mode 100644 index 0000000000..b6bfaf2e9b --- /dev/null +++ b/MediaBrowser.Model/Dto/PublicUserDto.cs @@ -0,0 +1,48 @@ +using System; + +namespace MediaBrowser.Model.Dto +{ + /// + /// Class PublicUserDto. Its goal is to show only public information about a user + /// + public class PublicUserDto : IItemDto + { + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } + + /// + /// Gets or sets the primary image tag. + /// + /// The primary image tag. + public string PrimaryImageTag { get; set; } + + /// + /// Gets or sets a value indicating whether this instance has password. + /// + /// true if this instance has password; otherwise, false. + public bool HasPassword { get; set; } + + /// + /// Gets or sets a value indicating whether this instance has configured password. + /// Note that in this case this method should not be here, but it is necessary when changing password at the + /// first login. + /// + /// true if this instance has configured password; otherwise, false. + public bool HasConfiguredPassword { get; set; } + + /// + /// Gets or sets the primary image aspect ratio. + /// + /// The primary image aspect ratio. + public double? PrimaryImageAspectRatio { get; set; } + + /// + public override string ToString() + { + return Name ?? base.ToString(); + } + } +} diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index e7e8d7cecd..ac33f1da42 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -34,8 +34,22 @@ namespace MediaBrowser.Model.Entities /// The language. public string Language { get; set; } + /// + /// Gets or sets the color transfer. + /// + /// The color transfer. public string ColorTransfer { get; set; } + + /// + /// Gets or sets the color primaries. + /// + /// The color primaries. public string ColorPrimaries { get; set; } + + /// + /// Gets or sets the color space. + /// + /// The color space. public string ColorSpace { get; set; } /// @@ -44,11 +58,28 @@ namespace MediaBrowser.Model.Entities /// The comment. public string Comment { get; set; } + /// + /// Gets or sets the time base. + /// + /// The time base. public string TimeBase { get; set; } + + /// + /// Gets or sets the codec time base. + /// + /// The codec time base. public string CodecTimeBase { get; set; } + /// + /// Gets or sets the title. + /// + /// The title. public string Title { get; set; } + /// + /// Gets or sets the video range. + /// + /// The video range. public string VideoRange { get @@ -60,7 +91,8 @@ namespace MediaBrowser.Model.Entities var colorTransfer = ColorTransfer; - if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) + || string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) { return "HDR"; } @@ -70,7 +102,9 @@ namespace MediaBrowser.Model.Entities } public string localizedUndefined { get; set; } + public string localizedDefault { get; set; } + public string localizedForced { get; set; } public string DisplayTitle @@ -197,34 +231,34 @@ namespace MediaBrowser.Model.Entities { if (i.IsInterlaced) { - return "1440I"; + return "1440i"; } - return "1440P"; + return "1440p"; } if (width >= 1900 || height >= 1000) { if (i.IsInterlaced) { - return "1080I"; + return "1080i"; } - return "1080P"; + return "1080p"; } if (width >= 1260 || height >= 700) { if (i.IsInterlaced) { - return "720I"; + return "720i"; } - return "720P"; + return "720p"; } if (width >= 700 || height >= 440) { if (i.IsInterlaced) { - return "480I"; + return "480i"; } - return "480P"; + return "480p"; } return "SD"; diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs deleted file mode 100644 index 90ce6f2e5e..0000000000 --- a/MediaBrowser.Model/Extensions/ListHelper.cs +++ /dev/null @@ -1,27 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Extensions -{ - // TODO: @bond remove - public static class ListHelper - { - public static bool ContainsIgnoreCase(string[] list, string value) - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - foreach (var item in list) - { - if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - return false; - } - } -} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b41d0af1d1..461f59672e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -21,9 +21,9 @@ - + - + @@ -37,6 +37,9 @@ + + + ../jellyfin.ruleset diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 06efedb300..b491a015c0 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -17,115 +17,133 @@ namespace MediaBrowser.Model.Net /// private static readonly HashSet _videoFileExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { - ".mkv", - ".m2t", - ".m2ts", - ".img", - ".iso", - ".mk3d", - ".ts", - ".rmvb", - ".mov", + ".3gp", + ".asf", ".avi", - ".mpg", - ".mpeg", - ".wmv", - ".mp4", ".divx", ".dvr-ms", - ".wtv", + ".f4v", + ".flv", + ".img", + ".iso", + ".m2t", + ".m2ts", + ".m2v", + ".m4v", + ".mk3d", + ".mkv", + ".mov", + ".mp4", + ".mpg", + ".mpeg", + ".mts", + ".ogg", ".ogm", ".ogv", - ".asf", - ".m4v", - ".flv", - ".f4v", - ".3gp", + ".rec", + ".ts", + ".rmvb", ".webm", - ".mts", - ".m2v", - ".rec" + ".wmv", + ".wtv", }; // http://en.wikipedia.org/wiki/Internet_media_type + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types + // http://www.iana.org/assignments/media-types/media-types.xhtml // Add more as needed private static readonly Dictionary _mimeTypeLookup = new Dictionary(StringComparer.OrdinalIgnoreCase) { // Type application + { ".7z", "application/x-7z-compressed" }, + { ".azw", "application/vnd.amazon.ebook" }, + { ".azw3", "application/vnd.amazon.ebook" }, { ".cbz", "application/x-cbz" }, { ".cbr", "application/epub+zip" }, { ".eot", "application/vnd.ms-fontobject" }, { ".epub", "application/epub+zip" }, { ".js", "application/x-javascript" }, { ".json", "application/json" }, - { ".map", "application/x-javascript" }, - { ".pdf", "application/pdf" }, - { ".ttml", "application/ttml+xml" }, { ".m3u8", "application/x-mpegURL" }, + { ".map", "application/x-javascript" }, { ".mobi", "application/x-mobipocket-ebook" }, - { ".xml", "application/xml" }, + { ".opf", "application/oebps-package+xml" }, + { ".pdf", "application/pdf" }, + { ".rar", "application/vnd.rar" }, + { ".srt", "application/x-subrip" }, + { ".ttml", "application/ttml+xml" }, { ".wasm", "application/wasm" }, + { ".xml", "application/xml" }, + { ".zip", "application/zip" }, // Type image + { ".bmp", "image/bmp" }, + { ".gif", "image/gif" }, + { ".ico", "image/vnd.microsoft.icon" }, { ".jpg", "image/jpeg" }, { ".jpeg", "image/jpeg" }, - { ".tbn", "image/jpeg" }, { ".png", "image/png" }, - { ".gif", "image/gif" }, - { ".tiff", "image/tiff" }, - { ".webp", "image/webp" }, - { ".ico", "image/vnd.microsoft.icon" }, { ".svg", "image/svg+xml" }, { ".svgz", "image/svg+xml" }, + { ".tbn", "image/jpeg" }, + { ".tif", "image/tiff" }, + { ".tiff", "image/tiff" }, + { ".webp", "image/webp" }, // Type font { ".ttf" , "font/ttf" }, { ".woff" , "font/woff" }, + { ".woff2" , "font/woff2" }, // Type text { ".ass", "text/x-ssa" }, { ".ssa", "text/x-ssa" }, { ".css", "text/css" }, { ".csv", "text/csv" }, + { ".rtf", "text/rtf" }, { ".txt", "text/plain" }, { ".vtt", "text/vtt" }, // Type video - { ".mpg", "video/mpeg" }, - { ".ogv", "video/ogg" }, - { ".mov", "video/quicktime" }, - { ".webm", "video/webm" }, - { ".mkv", "video/x-matroska" }, - { ".wmv", "video/x-ms-wmv" }, - { ".flv", "video/x-flv" }, - { ".avi", "video/x-msvideo" }, - { ".asf", "video/x-ms-asf" }, - { ".m4v", "video/x-m4v" }, - { ".m4s", "video/mp4" }, { ".3gp", "video/3gpp" }, { ".3g2", "video/3gpp2" }, - { ".mpd", "video/vnd.mpeg.dash.mpd" }, - { ".ts", "video/mp2t" }, + { ".asf", "video/x-ms-asf" }, + { ".avi", "video/x-msvideo" }, + { ".flv", "video/x-flv" }, + { ".mp4", "video/mp4" }, + { ".m4s", "video/mp4" }, + { ".m4v", "video/x-m4v" }, { ".mpegts", "video/mp2t" }, + { ".mpg", "video/mpeg" }, + { ".mkv", "video/x-matroska" }, + { ".mov", "video/quicktime" }, + { ".mpd", "video/vnd.mpeg.dash.mpd" }, + { ".ogv", "video/ogg" }, + { ".ts", "video/mp2t" }, + { ".webm", "video/webm" }, + { ".wmv", "video/x-ms-wmv" }, // Type audio - { ".mp3", "audio/mpeg" }, - { ".m4a", "audio/mp4" }, { ".aac", "audio/mp4" }, - { ".webma", "audio/webm" }, - { ".wav", "audio/wav" }, - { ".wma", "audio/x-ms-wma" }, - { ".ogg", "audio/ogg" }, - { ".oga", "audio/ogg" }, - { ".opus", "audio/ogg" }, { ".ac3", "audio/ac3" }, + { ".ape", "audio/x-ape" }, { ".dsf", "audio/dsf" }, - { ".m4b", "audio/m4b" }, - { ".xsp", "audio/xsp" }, { ".dsp", "audio/dsp" }, { ".flac", "audio/flac" }, - { ".ape", "audio/x-ape" }, + { ".m4a", "audio/mp4" }, + { ".m4b", "audio/m4b" }, + { ".mid", "audio/midi" }, + { ".midi", "audio/midi" }, + { ".mp3", "audio/mpeg" }, + { ".oga", "audio/ogg" }, + { ".ogg", "audio/ogg" }, + { ".opus", "audio/ogg" }, + { ".vorbis", "audio/vorbis" }, + { ".wav", "audio/wav" }, + { ".webma", "audio/webm" }, + { ".wma", "audio/x-ms-wma" }, { ".wv", "audio/x-wavpack" }, + { ".xsp", "audio/xsp" }, }; private static readonly Dictionary _extensionLookup = CreateExtensionLookup(); diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs index 7575224d4e..03f03e4ccd 100644 --- a/MediaBrowser.Model/Net/WebSocketMessage.cs +++ b/MediaBrowser.Model/Net/WebSocketMessage.cs @@ -1,5 +1,8 @@ + #pragma warning disable CS1591 +using System; + namespace MediaBrowser.Model.Net { /// @@ -13,7 +16,9 @@ namespace MediaBrowser.Model.Net /// /// The type of the message. public string MessageType { get; set; } - public string MessageId { get; set; } + + public Guid MessageId { get; set; } + public string ServerId { get; set; } /// @@ -22,5 +27,4 @@ namespace MediaBrowser.Model.Net /// The data. public T Data { get; set; } } - } diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index 79a128e9be..9c54bd70e0 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 using System; -using MediaBrowser.Model.Extensions; +using System.Linq; using MediaBrowser.Model.Users; namespace MediaBrowser.Model.Notifications @@ -81,8 +81,12 @@ namespace MediaBrowser.Model.Notifications { foreach (NotificationOption i in Options) { - if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) return i; + if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) + { + return i; + } } + return null; } @@ -98,7 +102,7 @@ namespace MediaBrowser.Model.Notifications NotificationOption opt = GetOptions(notificationType); return opt == null || - !ListHelper.ContainsIgnoreCase(opt.DisabledServices, service); + !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase); } public bool IsEnabledToMonitorUser(string type, Guid userId) @@ -106,7 +110,7 @@ namespace MediaBrowser.Model.Notifications NotificationOption opt = GetOptions(type); return opt != null && opt.Enabled && - !ListHelper.ContainsIgnoreCase(opt.DisabledMonitorUsers, userId.ToString("")); + !opt.DisabledMonitorUsers.Contains(userId.ToString(""), StringComparer.OrdinalIgnoreCase); } public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy) @@ -125,7 +129,7 @@ namespace MediaBrowser.Model.Notifications return true; } - return ListHelper.ContainsIgnoreCase(opt.SendToUsers, userId); + return opt.SendToUsers.Contains(userId, StringComparer.OrdinalIgnoreCase); } return false; diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 3d543039ec..f2c5aa1e39 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -115,24 +115,6 @@ namespace MediaBrowser.Model.System /// The transcode path. public string TranscodingTempPath { get; set; } - /// - /// Gets or sets the HTTP server port number. - /// - /// The HTTP server port number. - public int HttpServerPortNumber { get; set; } - - /// - /// Gets or sets a value indicating whether [enable HTTPS]. - /// - /// true if [enable HTTPS]; otherwise, false. - public bool SupportsHttps { get; set; } - - /// - /// Gets or sets the HTTPS server port number. - /// - /// The HTTPS server port number. - public int HttpsPortNumber { get; set; } - /// /// Gets or sets a value indicating whether this instance has update available. /// diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 1b3df63b63..5073b40157 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index 37160dd2c0..f0328e8d87 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -11,13 +11,10 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Omdb { - public class OmdbEpisodeProvider : - IRemoteMetadataProvider, - IHasOrder + public class OmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; @@ -26,16 +23,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbEpisodeProvider( + IJsonSerializer jsonSerializer, + IApplicationHost appHost, + IHttpClient httpClient, + ILibraryManager libraryManager, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; - _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, logger, libraryManager, fileSystem, configurationManager); + _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, libraryManager, fileSystem, configurationManager); } + // After TheTvDb + public int Order => 1; + + public string Name => "The Open Movie Database"; + public Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { return _itemProvider.GetSearchResults(searchInfo, "episode", cancellationToken); @@ -66,10 +74,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb return result; } - // After TheTvDb - public int Order => 1; - - public string Name => "The Open Movie Database"; public Task GetImageResponse(string url, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 3aadda5d07..64a75955a2 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -17,7 +17,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Omdb { @@ -26,22 +25,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; - private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbItemProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbItemProvider( + IJsonSerializer jsonSerializer, + IApplicationHost appHost, + IHttpClient httpClient, + ILibraryManager libraryManager, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; - _logger = logger; _libraryManager = libraryManager; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; } + // After primary option public int Order => 2; @@ -80,7 +84,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var parsedName = _libraryManager.ParseName(name); var yearInName = parsedName.Year; name = parsedName.Name; - year = year ?? yearInName; + year ??= yearInName; } if (string.IsNullOrWhiteSpace(imdbId)) @@ -312,6 +316,5 @@ namespace MediaBrowser.Providers.Plugins.Omdb /// The results. public List Search { get; set; } } - } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index 7195dc42a7..6e3c26c263 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -27,9 +27,6 @@ namespace MediaBrowser.Providers.Tmdb.TV public class TmdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - internal static TmdbSeriesProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; @@ -39,6 +36,10 @@ namespace MediaBrowser.Providers.Tmdb.TV private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + internal static TmdbSeriesProvider Current { get; private set; } + public TmdbSeriesProvider( IJsonSerializer jsonSerializer, IFileSystem fileSystem, @@ -217,10 +218,9 @@ namespace MediaBrowser.Providers.Tmdb.TV var series = seriesResult.Item; series.Name = seriesInfo.Name; + series.OriginalTitle = seriesInfo.Original_Name; series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.Id.ToString(_usCulture)); - //series.VoteCount = seriesInfo.vote_count; - string voteAvg = seriesInfo.Vote_Average.ToString(CultureInfo.InvariantCulture); if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out float rating)) @@ -240,7 +240,7 @@ namespace MediaBrowser.Providers.Tmdb.TV series.Genres = seriesInfo.Genres.Select(i => i.Name).ToArray(); } - //series.HomePageUrl = seriesInfo.homepage; + series.HomePageUrl = seriesInfo.Homepage; series.RunTimeTicks = seriesInfo.Episode_Run_Time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); @@ -308,29 +308,61 @@ namespace MediaBrowser.Providers.Tmdb.TV seriesResult.ResetPeople(); var tmdbImageUrl = settings.images.GetImageUrl("original"); - if (seriesInfo.Credits != null && seriesInfo.Credits.Cast != null) + if (seriesInfo.Credits != null) { - foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) + if (seriesInfo.Credits.Cast != null) { - var personInfo = new PersonInfo + foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) { - Name = actor.Name.Trim(), - Role = actor.Character, - Type = PersonType.Actor, - SortOrder = actor.Order + var personInfo = new PersonInfo + { + Name = actor.Name.Trim(), + Role = actor.Character, + Type = PersonType.Actor, + SortOrder = actor.Order + }; + + if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) + { + personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; + } + + if (actor.Id > 0) + { + personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + } + + seriesResult.AddPerson(personInfo); + } + } + + if (seriesInfo.Credits.Crew != null) + { + var keepTypes = new[] + { + PersonType.Director, + PersonType.Writer, + PersonType.Producer }; - if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) + foreach (var person in seriesInfo.Credits.Crew) { - personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; - } + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); - if (actor.Id > 0) - { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); - } + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } - seriesResult.AddPerson(personInfo); + seriesResult.AddPerson(new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }); + } } } } diff --git a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs index 035b99c1a6..7dacc74044 100644 --- a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs @@ -4,18 +4,51 @@ using MediaBrowser.Providers.Tmdb.Models.General; namespace MediaBrowser.Providers.Tmdb { + /// + /// Utilities for the TMDb provider + /// public static class TmdbUtils { + /// + /// URL of the TMDB instance to use. + /// public const string BaseTmdbUrl = "https://www.themoviedb.org/"; + + /// + /// URL of the TMDB API instance to use. + /// public const string BaseTmdbApiUrl = "https://api.themoviedb.org/"; + + /// + /// Name of the provider. + /// public const string ProviderName = "TheMovieDb"; + + /// + /// API key to use when performing an API call. + /// public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; + + /// + /// Value of the Accept header for requests to the provider. + /// public const string AcceptHeader = "application/json,image/*"; + /// + /// Maps the TMDB provided roles for crew members to Jellyfin roles. + /// + /// Crew member to map against the Jellyfin person types. + /// The Jellyfin person type. public static string MapCrewToPersonType(Crew crew) { if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) - && crew.Job.IndexOf("producer", StringComparison.InvariantCultureIgnoreCase) != -1) + && crew.Job.Contains("director", StringComparison.InvariantCultureIgnoreCase)) + { + return PersonType.Director; + } + + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) + && crew.Job.Contains("producer", StringComparison.InvariantCultureIgnoreCase)) { return PersonType.Producer; } diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a1dbe80476..e100c0b1cd 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -2,6 +2,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server", "Jellyfin.Server\Jellyfin.Server.csproj", "{07E39F42-A2C6-4B32-AF8C-725F957A73FF}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}" @@ -36,33 +38,39 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{960295EE-4AF4-4440-A525-B4C295B01A61}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server", "Jellyfin.Server\Jellyfin.Server.csproj", "{07E39F42-A2C6-4B32-AF8C-725F957A73FF}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{41093F42-C7CC-4D07-956B-6182CBEDE2EC}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + jellyfin.ruleset = jellyfin.ruleset SharedVersion.cs = SharedVersion.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing.Skia", "Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj", "{154872D9-6C12-4007-96E3-8F70A58386CE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api", "Jellyfin.Api\Jellyfin.Api.csproj", "{DFBEFB4C-DA19-4143-98B7-27320C7F7163}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Api", "Jellyfin.Api\Jellyfin.Api.csproj", "{DFBEFB4C-DA19-4143-98B7-27320C7F7163}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}" + ProjectSection(SolutionItems) = preProject + tests\jellyfin-tests.ruleset = tests\jellyfin-tests.ruleset + EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Common.Tests", "tests\Jellyfin.Common.Tests\Jellyfin.Common.Tests.csproj", "{DF194677-DFD3-42AF-9F75-D44D5A416478}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Common.Tests", "tests\Jellyfin.Common.Tests\Jellyfin.Common.Tests.csproj", "{DF194677-DFD3-42AF-9F75-D44D5A416478}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Tests", "tests\Jellyfin.MediaEncoding.Tests\Jellyfin.MediaEncoding.Tests.csproj", "{28464062-0939-4AA7-9F7B-24DDDA61A7C0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.MediaEncoding.Tests", "tests\Jellyfin.MediaEncoding.Tests\Jellyfin.MediaEncoding.Tests.csproj", "{28464062-0939-4AA7-9F7B-24DDDA61A7C0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Naming.Tests", "tests\Jellyfin.Naming.Tests\Jellyfin.Naming.Tests.csproj", "{3998657B-1CCC-49DD-A19F-275DC8495F57}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Naming.Tests", "tests\Jellyfin.Naming.Tests\Jellyfin.Naming.Tests.csproj", "{3998657B-1CCC-49DD-A19F-275DC8495F57}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api.Tests", "tests\Jellyfin.Api.Tests\Jellyfin.Api.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Api.Tests", "tests\Jellyfin.Api.Tests\Jellyfin.Api.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementations.Tests", "tests\Jellyfin.Server.Implementations.Tests\Jellyfin.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementations.Tests", "tests\Jellyfin.Server.Implementations.Tests\Jellyfin.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Data", "Jellyfin.Data\Jellyfin.Data.csproj", "{F03299F2-469F-40EF-A655-3766F97A5702}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Data", "Jellyfin.Data\Jellyfin.Data.csproj", "{F03299F2-469F-40EF-A655-3766F97A5702}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementations", "Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj", "{DAE48069-6D86-4BA6-B148-D1D49B6DDA52}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Api.Tests", "tests\MediaBrowser.Api.Tests\MediaBrowser.Api.Tests.csproj", "{7C93C84F-105C-48E5-A878-406FA0A5B296}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -114,10 +122,6 @@ Global {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.Build.0 = Release|Any CPU - {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -182,6 +186,14 @@ Global {F03299F2-469F-40EF-A655-3766F97A5702}.Debug|Any CPU.Build.0 = Debug|Any CPU {F03299F2-469F-40EF-A655-3766F97A5702}.Release|Any CPU.ActiveCfg = Release|Any CPU {F03299F2-469F-40EF-A655-3766F97A5702}.Release|Any CPU.Build.0 = Release|Any CPU + {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Release|Any CPU.Build.0 = Release|Any CPU + {7C93C84F-105C-48E5-A878-406FA0A5B296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C93C84F-105C-48E5-A878-406FA0A5B296}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C93C84F-105C-48E5-A878-406FA0A5B296}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C93C84F-105C-48E5-A878-406FA0A5B296}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -214,5 +226,6 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {7C93C84F-105C-48E5-A878-406FA0A5B296} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection EndGlobal diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index fb76f34d0e..9c4b7b0b07 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index ac0c970c13..8b14cf800d 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -21,5 +21,15 @@ + + + + + + + + + ../jellyfin-tests.ruleset + diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index 40d80607c8..d11809de11 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -23,9 +23,9 @@ namespace Jellyfin.Naming.Tests.Subtitles var result = parser.ParseFile(input); - Assert.Equal(language, result.Language, true); - Assert.Equal(isDefault, result.IsDefault); - Assert.Equal(isForced, result.IsForced); + Assert.Equal(language, result?.Language, true); + Assert.Equal(isDefault, result?.IsDefault); + Assert.Equal(isForced, result?.IsForced); } [Theory] diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs index 553d06681b..356ba216d6 100644 --- a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs @@ -21,7 +21,7 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(options) .Resolve(path, false, null, null, true); - Assert.Equal(episodeNumber, result.EpisodeNumber); + Assert.Equal(episodeNumber, result?.EpisodeNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs index 6ecffe80b7..2937914b9f 100644 --- a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs @@ -6,8 +6,6 @@ namespace Jellyfin.Naming.Tests.TV { public class DailyEpisodeTests { - - [Theory] [InlineData(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14)] [InlineData(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14)] @@ -23,12 +21,12 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(options) .Resolve(path, false); - Assert.Null(result.SeasonNumber); - Assert.Null(result.EpisodeNumber); - Assert.Equal(year, result.Year); - Assert.Equal(month, result.Month); - Assert.Equal(day, result.Day); - Assert.Equal(seriesName, result.SeriesName, true); + Assert.Null(result?.SeasonNumber); + Assert.Null(result?.EpisodeNumber); + Assert.Equal(year, result?.Year); + Assert.Equal(month, result?.Month); + Assert.Equal(day, result?.Day); + Assert.Equal(seriesName, result?.SeriesName, true); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs index 0c7d9520e2..8bd1a43d62 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs @@ -6,7 +6,6 @@ namespace Jellyfin.Naming.Tests.TV { public class EpisodeNumberWithoutSeasonTests { - [Theory] [InlineData(8, @"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")] [InlineData(2, @"The Simpsons/The Simpsons - 02 - Ep Name.avi")] @@ -30,7 +29,7 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(options) .Resolve(path, false); - Assert.Equal(episodeNumber, result.EpisodeNumber); + Assert.Equal(episodeNumber, result?.EpisodeNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs index 4b56067153..03aeb7f76b 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -1,4 +1,4 @@ -using Emby.Naming.Common; +using Emby.Naming.Common; using Emby.Naming.TV; using Xunit; @@ -35,7 +35,6 @@ namespace Jellyfin.Naming.Tests.TV // TODO: [InlineData("Watchmen (2019)/Watchmen 1x03 [WEBDL-720p][EAC3 5.1][h264][-TBS] - She Was Killed by Space Junk.mkv", "Watchmen (2019)", 1, 3)] // TODO: [InlineData("/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv", "The Legend of Condor Heroes 2017", 1, 7)] public void ParseEpisodesCorrectly(string path, string name, int season, int episode) - { NamingOptions o = new NamingOptions(); EpisodePathParser p = new EpisodePathParser(o); diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs index 364eb7ff85..d0418a49ed 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs @@ -19,9 +19,9 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(options) .Resolve(path, false); - Assert.Equal(seasonNumber, result.SeasonNumber); - Assert.Equal(episodeNumber, result.EpisodeNumber); - Assert.Equal(seriesName, result.SeriesName, true); + Assert.Equal(seasonNumber, result?.SeasonNumber); + Assert.Equal(episodeNumber, result?.EpisodeNumber); + Assert.Equal(seriesName, result?.SeriesName, ignoreCase: true); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs index 9eaf897b9e..4837e3a3b4 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs @@ -59,7 +59,7 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(_namingOptions) .Resolve(path, false); - Assert.Equal(expected, result.SeasonNumber); + Assert.Equal(expected, result?.SeasonNumber); } } } diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs index de253ce375..40b41b9f3d 100644 --- a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs @@ -31,9 +31,9 @@ namespace Jellyfin.Naming.Tests.TV var result = new EpisodeResolver(options) .Resolve(path, false); - Assert.Equal(seasonNumber, result.SeasonNumber); - Assert.Equal(episodeNumber, result.EpisodeNumber); - Assert.Equal(seriesName, result.SeriesName, true); + Assert.Equal(seasonNumber, result?.SeasonNumber); + Assert.Equal(episodeNumber, result?.EpisodeNumber); + Assert.Equal(seriesName, result?.SeriesName, true); } } } diff --git a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs deleted file mode 100644 index 0c2978aca9..0000000000 --- a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Emby.Naming.Common; -using Emby.Naming.Video; - -namespace Jellyfin.Naming.Tests.Video -{ - public abstract class BaseVideoTest - { - private readonly NamingOptions _namingOptions = new NamingOptions(); - - protected VideoResolver GetParser() - => new VideoResolver(_namingOptions); - } -} diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index 49cb2387bb..917d8fb3a9 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -46,6 +46,7 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)] // FIXME: [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)] [InlineData(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014)] // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again + [InlineData("3 days to kill (2005).mkv", "3 days to kill", 2005)] public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) { input = Path.GetFileName(input); diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs index a64d173496..a2722a1753 100644 --- a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs @@ -5,7 +5,7 @@ using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class ExtraTests : BaseVideoTest + public class ExtraTests { private readonly NamingOptions _videoOptions = new NamingOptions(); diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs index ed3112936d..69de96a47a 100644 --- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -4,29 +4,29 @@ using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class Format3DTests : BaseVideoTest + public class Format3DTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + [Fact] public void TestKodiFormat3D() { - var options = new NamingOptions(); - - Test("Super movie.3d.mp4", false, null, options); - Test("Super movie.3d.hsbs.mp4", true, "hsbs", options); - Test("Super movie.3d.sbs.mp4", true, "sbs", options); - Test("Super movie.3d.htab.mp4", true, "htab", options); - Test("Super movie.3d.tab.mp4", true, "tab", options); - Test("Super movie 3d hsbs.mp4", true, "hsbs", options); + Test("Super movie.3d.mp4", false, null); + Test("Super movie.3d.hsbs.mp4", true, "hsbs"); + Test("Super movie.3d.sbs.mp4", true, "sbs"); + Test("Super movie.3d.htab.mp4", true, "htab"); + Test("Super movie.3d.tab.mp4", true, "tab"); + Test("Super movie 3d hsbs.mp4", true, "hsbs"); } [Fact] public void Test3DName() { var result = - GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv"); + new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv"); - Assert.Equal("hsbs", result.Format3D); - Assert.Equal("Oblivion", result.Name); + Assert.Equal("hsbs", result?.Format3D); + Assert.Equal("Oblivion", result?.Name); } [Fact] @@ -34,32 +34,31 @@ namespace Jellyfin.Naming.Tests.Video { // These were introduced for Media Browser 3 // Kodi conventions are preferred but these still need to be supported - var options = new NamingOptions(); - Test("Super movie.3d.mp4", false, null, options); - Test("Super movie.3d.hsbs.mp4", true, "hsbs", options); - Test("Super movie.3d.sbs.mp4", true, "sbs", options); - Test("Super movie.3d.htab.mp4", true, "htab", options); - Test("Super movie.3d.tab.mp4", true, "tab", options); + Test("Super movie.3d.mp4", false, null); + Test("Super movie.3d.hsbs.mp4", true, "hsbs"); + Test("Super movie.3d.sbs.mp4", true, "sbs"); + Test("Super movie.3d.htab.mp4", true, "htab"); + Test("Super movie.3d.tab.mp4", true, "tab"); - Test("Super movie.hsbs.mp4", true, "hsbs", options); - Test("Super movie.sbs.mp4", true, "sbs", options); - Test("Super movie.htab.mp4", true, "htab", options); - Test("Super movie.tab.mp4", true, "tab", options); - Test("Super movie.sbs3d.mp4", true, "sbs3d", options); - Test("Super movie.3d.mvc.mp4", true, "mvc", options); + Test("Super movie.hsbs.mp4", true, "hsbs"); + Test("Super movie.sbs.mp4", true, "sbs"); + Test("Super movie.htab.mp4", true, "htab"); + Test("Super movie.tab.mp4", true, "tab"); + Test("Super movie.sbs3d.mp4", true, "sbs3d"); + Test("Super movie.3d.mvc.mp4", true, "mvc"); - Test("Super movie [3d].mp4", false, null, options); - Test("Super movie [hsbs].mp4", true, "hsbs", options); - Test("Super movie [fsbs].mp4", true, "fsbs", options); - Test("Super movie [ftab].mp4", true, "ftab", options); - Test("Super movie [htab].mp4", true, "htab", options); - Test("Super movie [sbs3d].mp4", true, "sbs3d", options); + Test("Super movie [3d].mp4", false, null); + Test("Super movie [hsbs].mp4", true, "hsbs"); + Test("Super movie [fsbs].mp4", true, "fsbs"); + Test("Super movie [ftab].mp4", true, "ftab"); + Test("Super movie [htab].mp4", true, "htab"); + Test("Super movie [sbs3d].mp4", true, "sbs3d"); } - private void Test(string input, bool is3D, string format3D, NamingOptions options) + private void Test(string input, bool is3D, string? format3D) { - var parser = new Format3DParser(options); + var parser = new Format3DParser(_namingOptions); var result = parser.Parse(input); diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index b8fbb2cb25..4198d69ff8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -8,9 +8,11 @@ namespace Jellyfin.Naming.Tests.Video { public class MultiVersionTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + // FIXME // [Fact] - public void TestMultiEdition1() + private void TestMultiEdition1() { var files = new[] { @@ -26,7 +28,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -35,7 +36,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiEdition2() + private void TestMultiEdition2() { var files = new[] { @@ -51,7 +52,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -74,7 +74,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -83,7 +82,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestLetterFolders() + private void TestLetterFolders() { var files = new[] { @@ -102,7 +101,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(7, result.Count); @@ -112,7 +110,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersionLimit() + private void TestMultiVersionLimit() { var files = new[] { @@ -132,7 +130,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -142,7 +139,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersionLimit2() + private void TestMultiVersionLimit2() { var files = new[] { @@ -163,7 +160,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(9, result.Count); @@ -173,7 +169,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion3() + private void TestMultiVersion3() { var files = new[] { @@ -190,7 +186,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -200,7 +195,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion4() + private void TestMultiVersion4() { // Test for false positive @@ -219,7 +214,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -229,7 +223,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion5() + private void TestMultiVersion5() { var files = new[] { @@ -249,7 +243,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -262,7 +255,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion6() + private void TestMultiVersion6() { var files = new[] { @@ -282,7 +275,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -295,7 +287,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion7() + private void TestMultiVersion7() { var files = new[] { @@ -309,7 +301,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -317,7 +308,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion8() + private void TestMultiVersion8() { // This is not actually supported yet @@ -338,7 +329,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -351,7 +341,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion9() + private void TestMultiVersion9() { // Test for false positive @@ -370,7 +360,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -380,7 +369,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion10() + private void TestMultiVersion10() { var files = new[] { @@ -394,7 +383,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -404,7 +392,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion11() + private void TestMultiVersion11() { // Currently not supported but we should probably handle this. @@ -420,7 +408,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -430,8 +417,7 @@ namespace Jellyfin.Naming.Tests.Video private VideoListResolver GetResolver() { - var options = new NamingOptions(); - return new VideoListResolver(options); + return new VideoListResolver(_namingOptions); } } } diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs index 3e0cbaf0c2..8794d3ebe8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs @@ -6,8 +6,10 @@ using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class StackTests : BaseVideoTest + public class StackTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + [Fact] public void TestSimpleStack() { @@ -366,11 +368,11 @@ namespace Jellyfin.Naming.Tests.Video { var files = new[] { - new FileSystemMetadata{FullName = "Bad Boys (2006) part1.mkv", IsDirectory = false}, - new FileSystemMetadata{FullName = "Bad Boys (2006) part2.mkv", IsDirectory = false}, - new FileSystemMetadata{FullName = "300 (2006) part2", IsDirectory = true}, - new FileSystemMetadata{FullName = "300 (2006) part3", IsDirectory = true}, - new FileSystemMetadata{FullName = "300 (2006) part1", IsDirectory = true} + new FileSystemMetadata { FullName = "Bad Boys (2006) part1.mkv", IsDirectory = false }, + new FileSystemMetadata { FullName = "Bad Boys (2006) part2.mkv", IsDirectory = false }, + new FileSystemMetadata { FullName = "300 (2006) part2", IsDirectory = true }, + new FileSystemMetadata { FullName = "300 (2006) part3", IsDirectory = true }, + new FileSystemMetadata { FullName = "300 (2006) part1", IsDirectory = true } }; var resolver = GetResolver(); @@ -446,7 +448,7 @@ namespace Jellyfin.Naming.Tests.Video private StackResolver GetResolver() { - return new StackResolver(new NamingOptions()); + return new StackResolver(_namingOptions); } } } diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs index 8d5ced9a41..30ba941365 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs @@ -4,8 +4,10 @@ using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class StubTests : BaseVideoTest + public class StubTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + [Fact] public void TestStubs() { @@ -27,16 +29,14 @@ namespace Jellyfin.Naming.Tests.Video public void TestStubName() { var result = - GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc"); + new VideoResolver(_namingOptions).ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc"); - Assert.Equal("Oblivion", result.Name); + Assert.Equal("Oblivion", result?.Name); } - private void Test(string path, bool isStub, string stubType) + private void Test(string path, bool isStub, string? stubType) { - var options = new NamingOptions(); - - var isStubResult = StubResolver.TryResolveFile(path, options, out var stubTypeResult); + var isStubResult = StubResolver.TryResolveFile(path, _namingOptions, out var stubTypeResult); Assert.Equal(isStub, isStubResult); diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs index ef8a178989..12c4a50fe3 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -8,9 +8,11 @@ namespace Jellyfin.Naming.Tests.Video { public class VideoListResolverTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + // FIXME // [Fact] - public void TestStackAndExtras() + private void TestStackAndExtras() { // No stacking here because there is no part/disc/etc var files = new[] @@ -44,7 +46,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -73,7 +74,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -94,7 +94,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -115,7 +114,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -137,7 +135,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -158,7 +155,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -183,7 +179,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -204,7 +199,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = true, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -226,7 +220,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = true, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -248,7 +241,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -270,7 +262,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -293,7 +284,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -316,7 +306,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -336,7 +325,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -356,7 +344,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -377,7 +364,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -398,7 +384,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -421,7 +406,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(4, result.Count); @@ -442,7 +426,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -450,8 +433,7 @@ namespace Jellyfin.Naming.Tests.Video private VideoListResolver GetResolver() { - var options = new NamingOptions(); - return new VideoListResolver(options); + return new VideoListResolver(_namingOptions); } } } diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index 5a3ce88869..99828b2eb7 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -1,275 +1,199 @@ -using MediaBrowser.Model.Entities; +using System.Collections.Generic; +using Emby.Naming.Common; +using Emby.Naming.Video; +using MediaBrowser.Model.Entities; using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class VideoResolverTests : BaseVideoTest + public class VideoResolverTests { - // FIXME - // [Fact] - public void TestSimpleFile() + private readonly NamingOptions _namingOptions = new NamingOptions(); + + public static IEnumerable GetResolveFileTestData() { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("Brave", result.Name); - Assert.Null(result.ExtraType); + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/7 Psychos.mkv/7 Psychos.mkv", + Container = "mkv", + Name = "7 Psychos" + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv", + Container = "mkv", + Name = "3 days to kill", + Year = 2005 + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/American Psycho/American.Psycho.mkv", + Container = "mkv", + Name = "American.Psycho", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv", + Container = "mkv", + Name = "brave", + Year = 2006, + Is3D = true, + Format3D = "sbs", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv", + Container = "mkv", + Name = "300", + Year = 2006 + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv", + Container = "mkv", + Name = "300", + Year = 2006, + Is3D = true, + Format3D = "sbs", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc", + Container = "disc", + Name = "brave", + Year = 2006, + IsStub = true, + StubType = "bluray", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc", + Container = "disc", + Name = "300", + Year = 2006, + IsStub = true, + StubType = "bluray", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/Brave (2007)/Brave (2006).bluray.disc", + Container = "disc", + Name = "Brave", + Year = 2006, + IsStub = true, + StubType = "bluray", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006).bluray.disc", + Container = "disc", + Name = "300", + Year = 2006, + IsStub = true, + StubType = "bluray", + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006)-trailer.mkv", + Container = "mkv", + Name = "300", + Year = 2006, + ExtraType = ExtraType.Trailer, + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv", + Container = "mkv", + Name = "Brave", + Year = 2006, + ExtraType = ExtraType.Trailer, + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/300 (2007)/300 (2006).mkv", + Container = "mkv", + Name = "300", + Year = 2006 + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv", + Container = "mkv", + Name = "Bad Boys", + Year = 1995, + } + }; + yield return new object[] + { + new VideoFileInfo() + { + Path = @"/server/Movies/Brave (2007)/Brave (2006).mkv", + Container = "mkv", + Name = "Brave", + Year = 2006, + } + }; } - // FIXME - // [Fact] - public void TestSimpleFile2() + [Theory] + [MemberData(nameof(GetResolveFileTestData))] + public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { - var parser = GetParser(); + var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path); - var result = - parser.ResolveFile(@"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv"); - - Assert.Equal(1995, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("Bad Boys", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestSimpleFileWithNumericName() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("300", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestExtra() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal(ExtraType.Trailer, result.ExtraType); - Assert.Equal("Brave (2006)-trailer", result.Name); - } - - // FIXME - // [Fact] - public void TestExtraWithNumericName() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("300 (2006)-trailer", result.Name); - Assert.Equal(ExtraType.Trailer, result.ExtraType); - } - - // FIXME - // [Fact] - public void TestStubFileWithNumericName() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).bluray.disc"); - - Assert.Equal(2006, result.Year); - Assert.True(result.IsStub); - Assert.Equal("bluray", result.StubType); - Assert.False(result.Is3D); - Assert.Equal("300", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestStubFile() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).bluray.disc"); - - Assert.Equal(2006, result.Year); - Assert.True(result.IsStub); - Assert.Equal("bluray", result.StubType); - Assert.False(result.Is3D); - Assert.Equal("Brave", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestExtraStubWithNumericNameNotSupported() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc"); - - Assert.Equal(2006, result.Year); - Assert.True(result.IsStub); - Assert.Equal("bluray", result.StubType); - Assert.False(result.Is3D); - Assert.Equal("300", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestExtraStubNotSupported() - { - // Using a stub for an extra is currently not supported - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc"); - - Assert.Equal(2006, result.Year); - Assert.True(result.IsStub); - Assert.Equal("bluray", result.StubType); - Assert.False(result.Is3D); - Assert.Equal("brave", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void Test3DFileWithNumericName() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.True(result.Is3D); - Assert.Equal("sbs", result.Format3D); - Assert.Equal("300", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestBad3DFileWithNumericName() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("300", result.Name); - Assert.Null(result.ExtraType); - Assert.Null(result.Format3D); - } - - // FIXME - // [Fact] - public void Test3DFile() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.True(result.Is3D); - Assert.Equal("sbs", result.Format3D); - Assert.Equal("brave", result.Name); - Assert.Null(result.ExtraType); - } - - [Fact] - public void TestNameWithoutDate() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/American Psycho/American.Psycho.mkv"); - - Assert.Null(result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Null(result.Format3D); - Assert.Equal("American.Psycho", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestCleanDateAndStringsSequence() - { - var parser = GetParser(); - - // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again - var result = - parser.ResolveFile(@"/server/Movies/3.Days.to.Kill/3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv"); - - Assert.Equal(2014, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Null(result.Format3D); - Assert.Equal("3.Days.to.Kill", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestCleanDateAndStringsSequence1() - { - var parser = GetParser(); - - // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again - var result = - parser.ResolveFile(@"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv"); - - Assert.Equal(2005, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Null(result.Format3D); - Assert.Equal("3 days to kill", result.Name); - Assert.Null(result.ExtraType); - } - - [Fact] - public void TestFolderNameWithExtension() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/7 Psychos.mkv/7 Psychos.mkv"); - - Assert.Null(result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("7 Psychos", result.Name); - Assert.Null(result.ExtraType); + Assert.NotNull(result); + Assert.Equal(result?.Path, expectedResult.Path); + Assert.Equal(result?.Container, expectedResult.Container); + Assert.Equal(result?.Name, expectedResult.Name); + Assert.Equal(result?.Year, expectedResult.Year); + Assert.Equal(result?.ExtraType, expectedResult.ExtraType); + Assert.Equal(result?.Format3D, expectedResult.Format3D); + Assert.Equal(result?.Is3D, expectedResult.Is3D); + Assert.Equal(result?.IsStub, expectedResult.IsStub); + Assert.Equal(result?.StubType, expectedResult.StubType); + Assert.Equal(result?.IsDirectory, expectedResult.IsDirectory); + Assert.Equal(result?.FileNameWithoutExtension, expectedResult.FileNameWithoutExtension); } } } diff --git a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs new file mode 100644 index 0000000000..34698fe251 --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using System.Threading.Tasks; +using MediaBrowser.Model.Branding; +using Xunit; + +namespace MediaBrowser.Api.Tests +{ + public sealed class BrandingServiceTests : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + + public BrandingServiceTests(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task GetConfiguration_ReturnsCorrectResponse() + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync("/Branding/Configuration"); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); + var responseBody = await response.Content.ReadAsStreamAsync(); + _ = await JsonSerializer.DeserializeAsync(responseBody); + } + + [Theory] + [InlineData("/Branding/Css")] + [InlineData("/Branding/Css.css")] + public async Task GetCss_ReturnsCorrectResponse(string url) + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync(url); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("text/css", response.Content.Headers.ContentType.ToString()); + } + } +} diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs new file mode 100644 index 0000000000..c39ed07de3 --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using Emby.Server.Implementations; +using Emby.Server.Implementations.IO; +using Emby.Server.Implementations.Networking; +using Jellyfin.Drawing.Skia; +using Jellyfin.Server; +using MediaBrowser.Common; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Extensions.Logging; + +namespace MediaBrowser.Api.Tests +{ + /// + /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. + /// + public class JellyfinApplicationFactory : WebApplicationFactory + { + private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + private static readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); + + /// + /// Initializes a new instance of the class. + /// + public JellyfinApplicationFactory() + { + // Perform static initialization that only needs to happen once per test-run + Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger(); + Program.PerformStaticInitialization(); + } + + /// + protected override IWebHostBuilder CreateWebHostBuilder() + { + return new WebHostBuilder(); + } + + /// + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + // Specify the startup command line options + var commandLineOpts = new StartupOptions + { + NoWebClient = true, + NoAutoRunWebApp = true + }; + + // Use a temporary directory for the application paths + var webHostPathRoot = Path.Combine(_testPathRoot, "test-host-" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "logs")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "config")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "cache")); + Directory.CreateDirectory(Path.Combine(webHostPathRoot, "jellyfin-web")); + var appPaths = new ServerApplicationPaths( + webHostPathRoot, + Path.Combine(webHostPathRoot, "logs"), + Path.Combine(webHostPathRoot, "config"), + Path.Combine(webHostPathRoot, "cache"), + Path.Combine(webHostPathRoot, "jellyfin-web")); + + // Create the logging config file + // TODO: We shouldn't need to do this since we are only logging to console + Program.InitLoggingConfigFile(appPaths).GetAwaiter().GetResult(); + + // Create a copy of the application configuration to use for startup + var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); + + ILoggerFactory loggerFactory = new SerilogLoggerFactory(); + _disposableComponents.Add(loggerFactory); + + // Create the app host and initialize it + var appHost = new CoreAppHost( + appPaths, + loggerFactory, + commandLineOpts, + new ManagedFileSystem(loggerFactory.CreateLogger(), appPaths), + new NetworkManager(loggerFactory.CreateLogger())); + _disposableComponents.Add(appHost); + var serviceCollection = new ServiceCollection(); + appHost.Init(serviceCollection); + + // Configure the web host builder + Program.ConfigureWebHostBuilder(builder, appHost, serviceCollection, commandLineOpts, startupConfig, appPaths); + } + + /// + protected override TestServer CreateServer(IWebHostBuilder builder) + { + // Create the test server using the base implementation + var testServer = base.CreateServer(builder); + + // Finish initializing the app host + var appHost = (CoreAppHost)testServer.Services.GetRequiredService(); + appHost.ServiceProvider = testServer.Services; + appHost.InitializeServices().GetAwaiter().GetResult(); + appHost.RunStartupTasksAsync().GetAwaiter().GetResult(); + + return testServer; + } + + /// + protected override void Dispose(bool disposing) + { + foreach (var disposable in _disposableComponents) + { + disposable.Dispose(); + } + + _disposableComponents.Clear(); + + base.Dispose(disposing); + } + } +} diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj new file mode 100644 index 0000000000..60c392314d --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -0,0 +1,33 @@ + + + + netcoreapp3.1 + false + true + enable + + + + + + + + + + + + + + + + + + + + + + + ../jellyfin-tests.ruleset + + + diff --git a/tests/jellyfin-tests.ruleset b/tests/jellyfin-tests.ruleset new file mode 100644 index 0000000000..5a113e955e --- /dev/null +++ b/tests/jellyfin-tests.ruleset @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + +