From 976459d3e8a8b889cebc2cf281e38b0fbc19c9b9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 17 Dec 2019 23:15:02 +0100 Subject: [PATCH 001/137] Rewrite WebSocket handling code --- Emby.Dlna/PlayTo/PlayToController.cs | 6 +- Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- .../ApplicationHost.cs | 34 +-- .../HttpServer/HttpListenerHost.cs | 160 ++++++------ .../HttpServer/IHttpListener.cs | 40 --- .../HttpServer/WebSocketConnection.cs | 233 +++++++++--------- Emby.Server.Implementations/Net/IWebSocket.cs | 48 ---- .../Net/WebSocketConnectEventArgs.cs | 31 --- .../Session/HttpSessionController.cs | 191 -------------- .../Session/SessionManager.cs | 13 +- .../Session/SessionWebSocketListener.cs | 36 +-- .../Session/WebSocketController.cs | 70 +++--- .../SocketSharp/SharpWebSocket.cs | 105 -------- .../SocketSharp/WebSocketSharpListener.cs | 138 ----------- Jellyfin.Server/Startup.cs | 2 - .../Session/SessionInfoWebSocketListener.cs | 43 ++-- .../IServerApplicationHost.cs | 2 - .../Net/BasePeriodicWebSocketListener.cs | 1 - MediaBrowser.Controller/Net/IHttpServer.cs | 22 +- .../Net/IWebSocketConnection.cs | 28 +-- .../Session/ISessionController.cs | 3 +- .../Session/SessionInfo.cs | 80 ++---- MediaBrowser.Model/Net/WebSocketMessage.cs | 8 +- 23 files changed, 325 insertions(+), 971 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpServer/IHttpListener.cs delete mode 100644 Emby.Server.Implementations/Net/IWebSocket.cs delete mode 100644 Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs delete mode 100644 Emby.Server.Implementations/Session/HttpSessionController.cs delete mode 100644 Emby.Server.Implementations/SocketSharp/SharpWebSocket.cs delete mode 100644 Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index c58f16438b..a215c9f4bf 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.Didl; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -899,7 +898,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) { @@ -915,10 +915,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.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 2ca44b7ea2..943d52b0d7 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -21,7 +21,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Dlna.PlayTo { - class PlayToManager : IDisposable + public class PlayToManager : IDisposable { private readonly ILogger _logger; private readonly ISessionManager _sessionManager; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832fb..e3e071af95 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -45,7 +45,6 @@ using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Session; -using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using MediaBrowser.Api; @@ -105,7 +104,6 @@ using MediaBrowser.Providers.TV.TheTVDB; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -629,32 +627,8 @@ namespace Emby.Server.Implementations } } - 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, Logger); - 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 resources that classes will depend on @@ -777,7 +751,7 @@ namespace Emby.Server.Implementations NetworkManager, JsonSerializer, XmlSerializer, - CreateHttpListener()) + LoggerFactory) { GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading") }; @@ -1194,8 +1168,6 @@ namespace Emby.Server.Implementations }); } - protected IHttpListener CreateHttpListener() => new WebSocketSharpListener(Logger); - private CertificateInfo GetCertificateInfo(bool generateCertificate) { // Custom cert diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index b0126f7fa5..4baf96ab51 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -7,11 +7,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; @@ -21,9 +22,11 @@ using MediaBrowser.Model.Events; 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.Logging; +using Microsoft.Net.Http.Headers; using ServiceStack.Text.Jsv; namespace Emby.Server.Implementations.HttpServer @@ -31,18 +34,17 @@ namespace Emby.Server.Implementations.HttpServer public class HttpListenerHost : IHttpServer, IDisposable { 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 IWebSocketListener[] _webSocketListeners = Array.Empty(); - private readonly List _webSocketConnections = new List(); private bool _disposed = false; public HttpListenerHost( @@ -53,7 +55,7 @@ namespace Emby.Server.Implementations.HttpServer INetworkManager networkManager, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, - IHttpListener socketListener) + ILoggerFactory loggerFactory) { _appHost = applicationHost; _logger = logger; @@ -63,8 +65,7 @@ namespace Emby.Server.Implementations.HttpServer _networkManager = networkManager; _jsonSerializer = jsonSerializer; _xmlSerializer = xmlSerializer; - _socketListener = socketListener; - _socketListener.WebSocketConnected = OnWebSocketConnected; + _loggerFactory = loggerFactory; _funcParseFn = t => s => JsvReader.GetParseFn(t)(s); @@ -155,38 +156,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) @@ -274,32 +243,6 @@ namespace Emby.Server.Implementations.HttpServer return msg; } - /// - /// 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); @@ -411,31 +354,47 @@ namespace Emby.Server.Implementations.HttpServer private bool ValidateSsl(string remoteIp, string urlString) { - if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy) + if (_config.Configuration.RequireHttps + && _appHost.EnableHttps + && !_config.Configuration.IsBehindProxy + && !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 hnandler + /// 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(); @@ -552,6 +511,44 @@ namespace Emby.Server.Implementations.HttpServer } } + private async Task WebSocketRequestHandler(HttpContext context) + { + if (_disposed) + { + return; + } + + var url = context.Request.GetDisplayUrl(); + _logger.LogInformation("WS {Url}. UserAgent: {UserAgent}", url, context.Request.Headers[HeaderNames.UserAgent].ToString()); + + try + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false); + + var connection = new WebSocketConnection( + _loggerFactory.CreateLogger(), + webSocket, + context.Connection.RemoteIpAddress) + { + Url = url, + QueryString = context.Request.Query, + OnReceive = ProcessWebSocketMessageReceived + }; + + WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); + + await connection.ProcessAsync().ConfigureAwait(false); + } + catch (WebSocketException ex) + { + _logger.LogError(ex, "ProcessWebSocketRequest error"); + if (!context.Response.HasStarted) + { + context.Response.StatusCode = 500; + } + } + } + // Entry point for HttpListener public ServiceHandler GetServiceHandler(IHttpRequest httpReq) { @@ -661,11 +658,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"); @@ -697,7 +689,7 @@ namespace Emby.Server.Implementations.HttpServer if (disposing) { - Stop(); + // TODO: } _disposed = true; diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs deleted file mode 100644 index 1c3496e5d5..0000000000 --- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs +++ /dev/null @@ -1,40 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -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/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 2292d86a4a..b4f420e5d2 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -1,15 +1,16 @@ 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,54 +25,46 @@ 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; + + private bool _disposed = false; /// /// Initializes a new instance of the class. /// /// The socket. /// The remote end point. - /// The json serializer. /// The logger. /// socket - public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger) + public WebSocketConnection(ILogger logger, WebSocket socket, IPAddress remoteEndPoint) { if (socket == null) { throw new ArgumentNullException(nameof(socket)); } - if (string.IsNullOrEmpty(remoteEndPoint)) + if (remoteEndPoint != null) { 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.Closed += OnSocketClosed; + _jsonOptions = JsonDefaults.GetOptions(); } /// @@ -80,7 +73,7 @@ namespace Emby.Server.Implementations.HttpServer /// /// Gets or sets the remote end point. /// - public string RemoteEndPoint { get; private set; } + public IPAddress RemoteEndPoint { get; private set; } /// /// Gets or sets the receive action. @@ -94,12 +87,6 @@ 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. /// @@ -118,70 +105,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. /// @@ -197,40 +120,109 @@ namespace Emby.Server.Implementations.HttpServer 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); + + receiveresult = await _socket.ReceiveAsync(memory, cancellationToken); + int bytesRead = receiveresult.Count; + if (bytesRead == 0) + { + continue; + } + + // 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; + } + + if (receiveresult.EndOfMessage) + { + await ProcessInternal(pipe.Reader).ConfigureAwait(false); + } + } while (_socket.State == WebSocketState.Open && receiveresult.MessageType != WebSocketMessageType.Close); + + if (_socket.State == WebSocketState.Open) + { + await _socket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + string.Empty, // REVIEW: human readable explanation as to why the connection is closed. + cancellationToken).ConfigureAwait(false); } - cancellationToken.ThrowIfCancellationRequested(); + Closed?.Invoke(this, EventArgs.Empty); - return _socket.SendAsync(text, true, cancellationToken); + _socket.Dispose(); + } + + private async Task ProcessInternal(PipeReader reader) + { + LastActivityDate = DateTime.UtcNow; + + if (OnReceive == null) + { + return; + } + + try + { + var result = await reader.ReadAsync().ConfigureAwait(false); + if (!result.IsCompleted) + { + return; + } + + WebSocketMessage stub; + var buffer = result.Buffer; + 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); + } + } + + var info = new WebSocketMessageInfo + { + MessageType = stub.MessageType, + Data = stub.Data.ToString(), + Connection = this + }; + + await OnReceive(info).ConfigureAwait(false); + } + catch (JsonException ex) + { + _logger.LogError(ex, "Error processing web socket message"); + } } /// @@ -246,10 +238,17 @@ namespace Emby.Server.Implementations.HttpServer /// 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) { _socket.Dispose(); } + + _disposed = true; } } } 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 e3047d3926..0000000000 --- a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Net.WebSockets; -using MediaBrowser.Model.Services; -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 b1d513dd4f..db00ceeb71 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -463,8 +463,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; @@ -1024,12 +1023,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); } } @@ -1037,13 +1036,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); } } } diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 930f2d35d3..13b42698d3 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,35 +22,34 @@ 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); @@ -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..c17e67da93 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -11,60 +11,64 @@ 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 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) { + _logger.LogDebug("Removing websocket from session {Session}", _session.Id); var connection = (IWebSocketConnection)sender; - var sockets = Sockets.ToList(); - sockets.Remove(connection); - - Sockets = sockets; - - _sessionManager.CloseIfNeeded(Session); + _sockets.Remove(connection); + _sessionManager.CloseIfNeeded(_session); + connection.Dispose(); } - 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) @@ -77,16 +81,24 @@ namespace Emby.Server.Implementations.Session 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; + socket.Dispose(); + } + + _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 ba5ba1904c..0000000000 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Net; -using MediaBrowser.Controller.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/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 3ee5fb8b50..45f2b9d7c2 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -64,7 +63,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/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index f1a6622fb5..cf7ddd6319 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -31,46 +31,46 @@ namespace MediaBrowser.Api.Session { _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.Session 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.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 25f0905eb8..0790b6cd60 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -92,7 +92,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/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index ee5c1a165a..9d71426d88 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -154,7 +154,6 @@ namespace MediaBrowser.Controller.Net { MessageType = Name, Data = data - }, cancellationToken).ConfigureAwait(false); state.DateLastSendUtc = DateTime.UtcNow; diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 46933c0465..694e3954ed 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; @@ -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,11 @@ 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); } } diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 566897b31f..e2a714d5b1 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,9 +1,9 @@ using System; +using System.Net; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net @@ -15,12 +15,6 @@ namespace MediaBrowser.Controller.Net /// event EventHandler Closed; - /// - /// Gets the id. - /// - /// The id. - Guid Id { get; } - /// /// Gets the last activity date. /// @@ -32,6 +26,7 @@ namespace MediaBrowser.Controller.Net /// /// The URL. string Url { get; set; } + /// /// Gets or sets the query string. /// @@ -54,7 +49,7 @@ namespace MediaBrowser.Controller.Net /// Gets the remote end point. /// /// The remote end point. - string RemoteEndPoint { get; } + IPAddress RemoteEndPoint { get; } /// /// Sends a message asynchronously. @@ -66,21 +61,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 acda6a4166..63ed43a833 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. /// @@ -126,28 +134,6 @@ namespace MediaBrowser.Controller.Session [JsonIgnore] public ISessionController[] SessionControllers { get; set; } - /// - /// Gets or sets the application icon URL. - /// - /// The application icon URL. - public string AppIconUrl { 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; } /// @@ -219,6 +205,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(); @@ -267,10 +261,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) @@ -293,9 +283,6 @@ namespace MediaBrowser.Controller.Session } } - // 1 second - private const long ProgressIncrement = 10000000; - private async void OnProgressTimerCallback(object state) { if (_disposed) @@ -354,8 +341,7 @@ namespace MediaBrowser.Controller.Session } } - private bool _disposed = false; - + /// public void Dispose() { _disposed = true; @@ -367,30 +353,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.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs index c763216f12..308032f833 100644 --- a/MediaBrowser.Model/Net/WebSocketMessage.cs +++ b/MediaBrowser.Model/Net/WebSocketMessage.cs @@ -1,3 +1,5 @@ +using System; + namespace MediaBrowser.Model.Net { /// @@ -11,13 +13,15 @@ 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; } + /// /// Gets or sets the data. /// /// The data. public T Data { get; set; } } - } From 5ca68f9623e414b85ddbda1f97895f1b90bd05e0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Dec 2019 20:57:46 +0100 Subject: [PATCH 002/137] Fix nullref exception and added logging --- .../HttpServer/HttpListenerHost.cs | 17 +++-- .../HttpServer/WebSocketConnection.cs | 63 +++++++------------ .../Session/SessionManager.cs | 3 +- .../Session/SessionWebSocketListener.cs | 2 +- .../Session/WebSocketController.cs | 5 +- .../Net/IWebSocketConnection.cs | 16 ++--- 6 files changed, 41 insertions(+), 65 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 4baf96ab51..ebae4d0b1e 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -518,30 +518,29 @@ namespace Emby.Server.Implementations.HttpServer return; } - var url = context.Request.GetDisplayUrl(); - _logger.LogInformation("WS {Url}. UserAgent: {UserAgent}", url, context.Request.Headers[HeaderNames.UserAgent].ToString()); - try { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false); + _logger.LogInformation("WS Request from {IP}", context.Connection.RemoteIpAddress); + + WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); var connection = new WebSocketConnection( _loggerFactory.CreateLogger(), webSocket, - context.Connection.RemoteIpAddress) + context.Connection.RemoteIpAddress, + context.Request.Query) { - Url = url, - QueryString = context.Request.Query, OnReceive = ProcessWebSocketMessageReceived }; WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); await connection.ProcessAsync().ConfigureAwait(false); + _logger.LogInformation("WS closed from {IP}", context.Connection.RemoteIpAddress); } - catch (WebSocketException ex) + catch (Exception ex) // Otherwise ASP.Net will ignore the exception { - _logger.LogError(ex, "ProcessWebSocketRequest error"); + _logger.LogError(ex, "WebSocketRequestHandler error"); if (!context.Response.HasStarted) { context.Response.StatusCode = 500; diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index b4f420e5d2..88974f9aba 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; using System.Buffers; using System.IO.Pipelines; using System.Net; @@ -39,47 +41,38 @@ namespace Emby.Server.Implementations.HttpServer /// /// Initializes a new instance of the class. /// + /// The logger. /// The socket. /// The remote end point. - /// The logger. - /// socket - public WebSocketConnection(ILogger logger, WebSocket socket, IPAddress remoteEndPoint) + /// The query. + public WebSocketConnection( + ILogger logger, + WebSocket socket, + IPAddress? remoteEndPoint, + IQueryCollection query) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } - - if (remoteEndPoint != null) - { - throw new ArgumentNullException(nameof(remoteEndPoint)); - } - - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } - + _logger = logger; _socket = socket; RemoteEndPoint = remoteEndPoint; - _logger = logger; + QueryString = query; _jsonOptions = JsonDefaults.GetOptions(); + LastActivityDate = DateTime.Now; } /// - public event EventHandler Closed; + public event EventHandler? Closed; /// /// Gets or sets the remote end point. /// - public IPAddress 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. @@ -87,17 +80,11 @@ namespace Emby.Server.Implementations.HttpServer /// The last activity date. public DateTime LastActivityDate { 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. @@ -115,11 +102,6 @@ namespace Emby.Server.Implementations.HttpServer /// message public Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken) { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken); } @@ -140,7 +122,7 @@ namespace Emby.Server.Implementations.HttpServer int bytesRead = receiveresult.Count; if (bytesRead == 0) { - continue; + break; } // Tell the PipeWriter how much was read from the Socket @@ -154,6 +136,8 @@ namespace Emby.Server.Implementations.HttpServer break; } + LastActivityDate = DateTime.UtcNow; + if (receiveresult.EndOfMessage) { await ProcessInternal(pipe.Reader).ConfigureAwait(false); @@ -162,10 +146,7 @@ namespace Emby.Server.Implementations.HttpServer if (_socket.State == WebSocketState.Open) { - await _socket.CloseAsync( - WebSocketCloseStatus.NormalClosure, - string.Empty, // REVIEW: human readable explanation as to why the connection is closed. - cancellationToken).ConfigureAwait(false); + _logger.LogWarning("Stopped reading from websocket before it was closed"); } Closed?.Invoke(this, EventArgs.Empty); @@ -175,8 +156,6 @@ namespace Emby.Server.Implementations.HttpServer private async Task ProcessInternal(PipeReader reader) { - LastActivityDate = DateTime.UtcNow; - if (OnReceive == null) { return; diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index db00ceeb71..0d5df1dadc 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1726,6 +1726,7 @@ namespace Emby.Server.Implementations.Session string.Equals(i.Client, client)); } + /// public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) { if (info == null) @@ -1733,7 +1734,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 13b42698d3..d4e4ba1f2f 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.Session } 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); } } diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index c17e67da93..536013c7ad 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -53,11 +53,12 @@ namespace Emby.Server.Implementations.Session private void OnConnectionClosed(object sender, EventArgs e) { - _logger.LogDebug("Removing websocket from session {Session}", _session.Id); var connection = (IWebSocketConnection)sender; + _logger.LogDebug("Removing websocket from session {Session}", _session.Id); _sockets.Remove(connection); - _sessionManager.CloseIfNeeded(_session); + connection.Closed -= OnConnectionClosed; connection.Dispose(); + _sessionManager.CloseIfNeeded(_session); } /// diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index e2a714d5b1..d5555884df 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Net; using System.Net.WebSockets; @@ -13,7 +15,7 @@ namespace MediaBrowser.Controller.Net /// /// Occurs when [closed]. /// - event EventHandler Closed; + event EventHandler? Closed; /// /// Gets the last activity date. @@ -21,23 +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. @@ -49,7 +45,7 @@ namespace MediaBrowser.Controller.Net /// Gets the remote end point. /// /// The remote end point. - IPAddress RemoteEndPoint { get; } + IPAddress? RemoteEndPoint { get; } /// /// Sends a message asynchronously. From 4d311870d2f40f67da6df5641b53df637fdee88d Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 27 Dec 2019 14:42:53 +0100 Subject: [PATCH 003/137] Fix websocket handling --- .../HttpServer/WebSocketConnection.cs | 79 ++++++++----------- .../Session/WebSocketController.cs | 2 - .../Net/IWebSocketConnection.cs | 2 +- 3 files changed, 33 insertions(+), 50 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 88974f9aba..913a51217f 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.HttpServer /// The message. /// The cancellation token. /// Task. - /// message public Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken) { var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); @@ -117,7 +116,6 @@ namespace Emby.Server.Implementations.HttpServer { // Allocate at least 512 bytes from the PipeWriter Memory memory = writer.GetMemory(512); - receiveresult = await _socket.ReceiveAsync(memory, cancellationToken); int bytesRead = receiveresult.Count; if (bytesRead == 0) @@ -144,33 +142,30 @@ namespace Emby.Server.Implementations.HttpServer } } while (_socket.State == WebSocketState.Open && receiveresult.MessageType != WebSocketMessageType.Close); - if (_socket.State == WebSocketState.Open) - { - _logger.LogWarning("Stopped reading from websocket before it was closed"); - } - Closed?.Invoke(this, EventArgs.Empty); - _socket.Dispose(); + await _socket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + string.Empty, + cancellationToken).ConfigureAwait(false); } private async Task ProcessInternal(PipeReader reader) { + ReadResult result = await reader.ReadAsync().ConfigureAwait(false); + ReadOnlySequence buffer = result.Buffer; + if (OnReceive == null) { + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); return; } + WebSocketMessage stub; try { - var result = await reader.ReadAsync().ConfigureAwait(false); - if (!result.IsCompleted) - { - return; - } - WebSocketMessage stub; - var buffer = result.Buffer; if (buffer.IsSingleSegment) { stub = JsonSerializer.Deserialize>(buffer.FirstSpan, _jsonOptions); @@ -188,46 +183,36 @@ namespace Emby.Server.Implementations.HttpServer ArrayPool.Shared.Return(buf); } } - - var info = new WebSocketMessageInfo - { - MessageType = stub.MessageType, - Data = stub.Data.ToString(), - Connection = this - }; - - await OnReceive(info).ConfigureAwait(false); } 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"); - } - } - - /// - 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) - { - _socket.Dispose(); - } + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); - _disposed = true; + _logger.LogDebug("WS received message: {@Message}", stub); + + var info = new WebSocketMessageInfo + { + MessageType = stub.MessageType, + Data = stub.Data?.ToString(), // Data can be null + Connection = this + }; + + _logger.LogDebug("WS message info: {@MessageInfo}", info); + + await OnReceive(info).ConfigureAwait(false); + + // Stop reading if there's no more data coming + if (result.IsCompleted) + { + return; + } } } } diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index 536013c7ad..c3c4b716fc 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -57,7 +57,6 @@ namespace Emby.Server.Implementations.Session _logger.LogDebug("Removing websocket from session {Session}", _session.Id); _sockets.Remove(connection); connection.Closed -= OnConnectionClosed; - connection.Dispose(); _sessionManager.CloseIfNeeded(_session); } @@ -96,7 +95,6 @@ namespace Emby.Server.Implementations.Session foreach (var socket in _sockets) { socket.Closed -= OnConnectionClosed; - socket.Dispose(); } _disposed = true; diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index d5555884df..09e43c683f 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { - public interface IWebSocketConnection : IDisposable + public interface IWebSocketConnection { /// /// Occurs when [closed]. From 8865b3ea3d0af201c37aa129016b843f0b9fe686 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 27 Dec 2019 15:20:27 +0100 Subject: [PATCH 004/137] Remove dead code and improve logging --- .../HttpServer/HttpListenerHost.cs | 8 +- .../HttpServer/WebSocketConnection.cs | 4 +- .../Middleware/WebSocketMiddleware.cs | 39 ------- .../WebSockets/WebSocketHandler.cs | 10 -- .../WebSockets/WebSocketManager.cs | 104 ------------------ .../System/ActivityLogWebSocketListener.cs | 17 ++- .../Net/BasePeriodicWebSocketListener.cs | 11 +- 7 files changed, 18 insertions(+), 175 deletions(-) delete mode 100644 Emby.Server.Implementations/Middleware/WebSocketMiddleware.cs delete mode 100644 Emby.Server.Implementations/WebSockets/WebSocketHandler.cs delete mode 100644 Emby.Server.Implementations/WebSockets/WebSocketManager.cs diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index ebae4d0b1e..3cdb0ecaeb 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -520,7 +520,7 @@ namespace Emby.Server.Implementations.HttpServer try { - _logger.LogInformation("WS Request from {IP}", context.Connection.RemoteIpAddress); + _logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); @@ -536,11 +536,11 @@ namespace Emby.Server.Implementations.HttpServer WebSocketConnected?.Invoke(this, new GenericEventArgs(connection)); await connection.ProcessAsync().ConfigureAwait(false); - _logger.LogInformation("WS closed from {IP}", context.Connection.RemoteIpAddress); + _logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress); } catch (Exception ex) // Otherwise ASP.Net will ignore the exception { - _logger.LogError(ex, "WebSocketRequestHandler error"); + _logger.LogError(ex, "WS {IP} WebSocketRequestHandler error"); if (!context.Response.HasStarted) { context.Response.StatusCode = 500; @@ -705,8 +705,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/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 913a51217f..0afd0ecce1 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.HttpServer // Tell the PipeReader how much of the buffer we have consumed reader.AdvanceTo(buffer.End); - _logger.LogDebug("WS received message: {@Message}", stub); + _logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub); var info = new WebSocketMessageInfo { @@ -204,7 +204,7 @@ namespace Emby.Server.Implementations.HttpServer Connection = this }; - _logger.LogDebug("WS message info: {@MessageInfo}", info); + _logger.LogDebug("WS {IP} message info: {@MessageInfo}", RemoteEndPoint, info); await OnReceive(info).ConfigureAwait(false); 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/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 efd97e4ff1..0000000000 --- a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Net; -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/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index a036619b81..60b190a0e7 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Threading; +using System; using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; @@ -11,7 +10,7 @@ namespace MediaBrowser.Api.System /// /// Class SessionInfoWebSocketListener /// - public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> + public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener { /// /// Gets the name. @@ -27,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); } @@ -39,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.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 9d71426d88..b193cbb55a 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 { @@ -196,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(); @@ -241,6 +239,7 @@ namespace MediaBrowser.Controller.Net public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } } From bdd823d22ff4d20e8aa2e5d8bf34e0faaad285ba Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 27 Dec 2019 15:42:59 +0100 Subject: [PATCH 005/137] Handle unexpected disconnect --- .../HttpServer/HttpListenerHost.cs | 2 +- .../HttpServer/WebSocketConnection.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 3cdb0ecaeb..05dbad624b 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -540,7 +540,7 @@ namespace Emby.Server.Implementations.HttpServer } catch (Exception ex) // Otherwise ASP.Net will ignore the exception { - _logger.LogError(ex, "WS {IP} WebSocketRequestHandler error"); + _logger.LogError(ex, "WS {IP} WebSocketRequestHandler error", context.Connection.RemoteIpAddress); if (!context.Response.HasStarted) { context.Response.StatusCode = 500; diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 0afd0ecce1..7c0d82d899 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -116,7 +116,16 @@ namespace Emby.Server.Implementations.HttpServer { // Allocate at least 512 bytes from the PipeWriter Memory memory = writer.GetMemory(512); - receiveresult = await _socket.ReceiveAsync(memory, cancellationToken); + 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) { From f89e18ea26aa2f4eec19f52ee6dfd28b53cee5df Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 27 Dec 2019 15:56:20 +0100 Subject: [PATCH 006/137] Improve error handling --- .../HttpServer/WebSocketConnection.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 7c0d82d899..0b376bf3c2 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -149,14 +149,21 @@ namespace Emby.Server.Implementations.HttpServer { await ProcessInternal(pipe.Reader).ConfigureAwait(false); } - } while (_socket.State == WebSocketState.Open && receiveresult.MessageType != WebSocketMessageType.Close); + } while ( + (_socket.State == WebSocketState.Open || _socket.State == WebSocketState.Connecting) + && receiveresult.MessageType != WebSocketMessageType.Close); Closed?.Invoke(this, EventArgs.Empty); - await _socket.CloseAsync( - WebSocketCloseStatus.NormalClosure, - string.Empty, - cancellationToken).ConfigureAwait(false); + if (_socket.State == WebSocketState.Open + || _socket.State == WebSocketState.CloseReceived + || _socket.State == WebSocketState.CloseSent) + { + await _socket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + string.Empty, + cancellationToken).ConfigureAwait(false); + } } private async Task ProcessInternal(PipeReader reader) From d01ba49be3cd643b7b306216cb96aef31dba9569 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 29 Dec 2019 14:53:04 +0100 Subject: [PATCH 007/137] Fix space --- Emby.Server.Implementations/HttpServer/WebSocketConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 0b376bf3c2..a8d5e9086a 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -211,7 +211,7 @@ namespace Emby.Server.Implementations.HttpServer // Tell the PipeReader how much of the buffer we have consumed reader.AdvanceTo(buffer.End); - _logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub); + _logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub); var info = new WebSocketMessageInfo { From ee964f8a58a0324b9e7b2ae37a9d4831f59c922f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 29 Dec 2019 15:44:17 +0100 Subject: [PATCH 008/137] Don't log message info --- Emby.Server.Implementations/HttpServer/WebSocketConnection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index a8d5e9086a..1af748ebc2 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -220,8 +220,6 @@ namespace Emby.Server.Implementations.HttpServer Connection = this }; - _logger.LogDebug("WS {IP} message info: {@MessageInfo}", RemoteEndPoint, info); - await OnReceive(info).ConfigureAwait(false); // Stop reading if there's no more data coming From 407f54e7764a6bfd8e4ccc0df897fac7e8c658b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 13 Jan 2020 20:03:49 +0100 Subject: [PATCH 009/137] Style fixes --- .../Session/WebSocketController.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index c3c4b716fc..c7ef9b1cec 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; @@ -42,7 +46,6 @@ namespace Emby.Server.Implementations.Session private IEnumerable GetActiveSockets() => _sockets.Where(i => i.State == WebSocketState.Open); - /// public void AddWebSocket(IWebSocketConnection connection) { _logger.LogDebug("Adding websocket to session {Session}", _session.Id); @@ -76,12 +79,14 @@ 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); } /// From 5d760b7ee806d3fb00ac5aa7d0981362526f1d11 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Sun, 1 Mar 2020 21:38:34 +0100 Subject: [PATCH 010/137] Fix emby/user/public API leaking private data This commit fixes the emby/user/public API that was returning more data than necessary. Now only the following information are returned: - the account name - the primary image tag - the field hasPassword - the field hasConfiguredPassword, useful for the first wizard only (see https://github.com/jellyfin/jellyfin/issues/880#issuecomment-465370051) - the primary image aspect ratio A new DTO class, PrivateUserDTO has been created, and the route has been modified in order to return that data object. --- .../Library/UserManager.cs | 25 ++++++++++ MediaBrowser.Api/UserService.cs | 36 +++++++++----- .../Library/IUserManager.cs | 8 ++++ MediaBrowser.Model/Dto/PublicUserDto.cs | 48 +++++++++++++++++++ 4 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 MediaBrowser.Model/Dto/PublicUserDto.cs diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 6e203f894f..8941767b41 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -613,6 +613,31 @@ namespace Emby.Server.Implementations.Library return dto; } + public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); + bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).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/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 4015143497..b4ab8c9740 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,36 @@ 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 users = _userManager + .Users + .Where(item => item.Policy.IsDisabled == false) + .Where(item => item.Policy.IsHidden == false); + + var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; + + if (!string.IsNullOrWhiteSpace(deviceId)) { - return Get(new GetUsers - { - IsDisabled = false - }); + users = users.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); } - return Get(new GetUsers + if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) { - IsHidden = false, - IsDisabled = false - }, true, true); + users = users.Where(i => i.Policy.EnableRemoteAccess); + } + + var result = users + .OrderBy(u => u.Name) + .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp)) + .ToArray(); + + return ToOptimizedResult(result); } /// 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.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs new file mode 100644 index 0000000000..bf529a2d0b --- /dev/null +++ b/MediaBrowser.Model/Dto/PublicUserDto.cs @@ -0,0 +1,48 @@ +using System; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Users; + +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. + /// + /// 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(); + } + } +} \ No newline at end of file From 737d4d2b3f200e2dc140151d11dc9f29d70bd5bf Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Tue, 3 Mar 2020 19:51:03 +0100 Subject: [PATCH 011/137] Fix conditional with a less verbose one Co-Authored-By: Vasily --- MediaBrowser.Api/UserService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index b4ab8c9740..5ad4fce64d 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -275,8 +275,8 @@ namespace MediaBrowser.Api { var users = _userManager .Users - .Where(item => item.Policy.IsDisabled == false) - .Where(item => item.Policy.IsHidden == false); + .Where(item => !item.Policy.IsDisabled) + .Where(item => !item.Policy.IsHidden); var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; From cd471ed4df9260d9de3cfa1e58abf537aa460727 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Tue, 3 Mar 2020 20:20:35 +0100 Subject: [PATCH 012/137] Fix emby/users/public not taking into account first run The previous implementation was not taking in account the first seup phase. Now the check has been added. A little method refactor has been done in order to make the code more elegant. --- MediaBrowser.Api/UserService.cs | 38 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 5ad4fce64d..bb630c0b37 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -273,29 +273,31 @@ namespace MediaBrowser.Api /// System.Object. public object Get(GetPublicUsers request) { - var users = _userManager + var result = _userManager .Users - .Where(item => !item.Policy.IsDisabled) - .Where(item => !item.Policy.IsHidden); + .Where(item => !item.Policy.IsDisabled); - var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; - - if (!string.IsNullOrWhiteSpace(deviceId)) + if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) { - users = users.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); + var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; + result = result.Where(item => !item.Policy.IsHidden); + + if (!string.IsNullOrWhiteSpace(deviceId)) + { + result = result.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); + } + + if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + result = result.Where(i => i.Policy.EnableRemoteAccess); + } } - if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) - { - users = users.Where(i => i.Policy.EnableRemoteAccess); - } - - var result = users - .OrderBy(u => u.Name) - .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp)) - .ToArray(); - - return ToOptimizedResult(result); + return ToOptimizedResult(result + .OrderBy(u => u.Name) + .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp)) + .ToArray() + ); } /// From 5099f6e4a24128e47edbad674f551c5ebe29cb7e Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Thu, 5 Mar 2020 08:01:47 +0100 Subject: [PATCH 013/137] Add FIXME in HasConfiguredPassword public user DTO method --- MediaBrowser.Model/Dto/PublicUserDto.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs index bf529a2d0b..d5fd431eb6 100644 --- a/MediaBrowser.Model/Dto/PublicUserDto.cs +++ b/MediaBrowser.Model/Dto/PublicUserDto.cs @@ -31,6 +31,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets a value indicating whether this instance has configured password. /// /// true if this instance has configured password; otherwise, false. + // FIXME this shouldn't be here, but it's necessary when changing password at the first login public bool HasConfiguredPassword { get; set; } /// From ca71ac72abf5d5ff31d50553283a43de298e0c73 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 2 Apr 2020 17:45:04 -0400 Subject: [PATCH 014/137] Replace EnableHttps and SupportsHttps with ListenWithHttps and CanConnectWithHttps --- Emby.Server.Implementations/ApplicationHost.cs | 15 ++++++++++----- .../EntryPoints/ExternalPortForwarding.cs | 2 +- .../HttpServer/HttpListenerHost.cs | 2 +- Jellyfin.Server/Program.cs | 4 ++-- MediaBrowser.Controller/IServerApplicationHost.cs | 5 ++--- MediaBrowser.Model/System/SystemInfo.cs | 4 ++-- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c959cc974a..8158b45592 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1408,7 +1408,7 @@ namespace Emby.Server.Implementations InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, HttpServerPortNumber = HttpPort, - SupportsHttps = SupportsHttps, + SupportsHttps = CanConnectWithHttps, HttpsPortNumber = HttpsPort, OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystemDisplayName = OperatingSystem.Name, @@ -1446,9 +1446,14 @@ 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; + /// + /// Gets a value indicating whether a client can connect to the server over HTTPS, either directly or via a + /// reverse proxy. + /// + public bool CanConnectWithHttps => ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy; public async Task GetLocalApiUrl(CancellationToken cancellationToken) { @@ -1509,10 +1514,10 @@ namespace Emby.Server.Implementations public string GetLocalApiUrl(ReadOnlySpan host) { var url = new StringBuilder(64); - url.Append(EnableHttps ? "https://" : "http://") + url.Append(ListenWithHttps ? "https://" : "http://") .Append(host) .Append(':') - .Append(EnableHttps ? HttpsPort : HttpPort); + .Append(ListenWithHttps ? HttpsPort : HttpPort); string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; if (baseUrl.Length != 0) diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index e290c62e16..2023c470a8 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.EntryPoints .Append(config.PublicPort).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(); } diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 7a812f3201..e3f8ec0146 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.HttpServer private bool ValidateSsl(string remoteIp, string urlString) { - if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy) + if (_config.Configuration.RequireHttps && _appHost.ListenWithHttps) { if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1) { diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 4abdd59aaf..ddd1054ad0 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -274,7 +274,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,7 +289,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 => { diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 608ffc61c2..d999f76dbf 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. diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index cfa7684c91..83c60563e7 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -124,9 +124,9 @@ namespace MediaBrowser.Model.System public int HttpServerPortNumber { get; set; } /// - /// Gets or sets a value indicating whether [enable HTTPS]. + /// Gets or sets a value indicating whether a client can connect to the server over HTTPS, either directly or + /// via a reverse proxy. /// - /// true if [enable HTTPS]; otherwise, false. public bool SupportsHttps { get; set; } /// From 387fa474aa8ee8e237648ab0ea3130b8b35cf92f Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 2 Apr 2020 17:45:33 -0400 Subject: [PATCH 015/137] Document HTTPS configuration options --- .../HttpServer/HttpListenerHost.cs | 4 +++ .../Configuration/ServerConfiguration.cs | 35 ++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index e3f8ec0146..dc542af784 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -420,6 +420,10 @@ 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.ListenWithHttps) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 3107ec2426..b14b347ccc 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -45,17 +45,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; } /// @@ -65,8 +72,11 @@ 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; } /// @@ -82,6 +92,7 @@ namespace MediaBrowser.Model.Configuration /// /// The metadata path. public string MetadataPath { get; set; } + public string MetadataNetworkPath { get; set; } /// @@ -204,15 +215,31 @@ 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; } + + /// + /// Gets or sets a value indicating whether the server is behind a reverse proxy. + /// public bool IsBehindProxy { get; set; } + public bool EnableNewOmdbSupport { get; set; } public string[] RemoteIPFilter { get; set; } From 410a322fe22302eb3c8a37ea38bbe1d7e9d12aff Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 5 Apr 2020 23:30:57 -0400 Subject: [PATCH 016/137] Add CanConnectWithHttps to interface --- Emby.Server.Implementations/ApplicationHost.cs | 5 +---- MediaBrowser.Controller/IServerApplicationHost.cs | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8158b45592..9cb7471717 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1449,10 +1449,7 @@ namespace Emby.Server.Implementations /// public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; - /// - /// Gets a value indicating whether a client can connect to the server over HTTPS, either directly or via a - /// reverse proxy. - /// + /// public bool CanConnectWithHttps => ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy; public async Task GetLocalApiUrl(CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index d999f76dbf..7742279f7a 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -43,6 +43,12 @@ namespace MediaBrowser.Controller /// bool ListenWithHttps { get; } + /// + /// Gets a value indicating whether a client can connect to the server over HTTPS, either directly or via a + /// reverse proxy. + /// + bool CanConnectWithHttps { get; } + /// /// Gets a value indicating whether this instance has update available. /// From 38dae51ccf93e3b3a266007a9aad1ab980d0bb05 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Apr 2020 19:36:28 +0200 Subject: [PATCH 017/137] Minor IAsyncDisposable improvements --- MediaBrowser.Api/Images/RemoteImageService.cs | 13 ++++++---- MediaBrowser.Api/ItemLookupService.cs | 14 ++++++---- .../Playback/Hls/BaseHlsService.cs | 26 +++++++++++-------- 3 files changed, 32 insertions(+), 21 deletions(-) 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/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 52962366c6..4213193bac 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -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) From fee76097f44d0f11356e4a559df9178063b1bf29 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 16 Apr 2020 21:45:00 -0400 Subject: [PATCH 018/137] Remove CanConnectWithHttps Property It is only used in one place and only adds confusion by existing --- Emby.Server.Implementations/ApplicationHost.cs | 4 +--- MediaBrowser.Controller/IServerApplicationHost.cs | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6a324e0904..a39baa84ab 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1413,7 +1413,7 @@ namespace Emby.Server.Implementations InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, HttpServerPortNumber = HttpPort, - SupportsHttps = CanConnectWithHttps, + SupportsHttps = ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy, HttpsPortNumber = HttpsPort, OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystemDisplayName = OperatingSystem.Name, @@ -1455,8 +1455,6 @@ namespace Emby.Server.Implementations public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps; /// - public bool CanConnectWithHttps => ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy; - public async Task GetLocalApiUrl(CancellationToken cancellationToken) { try diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 7742279f7a..d999f76dbf 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -43,12 +43,6 @@ namespace MediaBrowser.Controller /// bool ListenWithHttps { get; } - /// - /// Gets a value indicating whether a client can connect to the server over HTTPS, either directly or via a - /// reverse proxy. - /// - bool CanConnectWithHttps { get; } - /// /// Gets a value indicating whether this instance has update available. /// From 00a0e013c695a3741218985afadd31265a6ddb40 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 16 Apr 2020 21:46:49 -0400 Subject: [PATCH 019/137] Update documentation for URL methods in ApplicationHost --- .../ApplicationHost.cs | 6 ++--- .../IServerApplicationHost.cs | 24 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index a39baa84ab..7699faf14e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1510,7 +1510,7 @@ namespace Emby.Server.Implementations return GetLocalApiUrl(ipAddress.ToString()); } - /// + /// public string GetLocalApiUrl(ReadOnlySpan host) { var url = new StringBuilder(64); @@ -1559,7 +1559,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); @@ -1593,7 +1593,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)) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index d999f76dbf..8537e41800 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -56,29 +56,33 @@ 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 . /// - /// The local API URL. + /// 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 local (LAN) URL that can be used to access the API. /// - /// The hostname. - /// The local API URL. + /// The hostname to use in the URL. + /// The API URL. string GetLocalApiUrl(ReadOnlySpan hostname); /// - /// Gets the local API URL. + /// Gets a local (LAN) URL that can be used to access the API. /// - /// The IP address. - /// The local API URL. + /// The IP address to use as the hostname in the URL. + /// The API URL. string GetLocalApiUrl(IPAddress address); /// From 1666f3ca148c38609c85cb81e1e8b69e3473e319 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 16 Apr 2020 23:40:32 -0400 Subject: [PATCH 020/137] Use dependency injection to construct migration routines --- .../Migrations/IMigrationRoutine.cs | 4 +--- Jellyfin.Server/Migrations/MigrationRunner.cs | 19 ++++++++++++------- .../Routines/CreateUserLoggingConfigFile.cs | 11 +++++++++-- .../Routines/DisableTranscodingThrottling.cs | 17 +++++++++++++---- 4 files changed, 35 insertions(+), 16 deletions(-) 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..ca17482820 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,10 @@ 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) }; /// @@ -27,6 +28,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 +39,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 +59,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); } } } From 3cf2fce983fafbe0efbf8fc21ac267186600837d Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 13:59:02 -0400 Subject: [PATCH 021/137] Handle null values for RemoteIpAddress and LocalIpAddress in websocket requests These values are null when creating fake web requests as part of integration tests --- .../SocketSharp/WebSocketSharpRequest.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 1781df8b51..f27f7eeb86 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -62,6 +62,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; } } @@ -89,7 +92,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; From 51b610b999035fac571d0faf24e5cd558ecaacb5 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 14:58:00 -0400 Subject: [PATCH 022/137] Expose some methods in Program.cs so they can be used to initialize the application for integration tests --- Jellyfin.Server/Program.cs | 71 ++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index e55b0d4ed9..eb19f57a96 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -161,23 +161,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, @@ -206,7 +190,7 @@ namespace Jellyfin.Server ServiceCollection serviceCollection = new ServiceCollection(); await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false); - 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; @@ -252,14 +236,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 @@ -492,7 +511,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); @@ -512,7 +533,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) From 307754a0e0a88ac331c1e8bb98e46ce51d004fd6 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 15:39:55 -0400 Subject: [PATCH 023/137] Create a derived version of WebApplicationFactory<> that works with the Jellyfin server --- MediaBrowser.sln | 4 +- .../JellyfinApplicationFactory.cs | 124 ++++++++++++++++++ .../MediaBrowser.Api.Tests.csproj | 22 ++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs create mode 100644 tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1c84622ac0..daac9b0a2e 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 @@ -62,6 +62,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementat EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", "tests\Jellyfin.Controller.Tests\Jellyfin.Controller.Tests.csproj", "{462584F7-5023-4019-9EAC-B98CA458C0A0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api.Tests", "tests\MediaBrowser.Api.Tests\MediaBrowser.Api.Tests.csproj", "{7C93C84F-105C-48E5-A878-406FA0A5B296}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs new file mode 100644 index 0000000000..0bd9909f5b --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +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.Hosting; +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 _appHosts = new ConcurrentBag(); + + /// + /// Initializes a new instance of . + /// + 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).Wait(); + + // Create a copy of the application configuration to use for startup + var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); + + // Create the app host and initialize it + ILoggerFactory loggerFactory = new SerilogLoggerFactory(); + var appHost = new CoreAppHost( + appPaths, + loggerFactory, + commandLineOpts, + new ManagedFileSystem(loggerFactory.CreateLogger(), appPaths), + new SkiaEncoder(loggerFactory.CreateLogger(), appPaths), + new NetworkManager(loggerFactory.CreateLogger())); + _appHosts.Add(appHost); + var serviceCollection = new ServiceCollection(); + appHost.InitAsync(serviceCollection, startupConfig).Wait(); + + // 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(); + appHost.FindParts(); + appHost.RunStartupTasksAsync().Wait(); + + return testServer; + } + + /// + protected override void Dispose(bool disposing) + { + foreach (var host in _appHosts) + { + host.Dispose(); + } + + _appHosts.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..313cf9a3d7 --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + false + true + enable + + + + + + + + + + + + + + + From e43e6af405dcc8cd5cff71ca39e671de7dba5ce6 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 15:47:36 -0400 Subject: [PATCH 024/137] Create integration tests for the endpoints in BrandingService --- .../BrandingServiceTests.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs diff --git a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs new file mode 100644 index 0000000000..881617914a --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using Jellyfin.Server; +using MediaBrowser.Model.Branding; +using Microsoft.AspNetCore.Mvc.Testing; +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()); + } + } +} From 2bcc553dda156bf9bc358517198cfeebfae60a75 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 15:54:54 -0400 Subject: [PATCH 025/137] Commit missing changes to solution file --- MediaBrowser.sln | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index daac9b0a2e..60571217fc 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -46,23 +46,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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}" 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}") = "MediaBrowser.Api.Tests", "tests\MediaBrowser.Api.Tests\MediaBrowser.Api.Tests.csproj", "{7C93C84F-105C-48E5-A878-406FA0A5B296}" +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 +114,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 @@ -178,6 +174,10 @@ Global {462584F7-5023-4019-9EAC-B98CA458C0A0}.Debug|Any CPU.Build.0 = Debug|Any CPU {462584F7-5023-4019-9EAC-B98CA458C0A0}.Release|Any CPU.ActiveCfg = Release|Any CPU {462584F7-5023-4019-9EAC-B98CA458C0A0}.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 @@ -210,5 +210,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 From 6612d9555f45cd0a84cbc3e2506263533c8eab4a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 16:01:33 -0400 Subject: [PATCH 026/137] Ignore lauchSettings.json in test projects This file is generated every time the solution is opened with Visual Studio but it is not a required file for an integration tests project --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 523c45a7e6..5ce0145dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ CorePlugins*/ ProgramData-Server*/ ProgramData-UI*/ MediaBrowser.WebDashboard/jellyfin-web/** +tests/**/launchSettings.json ################# ## Visual Studio From 24ed6397250df484b71ebfb3b428d31e4d05f510 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 20 Apr 2020 16:23:57 -0400 Subject: [PATCH 027/137] Fix NuGet dependencies --- tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj index 313cf9a3d7..077fefed0b 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -9,9 +9,10 @@ - + + From 974a4e79f647356caf6e497852fe799ade403930 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 Apr 2020 10:33:41 +0200 Subject: [PATCH 028/137] Enable TreatWarningsAsErrors for DvdLib --- DvdLib/BigEndianBinaryReader.cs | 2 ++ DvdLib/DvdLib.csproj | 1 + DvdLib/Ifo/Cell.cs | 2 ++ DvdLib/Ifo/CellPlaybackInfo.cs | 2 ++ DvdLib/Ifo/CellPositionInfo.cs | 2 ++ DvdLib/Ifo/Chapter.cs | 2 ++ DvdLib/Ifo/Dvd.cs | 2 ++ DvdLib/Ifo/DvdTime.cs | 2 ++ DvdLib/Ifo/Program.cs | 2 ++ DvdLib/Ifo/ProgramChain.cs | 2 ++ DvdLib/Ifo/Title.cs | 2 ++ DvdLib/Ifo/UserOperation.cs | 2 ++ 12 files changed, 23 insertions(+) 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 36f949616a..4b357c90b0 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -8,6 +8,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 From 735e7c3f7d505b8e00359a8f9498cde7906a0b83 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 Apr 2020 12:11:55 +0200 Subject: [PATCH 029/137] Fix VideoResolver and tests --- Emby.Naming/Video/VideoResolver.cs | 4 +- .../ConfigurationOptions.cs | 1 - .../Video/CleanDateTimeTests.cs | 1 + .../Jellyfin.Naming.Tests/Video/ExtraTests.cs | 2 +- .../Video/Format3DTests.cs | 59 ++- .../Video/MultiVersionTests.cs | 5 +- .../Jellyfin.Naming.Tests/Video/StackTests.cs | 6 +- .../Jellyfin.Naming.Tests/Video/StubTests.cs | 10 +- .../Video/VideoListResolverTests.cs | 4 +- .../Video/VideoResolverTests.cs | 449 ++++++++---------- 10 files changed, 236 insertions(+), 305 deletions(-) diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 0b75a8cce9..a26c4bbc65 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)) { name = newName.ToString(); } - - year = cleanDateTimeResult.Year; } return new VideoFileInfo 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/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..82004081ee 100644 --- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -4,26 +4,26 @@ 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); @@ -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..03fe32b6e1 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -8,6 +8,8 @@ namespace Jellyfin.Naming.Tests.Video { public class MultiVersionTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); + // FIXME // [Fact] public void TestMultiEdition1() @@ -430,8 +432,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..3630a07e46 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() { @@ -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..e31d97e2e3 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); } 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..566dc9f7c1 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -8,6 +8,7 @@ namespace Jellyfin.Naming.Tests.Video { public class VideoListResolverTests { + private readonly NamingOptions _namingOptions = new NamingOptions(); // FIXME // [Fact] public void TestStackAndExtras() @@ -450,8 +451,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..7bfe90f674 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -1,275 +1,204 @@ -using MediaBrowser.Model.Entities; +using System.Collections; +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 { - // FIXME - // [Fact] - public void TestSimpleFile() + private readonly NamingOptions _namingOptions = new NamingOptions(); + + private class ResolveFileTestData : IEnumerable { - var parser = GetParser(); + public IEnumerator GetEnumerator() + { + 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, + } + }; + } - 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); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - // FIXME - // [Fact] - public void TestSimpleFile2() + [Theory] + [ClassData(typeof(ResolveFileTestData))] + 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.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); } } } From 851dda097ef5486f1da1ccf837f8f028fd5e7c95 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 Apr 2020 12:54:04 +0200 Subject: [PATCH 030/137] Minor improvement --- .../Video/VideoResolverTests.cs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index 7bfe90f674..e16646fa52 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -11,11 +11,11 @@ namespace Jellyfin.Naming.Tests.Video { private readonly NamingOptions _namingOptions = new NamingOptions(); - private class ResolveFileTestData : IEnumerable + private class ResolveFileTestData : IEnumerable { - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -24,7 +24,7 @@ namespace Jellyfin.Naming.Tests.Video Name = "7 Psychos" } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -34,7 +34,7 @@ namespace Jellyfin.Naming.Tests.Video Year = 2005 } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -43,7 +43,7 @@ namespace Jellyfin.Naming.Tests.Video Name = "American.Psycho", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -55,7 +55,7 @@ namespace Jellyfin.Naming.Tests.Video Format3D = "sbs", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -65,7 +65,7 @@ namespace Jellyfin.Naming.Tests.Video Year = 2006 } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -77,7 +77,7 @@ namespace Jellyfin.Naming.Tests.Video Format3D = "sbs", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -89,7 +89,7 @@ namespace Jellyfin.Naming.Tests.Video StubType = "bluray", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -101,7 +101,7 @@ namespace Jellyfin.Naming.Tests.Video StubType = "bluray", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -113,7 +113,7 @@ namespace Jellyfin.Naming.Tests.Video StubType = "bluray", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -125,7 +125,7 @@ namespace Jellyfin.Naming.Tests.Video StubType = "bluray", } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -136,7 +136,7 @@ namespace Jellyfin.Naming.Tests.Video ExtraType = ExtraType.Trailer, } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -147,7 +147,7 @@ namespace Jellyfin.Naming.Tests.Video ExtraType = ExtraType.Trailer, } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -157,7 +157,7 @@ namespace Jellyfin.Naming.Tests.Video Year = 2006 } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -167,7 +167,7 @@ namespace Jellyfin.Naming.Tests.Video Year = 1995, } }; - yield return new object?[] + yield return new object[] { new VideoFileInfo() { @@ -184,10 +184,11 @@ namespace Jellyfin.Naming.Tests.Video [Theory] [ClassData(typeof(ResolveFileTestData))] - public void ResolveFile_ValidFileName_Success(VideoFileInfo? expectedResult) + public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path); + Assert.NotNull(result); Assert.Equal(result.Path, expectedResult.Path); Assert.Equal(result.Container, expectedResult.Container); Assert.Equal(result.Name, expectedResult.Name); From d1684b1053c9cfedc81e44ff698c5267ee88a0ef Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 23 Apr 2020 18:30:48 +0100 Subject: [PATCH 031/137] http in development mode crashed - --- Jellyfin.Server/Program.cs | 55 +++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 193d30e3a7..5bff1db622 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -272,22 +272,24 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); - - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.EnableHttps) { - options.Listen(address, appHost.HttpsPort, listenOptions => + if (appHost.Certificate != null) { - listenOptions.UseHttps(appHost.Certificate); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - else if (builderContext.HostingEnvironment.IsDevelopment()) - { - options.Listen(address, appHost.HttpsPort, listenOptions => + options.Listen(address, appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(appHost.Certificate); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + else if (builderContext.HostingEnvironment.IsDevelopment()) { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.Listen(address, appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } } } } @@ -296,21 +298,24 @@ namespace Jellyfin.Server _logger.LogInformation("Kestrel listening on all interfaces"); options.ListenAnyIP(appHost.HttpPort); - if (appHost.EnableHttps && appHost.Certificate != null) + if (appHost.EnableHttps) { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => + if (appHost.Certificate != null) { - listenOptions.UseHttps(appHost.Certificate); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - else if (builderContext.HostingEnvironment.IsDevelopment()) - { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => + options.ListenAnyIP(appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(appHost.Certificate); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + else if (builderContext.HostingEnvironment.IsDevelopment()) { - listenOptions.UseHttps(); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); + options.ListenAnyIP(appHost.HttpsPort, listenOptions => + { + listenOptions.UseHttps(); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } } } }) From 6f20e2482f42d1224a8eb11d30d4eeff2e140c94 Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 23 Apr 2020 12:43:05 -0600 Subject: [PATCH 032/137] Fix build --- tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs | 3 --- tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs | 5 ----- tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs index 881617914a..34698fe251 100644 --- a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs +++ b/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs @@ -1,9 +1,6 @@ -using System; using System.Text.Json; using System.Threading.Tasks; -using Jellyfin.Server; using MediaBrowser.Model.Branding; -using Microsoft.AspNetCore.Mvc.Testing; using Xunit; namespace MediaBrowser.Api.Tests diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs index 0bd9909f5b..4a01180bc3 100644 --- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -1,9 +1,5 @@ -using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Threading.Tasks; using Emby.Server.Implementations; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Networking; @@ -14,7 +10,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Serilog; using Serilog.Extensions.Logging; diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj index 077fefed0b..b99d61f5a8 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 From a273ed9a574a342c1a40d42d820268ddf2fc2d99 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 25 Apr 2020 15:29:59 +0200 Subject: [PATCH 033/137] Address comments --- Emby.Naming/Video/VideoResolver.cs | 2 +- .../Video/VideoResolverTests.cs | 305 +++++++++--------- 2 files changed, 151 insertions(+), 156 deletions(-) diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index a26c4bbc65..b4aee614b0 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -93,7 +93,7 @@ namespace Emby.Naming.Video year = cleanDateTimeResult.Year; if (extraResult.ExtraType == null - && TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan newName)) + && TryCleanString(name, out ReadOnlySpan newName)) { name = newName.ToString(); } diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index e16646fa52..709d893684 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Model.Entities; @@ -11,179 +10,175 @@ namespace Jellyfin.Naming.Tests.Video { private readonly NamingOptions _namingOptions = new NamingOptions(); - private class ResolveFileTestData : IEnumerable + public static IEnumerable GetResolveFileTestData() { - public IEnumerator GetEnumerator() + yield return new object[] { - yield return new object[] + new VideoFileInfo() { - new VideoFileInfo() - { - Path = @"/server/Movies/7 Psychos.mkv/7 Psychos.mkv", - Container = "mkv", - Name = "7 Psychos" - } - }; - yield return new object[] + Path = @"/server/Movies/7 Psychos.mkv/7 Psychos.mkv", + Container = "mkv", + Name = "7 Psychos" + } + }; + yield return new object[] + { + new VideoFileInfo() { - 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[] + 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() { - new VideoFileInfo() - { - Path = @"/server/Movies/American Psycho/American.Psycho.mkv", - Container = "mkv", - Name = "American.Psycho", - } - }; - yield return new object[] + Path = @"/server/Movies/American Psycho/American.Psycho.mkv", + Container = "mkv", + Name = "American.Psycho", + } + }; + yield return new object[] + { + new VideoFileInfo() { - 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[] + 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() { - new VideoFileInfo() - { - Path = @"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv", - Container = "mkv", - Name = "300", - Year = 2006 - } - }; - yield return new object[] + Path = @"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv", + Container = "mkv", + Name = "300", + Year = 2006 + } + }; + yield return new object[] + { + new VideoFileInfo() { - 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[] + 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() { - 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[] + 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() { - 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[] + 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() { - 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[] + 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() { - 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[] + 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() { - new VideoFileInfo() - { - Path = @"/server/Movies/300 (2007)/300 (2006)-trailer.mkv", - Container = "mkv", - Name = "300", - Year = 2006, - ExtraType = ExtraType.Trailer, - } - }; - yield return new object[] + Path = @"/server/Movies/300 (2007)/300 (2006)-trailer.mkv", + Container = "mkv", + Name = "300", + Year = 2006, + ExtraType = ExtraType.Trailer, + } + }; + yield return new object[] + { + new VideoFileInfo() { - new VideoFileInfo() - { - Path = @"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv", - Container = "mkv", - Name = "Brave", - Year = 2006, - ExtraType = ExtraType.Trailer, - } - }; - yield return new object[] + Path = @"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv", + Container = "mkv", + Name = "Brave", + Year = 2006, + ExtraType = ExtraType.Trailer, + } + }; + yield return new object[] + { + new VideoFileInfo() { - new VideoFileInfo() - { - Path = @"/server/Movies/300 (2007)/300 (2006).mkv", - Container = "mkv", - Name = "300", - Year = 2006 - } - }; - yield return new object[] + Path = @"/server/Movies/300 (2007)/300 (2006).mkv", + Container = "mkv", + Name = "300", + Year = 2006 + } + }; + yield return new object[] + { + new VideoFileInfo() { - new VideoFileInfo() - { - Path = @"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv", - Container = "mkv", - Name = "Bad Boys", - Year = 1995, - } - }; - yield return new object[] + Path = @"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv", + Container = "mkv", + Name = "Bad Boys", + Year = 1995, + } + }; + yield return new object[] + { + new VideoFileInfo() { - new VideoFileInfo() - { - Path = @"/server/Movies/Brave (2007)/Brave (2006).mkv", - Container = "mkv", - Name = "Brave", - Year = 2006, - } - }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + Path = @"/server/Movies/Brave (2007)/Brave (2006).mkv", + Container = "mkv", + Name = "Brave", + Year = 2006, + } + }; } + [Theory] - [ClassData(typeof(ResolveFileTestData))] + [MemberData(nameof(GetResolveFileTestData))] public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path); From da17a1201fc0d903054fd56069f224e95d694d3e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 25 Apr 2020 15:49:53 +0200 Subject: [PATCH 034/137] Please roslyn --- tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs index 82004081ee..d2b3d6ff0d 100644 --- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -56,7 +56,7 @@ namespace Jellyfin.Naming.Tests.Video Test("Super movie [sbs3d].mp4", true, "sbs3d"); } - private void Test(string input, bool is3D, string format3D) + private void Test(string input, bool is3D, string? format3D) { var parser = new Format3DParser(_namingOptions); From 99fe8dbe628563b6a72917b3530b2686d2899623 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 25 Apr 2020 18:55:54 +0200 Subject: [PATCH 035/137] Remove BaseVideoTest --- tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs | 13 ------------- .../Video/VideoResolverTests.cs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs 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/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index 709d893684..114735ceed 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -6,7 +6,7 @@ using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class VideoResolverTests : BaseVideoTest + public class VideoResolverTests { private readonly NamingOptions _namingOptions = new NamingOptions(); From 7e467f9faa18168ddff0607751f3929e4e3e0285 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 25 Apr 2020 18:36:09 -0400 Subject: [PATCH 036/137] Use the correct method to synchronously wait for tasks to complete --- tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs index 09821d7286..5300d36610 100644 --- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -65,7 +65,7 @@ namespace MediaBrowser.Api.Tests // Create the logging config file // TODO: We shouldn't need to do this since we are only logging to console - Program.InitLoggingConfigFile(appPaths).Wait(); + Program.InitLoggingConfigFile(appPaths).GetAwaiter().GetResult(); // Create a copy of the application configuration to use for startup var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); @@ -95,8 +95,8 @@ namespace MediaBrowser.Api.Tests // Finish initializing the app host var appHost = (CoreAppHost)testServer.Services.GetRequiredService(); appHost.ServiceProvider = testServer.Services; - appHost.InitializeServices().Wait(); - appHost.RunStartupTasksAsync().Wait(); + appHost.InitializeServices().GetAwaiter().GetResult(); + appHost.RunStartupTasksAsync().GetAwaiter().GetResult(); return testServer; } From 57b5ec1d514ad8c5a0e8aced4b84b46b4c8923a7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 26 Apr 2020 12:07:54 -0400 Subject: [PATCH 037/137] Remove unnecessary properties from SystemInfo response object These properties do not provide any useful information to the client. The client would already have to have all this information in order to connect to the endpoint to retrieve it --- Emby.Server.Implementations/ApplicationHost.cs | 4 ---- Jellyfin.Server/Program.cs | 3 --- .../Configuration/ServerConfiguration.cs | 5 ----- MediaBrowser.Model/System/SystemInfo.cs | 18 ------------------ 4 files changed, 30 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9655d9f5ee..edfed67a18 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -94,7 +94,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; @@ -1143,9 +1142,6 @@ namespace Emby.Server.Implementations ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, - HttpServerPortNumber = HttpPort, - SupportsHttps = ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy, - HttpsPortNumber = HttpsPort, OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystemDisplayName = OperatingSystem.Name, CanSelfRestart = CanSelfRestart, diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 7aa238efac..1c586ffdd6 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; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 6ee6a1f932..e4cd6c34ad 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -234,11 +234,6 @@ namespace MediaBrowser.Model.Configuration /// public bool RequireHttps { get; set; } - /// - /// Gets or sets a value indicating whether the server is behind a reverse proxy. - /// - public bool IsBehindProxy { get; set; } - public bool EnableNewOmdbSupport { get; set; } public string[] RemoteIPFilter { get; set; } diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 9753f4e06d..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 a client can connect to the server over HTTPS, either directly or - /// via a reverse proxy. - /// - 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. /// From a0a5512069a56c64d4cf2556f4c04633bda2d120 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sun, 26 Apr 2020 19:35:36 +0100 Subject: [PATCH 038/137] 2969 - re-issed code to address when developer doesn't have certificate installed. --- Jellyfin.Server/Program.cs | 44 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 5bff1db622..069a10b1a0 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -272,17 +272,17 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); - if (appHost.EnableHttps) + if (appHost.EnableHttps && appHost.Certificate != null) { - if (appHost.Certificate != null) + options.Listen(address, appHost.HttpsPort, listenOptions => { - options.Listen(address, appHost.HttpsPort, listenOptions => - { - listenOptions.UseHttps(appHost.Certificate); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - else if (builderContext.HostingEnvironment.IsDevelopment()) + listenOptions.UseHttps(appHost.Certificate); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + else if (builderContext.HostingEnvironment.IsDevelopment()) + { + try { options.Listen(address, appHost.HttpsPort, listenOptions => { @@ -290,6 +290,10 @@ namespace Jellyfin.Server listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); } + catch (Exception ex) + { + _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); + } } } } @@ -298,17 +302,17 @@ namespace Jellyfin.Server _logger.LogInformation("Kestrel listening on all interfaces"); options.ListenAnyIP(appHost.HttpPort); - if (appHost.EnableHttps) + if (appHost.EnableHttps && appHost.Certificate != null) { - if (appHost.Certificate != null) + options.ListenAnyIP(appHost.HttpsPort, listenOptions => { - options.ListenAnyIP(appHost.HttpsPort, listenOptions => - { - listenOptions.UseHttps(appHost.Certificate); - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - } - else if (builderContext.HostingEnvironment.IsDevelopment()) + listenOptions.UseHttps(appHost.Certificate); + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + } + else if (builderContext.HostingEnvironment.IsDevelopment()) + { + try { options.ListenAnyIP(appHost.HttpsPort, listenOptions => { @@ -316,6 +320,10 @@ namespace Jellyfin.Server listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); } + catch (Exception ex) + { + _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); + } } } }) From cabdec87e8e946bfa5de0dd0f97129f1a23e7d98 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 26 Apr 2020 16:55:00 -0400 Subject: [PATCH 039/137] Fix merge with master --- .../EntryPoints/ExternalPortForwarding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index adec9dbe2c..878cee23c4 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -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); } From 15fd4812f09282e9b81f53845ce462f42ff1b5e9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 26 Apr 2020 18:04:34 -0400 Subject: [PATCH 040/137] Remove unnecessary foreach loop --- Emby.Server.Implementations/ApplicationHost.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index edfed67a18..8f20a4921f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1187,13 +1187,12 @@ namespace Emby.Server.Implementations { // 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); + return null; } - return null; + return GetLocalApiUrl(addresses.First()); } catch (Exception ex) { From 7659a2ab326770d7c81501c88966b4443ad2d39e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 1 May 2020 16:48:33 +0200 Subject: [PATCH 041/137] Remove ListHelper extensions --- MediaBrowser.Model/Dlna/CodecProfile.cs | 4 +-- MediaBrowser.Model/Dlna/ConditionProcessor.cs | 6 ++--- MediaBrowser.Model/Dlna/ContainerProfile.cs | 8 +++--- MediaBrowser.Model/Dlna/DeviceProfile.cs | 25 ++++++++--------- MediaBrowser.Model/Dlna/SubtitleProfile.cs | 5 ++-- MediaBrowser.Model/Extensions/ListHelper.cs | 27 ------------------- .../Notifications/NotificationOptions.cs | 14 ++++++---- 7 files changed, 33 insertions(+), 56 deletions(-) delete mode 100644 MediaBrowser.Model/Extensions/ListHelper.cs 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/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/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; From 04f826e50c23a8a996fd317124260f67b7ff3f9a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 2 May 2020 01:09:35 +0200 Subject: [PATCH 042/137] Fix merge errors --- .../ApplicationHost.cs | 3 +- .../HttpServer/HttpListenerHost.cs | 9 +- .../HttpServer/IHttpListener.cs | 39 ----- .../Net/WebSocketConnectEventArgs.cs | 29 ---- .../SocketSharp/WebSocketSharpListener.cs | 135 ------------------ .../WebSockets/WebSocketManager.cs | 102 ------------- 6 files changed, 4 insertions(+), 313 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpServer/IHttpListener.cs delete mode 100644 Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs delete mode 100644 Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs delete mode 100644 Emby.Server.Implementations/WebSockets/WebSocketManager.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8b387e195a..6279ce5d0b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -504,7 +504,7 @@ namespace Emby.Server.Implementations } public Task ExecuteHttpHandlerAsync(HttpContext context, Func next) - => HttpServer.RequestHandler(context); + => _httpServer.RequestHandler(context); /// /// Registers services/resources with the service collection that will be available via DI. @@ -597,7 +597,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 2648b57d57..e75140d6c0 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -53,7 +53,6 @@ namespace Emby.Server.Implementations.HttpServer 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(); @@ -67,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; @@ -80,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); 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/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/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/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"); - } - } - } -} From 3623aafcb60dec4f4f5055046717d895b7597b60 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 2 May 2020 01:30:04 +0200 Subject: [PATCH 043/137] Make SonarCloud happy --- .../ApplicationHost.cs | 5 +--- .../HttpServer/HttpListenerHost.cs | 25 +------------------ .../HttpServer/WebSocketConnection.cs | 8 ------ .../Session/WebSocketController.cs | 2 +- MediaBrowser.Controller/Net/IHttpServer.cs | 5 ++-- 5 files changed, 5 insertions(+), 40 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6279ce5d0b..11fed24f7a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -93,7 +93,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; @@ -101,12 +100,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.Configuration; 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 { diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index e75140d6c0..7358c1a81d 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -28,12 +28,11 @@ using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; 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 @@ -699,28 +698,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) - { - // TODO: - } - - _disposed = true; - } - /// /// Processes the web socket message received. /// diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 1af748ebc2..095725c504 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -36,8 +36,6 @@ namespace Emby.Server.Implementations.HttpServer /// private readonly WebSocket _socket; - private bool _disposed = false; - /// /// Initializes a new instance of the class. /// @@ -221,12 +219,6 @@ namespace Emby.Server.Implementations.HttpServer }; await OnReceive(info).ConfigureAwait(false); - - // Stop reading if there's no more data coming - if (result.IsCompleted) - { - return; - } } } } diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index c7ef9b1cec..a0274acd20 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Session private readonly ISessionManager _sessionManager; private readonly SessionInfo _session; - private List _sockets; + private readonly List _sockets; private bool _disposed = false; public WebSocketController( diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 666ac1cfef..f1c4417613 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -2,15 +2,14 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { /// - /// Interface IHttpServer + /// Interface IHttpServer. /// - public interface IHttpServer : IDisposable + public interface IHttpServer { /// /// Gets the URL prefix. From 472efeeec4ddf5dbea1550aeea2173590b24953e Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Sat, 2 May 2020 13:09:57 +0200 Subject: [PATCH 044/137] Remove extra line in UserManager Co-authored-by: Bond-009 --- Emby.Server.Implementations/Library/UserManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 8941767b41..903d43faa6 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -627,7 +627,6 @@ namespace Emby.Server.Implementations.Library !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword; - PublicUserDto dto = new PublicUserDto { Name = user.Name, From daf79b8aeb0e202a6bd71e8a65cd24b42f329210 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 2 May 2020 15:44:24 -0400 Subject: [PATCH 045/137] Do not double dispose write lock and connection in user data repository --- .../Data/SqliteUserDataRepository.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) 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) + { + } } } From 2b41f8ab632b706b0f4e2d17032dbb07ce73136e Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 2 May 2020 17:56:05 -0400 Subject: [PATCH 046/137] Clean up generated code --- Jellyfin.Data/Entities/Artwork.cs | 338 +++++----- Jellyfin.Data/Entities/Book.cs | 109 ++- Jellyfin.Data/Entities/BookMetadata.cs | 175 +++-- Jellyfin.Data/Entities/Chapter.cs | 461 +++++++------ Jellyfin.Data/Entities/Collection.cs | 205 +++--- Jellyfin.Data/Entities/CollectionItem.cs | 232 ++++--- Jellyfin.Data/Entities/Company.cs | 223 +++---- Jellyfin.Data/Entities/CompanyMetadata.cs | 385 ++++++----- Jellyfin.Data/Entities/CustomItem.cs | 109 ++- Jellyfin.Data/Entities/CustomItemMetadata.cs | 113 ++-- Jellyfin.Data/Entities/Episode.cs | 183 +++-- Jellyfin.Data/Entities/EpisodeMetadata.cs | 315 +++++---- Jellyfin.Data/Entities/Genre.cs | 261 ++++---- Jellyfin.Data/Entities/Group.cs | 169 +++-- Jellyfin.Data/Entities/Library.cs | 253 ++++--- Jellyfin.Data/Entities/LibraryItem.cs | 292 ++++---- Jellyfin.Data/Entities/LibraryRoot.cs | 332 +++++---- Jellyfin.Data/Entities/MediaFile.cs | 340 +++++----- Jellyfin.Data/Entities/MediaFileStream.cs | 255 ++++--- Jellyfin.Data/Entities/Metadata.cs | 666 +++++++++---------- Jellyfin.Data/Entities/MetadataProvider.cs | 253 ++++--- Jellyfin.Data/Entities/MetadataProviderId.cs | 302 +++++---- Jellyfin.Data/Entities/Movie.cs | 109 ++- Jellyfin.Data/Entities/MovieMetadata.cs | 391 ++++++----- Jellyfin.Data/Entities/MusicAlbum.cs | 110 ++- Jellyfin.Data/Entities/MusicAlbumMetadata.cs | 322 +++++---- Jellyfin.Data/Entities/Permission.cs | 232 ++++--- Jellyfin.Data/Entities/PermissionKind.cs | 40 -- Jellyfin.Data/Entities/Person.cs | 531 ++++++++------- Jellyfin.Data/Entities/PersonRole.cs | 349 +++++----- Jellyfin.Data/Entities/Photo.cs | 110 ++- Jellyfin.Data/Entities/PhotoMetadata.cs | 113 ++-- Jellyfin.Data/Entities/Preference.cs | 172 +++-- Jellyfin.Data/Entities/PreferenceKind.cs | 27 - Jellyfin.Data/Entities/ProviderMapping.cs | 200 +++--- Jellyfin.Data/Entities/Rating.cs | 320 +++++---- Jellyfin.Data/Entities/RatingSource.cs | 403 ++++++----- Jellyfin.Data/Entities/Release.cs | 314 +++++---- Jellyfin.Data/Entities/Season.cs | 182 +++-- Jellyfin.Data/Entities/SeasonMetadata.cs | 179 +++-- Jellyfin.Data/Entities/Series.cs | 286 ++++---- Jellyfin.Data/Entities/SeriesMetadata.cs | 391 ++++++----- Jellyfin.Data/Entities/Track.cs | 181 +++-- Jellyfin.Data/Entities/TrackMetadata.cs | 113 ++-- Jellyfin.Data/Entities/User.cs | 370 +++++------ Jellyfin.Data/Enums/ArtKind.cs | 28 +- Jellyfin.Data/Enums/MediaFileKind.cs | 28 +- Jellyfin.Data/Enums/PermissionKind.cs | 28 + Jellyfin.Data/Enums/PersonRoleType.cs | 42 +- Jellyfin.Data/Enums/PreferenceKind.cs | 15 + Jellyfin.Data/Enums/Weekday.cs | 32 +- Jellyfin.Data/Structs/.gitkeep | 0 52 files changed, 5613 insertions(+), 5976 deletions(-) delete mode 100644 Jellyfin.Data/Entities/PermissionKind.cs delete mode 100644 Jellyfin.Data/Entities/PreferenceKind.cs create mode 100644 Jellyfin.Data/Enums/PermissionKind.cs create mode 100644 Jellyfin.Data/Enums/PreferenceKind.cs delete mode 100644 Jellyfin.Data/Structs/.gitkeep diff --git a/Jellyfin.Data/Entities/Artwork.cs b/Jellyfin.Data/Entities/Artwork.cs index be13686dc2..da31d686e0 100644 --- a/Jellyfin.Data/Entities/Artwork.cs +++ b/Jellyfin.Data/Entities/Artwork.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,188 +9,194 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Artwork - { - partial void Init(); + [Table("Artwork")] + 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..7dda26f412 100644 --- a/Jellyfin.Data/Entities/Book.cs +++ b/Jellyfin.Data/Entities/Book.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,64 +9,67 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Book: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Book")] + 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() : base() + { + 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..8afd371631 100644 --- a/Jellyfin.Data/Entities/BookMetadata.cs +++ b/Jellyfin.Data/Entities/BookMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,103 +9,104 @@ 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() : base() + { + 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..1ee6a9c07e 100644 --- a/Jellyfin.Data/Entities/Chapter.cs +++ b/Jellyfin.Data/Entities/Chapter.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,254 +9,261 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Chapter - { - partial void Init(); + [Table("Chapter")] + 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..d3ccb13f52 100644 --- a/Jellyfin.Data/Entities/Collection.cs +++ b/Jellyfin.Data/Entities/Collection.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,111 +9,118 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Collection - { - partial void Init(); + [Table("Collection")] + 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..f40158b203 100644 --- a/Jellyfin.Data/Entities/CollectionItem.cs +++ b/Jellyfin.Data/Entities/CollectionItem.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,131 +9,141 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CollectionItem - { - partial void Init(); + [Table("CollectionItem")] + 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..5b8a21423c 100644 --- a/Jellyfin.Data/Entities/Company.cs +++ b/Jellyfin.Data/Entities/Company.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,127 +9,134 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Company - { - partial void Init(); + [Table("Company")] + 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..18357b326c 100644 --- a/Jellyfin.Data/Entities/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/CompanyMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,214 +9,215 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CompanyMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("CompanyMetadata")] + 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() : base() + { + 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..29f4b62a6e 100644 --- a/Jellyfin.Data/Entities/CustomItem.cs +++ b/Jellyfin.Data/Entities/CustomItem.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,64 +9,65 @@ 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() : base() + { + 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..8fbccfdd83 100644 --- a/Jellyfin.Data/Entities/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/CustomItemMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,66 +9,67 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class CustomItemMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("CustomItemMetadata")] + 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() : base() + { + 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..be358a0fd3 100644 --- a/Jellyfin.Data/Entities/Episode.cs +++ b/Jellyfin.Data/Entities/Episode.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,107 +9,108 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Episode: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Episode")] + 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() : 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. - 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..a1f4adf7bf 100644 --- a/Jellyfin.Data/Entities/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/EpisodeMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,177 +9,178 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class EpisodeMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("EpisodeMetadata")] + 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() : base() + { + 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..d38265d80b 100644 --- a/Jellyfin.Data/Entities/Genre.cs +++ b/Jellyfin.Data/Entities/Genre.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,143 +9,150 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Genre - { - partial void Init(); + [Table("Genre")] + 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..4b58120fc0 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,95 +9,106 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Group - { - partial void Init(); + [Table("Group")] + 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..f3faa8699c 100644 --- a/Jellyfin.Data/Entities/Library.cs +++ b/Jellyfin.Data/Entities/Library.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,138 +9,145 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Library - { - partial void Init(); + [Table("Library")] + 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..29547562bb 100644 --- a/Jellyfin.Data/Entities/LibraryItem.cs +++ b/Jellyfin.Data/Entities/LibraryItem.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,160 +9,168 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public abstract partial class LibraryItem - { - partial void Init(); + [Table("LibraryItem")] + 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..932e3edb8e 100644 --- a/Jellyfin.Data/Entities/LibraryRoot.cs +++ b/Jellyfin.Data/Entities/LibraryRoot.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,182 +9,190 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class LibraryRoot - { - partial void Init(); + [Table("LibraryRoot")] + 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..dbb65a6f7f 100644 --- a/Jellyfin.Data/Entities/MediaFile.cs +++ b/Jellyfin.Data/Entities/MediaFile.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,189 +9,197 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MediaFile - { - partial void Init(); + [Table("MediaFile")] + 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..3ce18b8d71 100644 --- a/Jellyfin.Data/Entities/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/MediaFileStream.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,140 +9,147 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MediaFileStream - { - partial void Init(); + [Table("MediaFileStream")] + 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..5cba24ee3f 100644 --- a/Jellyfin.Data/Entities/Metadata.cs +++ b/Jellyfin.Data/Entities/Metadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,365 +9,377 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public abstract partial class Metadata - { - partial void Init(); + [Table("Metadata")] + 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..bc6e04277a 100644 --- a/Jellyfin.Data/Entities/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/MetadataProvider.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,138 +9,145 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MetadataProvider - { - partial void Init(); + [Table("MetadataProvider")] + 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..d381856f3d 100644 --- a/Jellyfin.Data/Entities/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/MetadataProviderId.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,169 +9,177 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MetadataProviderId - { - partial void Init(); + [Table("MetadataProviderId")] + 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..23a340b1b3 100644 --- a/Jellyfin.Data/Entities/Movie.cs +++ b/Jellyfin.Data/Entities/Movie.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,64 +9,67 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Movie: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Movie")] + 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() : base() + { + 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..090761877e 100644 --- a/Jellyfin.Data/Entities/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/MovieMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,219 +9,220 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MovieMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("MovieMetadata")] + 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() : base() + { + 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..fc9c122865 100644 --- a/Jellyfin.Data/Entities/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/MusicAlbum.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,64 +9,66 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MusicAlbum: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("MusicAlbum")] + 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() : base() + { + 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..4bfe780d1e 100644 --- a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,182 +9,184 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class MusicAlbumMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("MusicAlbumMetadata")] + 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() : base() + { + 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..29ba9e1a45 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,132 +9,140 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Permission - { - partial void Init(); + [Table("Permission")] + 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..6a4ad52851 100644 --- a/Jellyfin.Data/Entities/Person.cs +++ b/Jellyfin.Data/Entities/Person.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,292 +9,299 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Person - { - partial void Init(); + [Table("Person")] + 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..0c6cb2c6e1 100644 --- a/Jellyfin.Data/Entities/PersonRole.cs +++ b/Jellyfin.Data/Entities/PersonRole.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,195 +9,206 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class PersonRole - { - partial void Init(); + [Table("PersonRole")] + 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..89f9764442 100644 --- a/Jellyfin.Data/Entities/Photo.cs +++ b/Jellyfin.Data/Entities/Photo.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,64 +9,66 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Photo: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Photo")] + 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() : base() + { + 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..b3f796839f 100644 --- a/Jellyfin.Data/Entities/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/PhotoMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,66 +9,67 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class PhotoMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("PhotoMetadata")] + 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() : base() + { + 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..8b3ddb568f 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,97 +9,105 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Preference - { - partial void Init(); + [Table("Preference")] + 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..0eb098a8ff 100644 --- a/Jellyfin.Data/Entities/ProviderMapping.cs +++ b/Jellyfin.Data/Entities/ProviderMapping.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,113 +9,121 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class ProviderMapping - { - partial void Init(); + [Table("ProviderMapping")] + 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..46e8c3f117 100644 --- a/Jellyfin.Data/Entities/Rating.cs +++ b/Jellyfin.Data/Entities/Rating.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,177 +9,185 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Rating - { - partial void Init(); + [Table("Rating")] + 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..7e60fac437 100644 --- a/Jellyfin.Data/Entities/RatingSource.cs +++ b/Jellyfin.Data/Entities/RatingSource.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,222 +9,229 @@ 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 + /// + [Table("RatingSource")] + 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..91dd35a7f0 100644 --- a/Jellyfin.Data/Entities/Release.cs +++ b/Jellyfin.Data/Entities/Release.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,177 +9,185 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Release - { - partial void Init(); + [Table("Release")] + 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..3928a4ba62 100644 --- a/Jellyfin.Data/Entities/Season.cs +++ b/Jellyfin.Data/Entities/Season.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,107 +9,109 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Season: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Season")] + 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() : 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. - 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..f0e669a49e 100644 --- a/Jellyfin.Data/Entities/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/SeasonMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,103 +9,104 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class SeasonMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("SeasonMetadata")] + 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() : base() + { + 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..fecc229af9 100644 --- a/Jellyfin.Data/Entities/Series.cs +++ b/Jellyfin.Data/Entities/Series.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,163 +9,165 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Series: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Series")] + 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() : base() + { + 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..15818f9416 100644 --- a/Jellyfin.Data/Entities/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/SeriesMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,219 +9,220 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class SeriesMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("SeriesMetadata")] + 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() : base() + { + 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..50ee43042f 100644 --- a/Jellyfin.Data/Entities/Track.cs +++ b/Jellyfin.Data/Entities/Track.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,107 +9,110 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class Track: global::Jellyfin.Data.Entities.LibraryItem - { - partial void Init(); + [Table("Track")] + 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() : 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. - 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..84679ebb5d 100644 --- a/Jellyfin.Data/Entities/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/TrackMetadata.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,66 +9,67 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class TrackMetadata: global::Jellyfin.Data.Entities.Metadata - { - partial void Init(); + [Table("TrackMetadata")] + 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() : base() + { + 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..715969dbf0 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,15 +1,3 @@ -//------------------------------------------------------------------------------ -// -// 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; @@ -21,222 +9,232 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - public partial class User - { - partial void Init(); + [Table("User")] + 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..546e1533cc 100644 --- a/Jellyfin.Data/Enums/ArtKind.cs +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -1,25 +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 ArtKind : Int32 - { - Other, - Poster, - Banner, - Thumbnail, - Logo - } + public enum ArtKind : Int32 + { + Other, + Poster, + Banner, + Thumbnail, + Logo + } } diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs index 34d1b20f59..d249202282 100644 --- a/Jellyfin.Data/Enums/MediaFileKind.cs +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -1,25 +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 MediaFileKind : Int32 - { - Main, - Sidecar, - AdditionalPart, - AlternativeFormat, - AdditionalStream - } + public enum MediaFileKind : Int32 + { + 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..4447fdb773 --- /dev/null +++ b/Jellyfin.Data/Enums/PermissionKind.cs @@ -0,0 +1,28 @@ +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/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs index f5c8f43c51..5621ffa4d0 100644 --- a/Jellyfin.Data/Enums/PersonRoleType.cs +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -1,32 +1,20 @@ -//------------------------------------------------------------------------------ -// -// 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 : Int32 + { + 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..e66a51cae1 --- /dev/null +++ b/Jellyfin.Data/Enums/PreferenceKind.cs @@ -0,0 +1,15 @@ +using System; + +namespace Jellyfin.Data.Enums +{ + public enum PreferenceKind : Int32 + { + MaxParentalRating, + BlockedTags, + RemoteClientBitrateLimit, + EnabledDevices, + EnabledChannels, + EnabledFolders, + EnableContentDeletionFromFolders + } +} diff --git a/Jellyfin.Data/Enums/Weekday.cs b/Jellyfin.Data/Enums/Weekday.cs index ce0c6e4ce8..58523a6c7f 100644 --- a/Jellyfin.Data/Enums/Weekday.cs +++ b/Jellyfin.Data/Enums/Weekday.cs @@ -1,27 +1,15 @@ -//------------------------------------------------------------------------------ -// -// 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 : Int32 + { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday + } } diff --git a/Jellyfin.Data/Structs/.gitkeep b/Jellyfin.Data/Structs/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 From 032de931b14ded24bb1098a7eeec3d84561206e2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 2 May 2020 18:32:22 -0400 Subject: [PATCH 047/137] Migrate activity db to EF Core --- .../Activity/ActivityLogEntryPoint.cs | 288 ++-- .../Activity/ActivityManager.cs | 70 - .../Activity/ActivityRepository.cs | 308 ---- .../ApplicationHost.cs | 14 +- .../Emby.Server.Implementations.csproj | 5 +- Jellyfin.Data/DbContexts/Jellyfin.cs | 1140 ------------- Jellyfin.Data/Entities/ActivityLog.cs | 153 ++ Jellyfin.Data/ISavingChanges.cs | 9 + Jellyfin.Data/Jellyfin.Data.csproj | 24 +- .../Activity/ActivityManager.cs | 103 ++ .../Jellyfin.Server.Implementations.csproj | 34 + Jellyfin.Server.Implementations/JellyfinDb.cs | 119 ++ .../JellyfinDbProvider.cs | 33 + .../20200430215054_InitialSchema.Designer.cs | 1513 +++++++++++++++++ .../20200430215054_InitialSchema.cs | 1294 ++++++++++++++ .../Migrations/DesignTimeJellyfinDbFactory.cs | 20 + .../Migrations/JellyfinDbModelSnapshot.cs | 1511 ++++++++++++++++ Jellyfin.Server/Jellyfin.Server.csproj | 7 + Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Routines/MigrateActivityLogDb.cs | 109 ++ MediaBrowser.Api/Library/LibraryService.cs | 11 +- MediaBrowser.Api/System/ActivityLogService.cs | 2 +- .../Activity/ActivityLogEntry.cs | 1 + .../Activity/IActivityManager.cs | 15 +- .../Activity/IActivityRepository.cs | 14 - MediaBrowser.Model/MediaBrowser.Model.csproj | 3 + MediaBrowser.sln | 46 +- 27 files changed, 5147 insertions(+), 1702 deletions(-) delete mode 100644 Emby.Server.Implementations/Activity/ActivityManager.cs delete mode 100644 Emby.Server.Implementations/Activity/ActivityRepository.cs delete mode 100644 Jellyfin.Data/DbContexts/Jellyfin.cs create mode 100644 Jellyfin.Data/Entities/ActivityLog.cs create mode 100644 Jellyfin.Data/ISavingChanges.cs create mode 100644 Jellyfin.Server.Implementations/Activity/ActivityManager.cs create mode 100644 Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj create mode 100644 Jellyfin.Server.Implementations/JellyfinDb.cs create mode 100644 Jellyfin.Server.Implementations/JellyfinDbProvider.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs create mode 100644 Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs delete mode 100644 MediaBrowser.Model/Activity/IActivityRepository.cs diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 4685a03ac3..54894fd65b 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -4,11 +4,11 @@ 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; @@ -104,47 +104,53 @@ namespace Emby.Server.Implementations.Activity return Task.CompletedTask; } - private void OnCameraImageUploaded(object sender, GenericEventArgs e) + private async void OnCameraImageUploaded(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name), - Type = NotificationType.CameraImageUploaded.ToString() - }); + NotificationType.CameraImageUploaded.ToString(), + Guid.Empty, + DateTime.UtcNow, + LogLevel.Trace)) + .ConfigureAwait(false); } - private void OnUserLockedOut(object sender, GenericEventArgs e) + private async void OnUserLockedOut(object sender, GenericEventArgs e) { - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format( + await CreateLogEntry(new ActivityLog( + string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name), - Type = NotificationType.UserLockedOut.ToString(), - UserId = e.Argument.Id - }); + NotificationType.UserLockedOut.ToString(), + e.Argument.Id, + DateTime.UtcNow, + LogLevel.Trace)) + .ConfigureAwait(false); } - private void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) + private async 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", + Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), + "SubtitleDownloadFailure", + Guid.Empty, + DateTime.UtcNow, + LogLevel.Trace) + { 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 +173,21 @@ 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, + DateTime.UtcNow, + LogLevel.Trace)) + .ConfigureAwait(false); } - private void OnPlaybackStart(object sender, PlaybackProgressEventArgs e) + private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e) { var item = e.MediaInfo; @@ -203,17 +210,18 @@ 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, + DateTime.UtcNow, + LogLevel.Trace)) + .ConfigureAwait(false); } private static string GetItemName(BaseItemDto item) @@ -263,7 +271,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 +280,120 @@ 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, + DateTime.UtcNow, + LogLevel.Trace) + { 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, + DateTime.UtcNow, + LogLevel.Trace) + { 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, + DateTime.UtcNow, + 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, + DateTime.UtcNow, + LogLevel.Trace)) + .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, + DateTime.UtcNow, + LogLevel.Trace)) + .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, + DateTime.UtcNow, + LogLevel.Trace)).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, + DateTime.UtcNow, + LogLevel.Trace)) + .ConfigureAwait(false); } - private void OnSessionStarted(object sender, SessionEventArgs e) + private async void OnSessionStarted(object sender, SessionEventArgs e) { var session = e.SessionInfo; @@ -384,87 +402,100 @@ 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, + DateTime.UtcNow, + LogLevel.Trace) + { 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, + DateTime.UtcNow, + LogLevel.Trace) + { 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, + DateTime.UtcNow, + LogLevel.Trace)) + .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, + DateTime.UtcNow, + LogLevel.Trace) + { 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, + DateTime.UtcNow, + LogLevel.Trace) + { 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 +526,21 @@ 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, + DateTime.UtcNow, + LogLevel.Error) { - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("ScheduledTaskFailedWithName"), - task.Name), - Type = NotificationType.TaskFailed.ToString(), 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() @@ -558,7 +588,7 @@ namespace Emby.Server.Implementations.Activity { int years = days / DaysInYear; values.Add(CreateValueString(years, "year")); - days %= DaysInYear; + days = days % DaysInYear; } // Number of months @@ -566,7 +596,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..ddd9c79533 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; @@ -47,6 +46,8 @@ using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; +using Jellyfin.Server.Implementations; +using Jellyfin.Server.Implementations.Activity; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -94,7 +95,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; @@ -103,6 +103,7 @@ using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; @@ -553,6 +554,13 @@ namespace Emby.Server.Implementations return Logger; }); + // TODO: properly set up scoping and switch to AddDbContextPool + serviceCollection.AddDbContext( + options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), + ServiceLifetime.Transient); + + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton(); @@ -663,7 +671,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -696,7 +703,6 @@ namespace Emby.Server.Implementations ((SqliteDisplayPreferencesRepository)Resolve()).Initialize(); ((AuthenticationRepository)Resolve()).Initialize(); ((SqliteUserRepository)Resolve()).Initialize(); - ((ActivityRepository)Resolve()).Initialize(); SetStaticProperties(); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 44fc932e39..dccbe2a9a6 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -1,4 +1,4 @@ - + @@ -9,6 +9,7 @@ + @@ -50,7 +51,7 @@ - netstandard2.1 + netcoreapp3.1 false true 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..6338389913 --- /dev/null +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -0,0 +1,153 @@ +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 +{ + [Table("ActivityLog")] + public partial class ActivityLog + { + partial void Init(); + + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected ActivityLog() + { + Init(); + } + + /// + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// + public static ActivityLog CreateActivityLogUnsafe() + { + return new ActivityLog(); + } + + /// + /// Public constructor with required data + /// + /// + /// + /// + /// + /// + public ActivityLog(string name, string type, Guid userid, DateTime datecreated, Microsoft.Extensions.Logging.LogLevel logseverity) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + + if (string.IsNullOrEmpty(type)) throw new ArgumentNullException(nameof(type)); + this.Type = type; + + this.UserId = userid; + + this.DateCreated = datecreated; + + this.LogSeverity = logseverity; + + + Init(); + } + + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// + /// + /// + /// + /// + public static ActivityLog Create(string name, string type, Guid userid, DateTime datecreated, Microsoft.Extensions.Logging.LogLevel logseverity) + { + return new ActivityLog(name, type, userid, datecreated, logseverity); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// + /// Identity, Indexed, Required + /// + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// + /// Required, Max length = 512 + /// + [Required] + [MaxLength(512)] + [StringLength(512)] + public string Name { get; set; } + + /// + /// Max length = 512 + /// + [MaxLength(512)] + [StringLength(512)] + public string Overview { get; set; } + + /// + /// Max length = 512 + /// + [MaxLength(512)] + [StringLength(512)] + public string ShortOverview { get; set; } + + /// + /// Required, Max length = 256 + /// + [Required] + [MaxLength(256)] + [StringLength(256)] + public string Type { get; set; } + + /// + /// Required + /// + [Required] + public Guid UserId { get; set; } + + /// + /// Max length = 256 + /// + [MaxLength(256)] + [StringLength(256)] + public string ItemId { get; set; } + + /// + /// Required + /// + [Required] + public DateTime DateCreated { get; set; } + + /// + /// Required + /// + [Required] + public Microsoft.Extensions.Logging.LogLevel LogSeverity { get; set; } + + /// + /// Required, ConcurrenyToken + /// + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + } +} + diff --git a/Jellyfin.Data/ISavingChanges.cs b/Jellyfin.Data/ISavingChanges.cs new file mode 100644 index 0000000000..5388b921d8 --- /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..8eae366bab 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -1,12 +1,30 @@ - netstandard2.0 + netstandard2.0;netstandard2.1 + false + true + + ../jellyfin.ruleset + + + + + + + + + + - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs new file mode 100644 index 0000000000..d7bbf793c4 --- /dev/null +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +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 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, IEnumerable> func, + int? startIndex, + int? limit) + { + using var dbContext = _provider.CreateContext(); + + var result = func.Invoke(dbContext.ActivityLogs).AsQueryable(); + + if (startIndex.HasValue) + { + result = result.Where(entry => entry.Id >= startIndex.Value); + } + + if (limit.HasValue) + { + result = result.OrderByDescending(entry => entry.DateCreated).Take(limit.Value); + } + + // This converts the objects from the new database model to the old for compatibility with the existing API. + var list = result.Select(entry => ConvertToOldModel(entry)).ToList(); + + return new QueryResult() + { + Items = list, + TotalRecordCount = list.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..a31f28f64a --- /dev/null +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp3.1 + false + true + true + + + + ../jellyfin.ruleset + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs new file mode 100644 index 0000000000..9c1a23877b --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -0,0 +1,119 @@ +#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 entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + { + var saveEntity = entity.Entity as ISavingChanges; + 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..8fdeab0887 --- /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.GetService(); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs new file mode 100644 index 0000000000..3fb0fd51e1 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs @@ -0,0 +1,1513 @@ +#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("20200430215054_InitialSchema")] + partial class InitialSchema + { + 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("ActivityLog"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Kind"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Chapter_Chapters_Id") + .HasColumnType("INTEGER"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("TimeEnd") + .HasColumnType("INTEGER"); + + b.Property("TimeStart") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Chapter_Chapters_Id"); + + b.ToTable("Chapter"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_CollectionItem_Id") + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_Next_Id") + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_Previous_Id") + .HasColumnType("INTEGER"); + + b.Property("LibraryItem_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionItem_CollectionItem_Id"); + + b.HasIndex("CollectionItem_Next_Id"); + + b.HasIndex("CollectionItem_Previous_Id"); + + b.HasIndex("LibraryItem_Id"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Company_Labels_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Networks_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Parent_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Publishers_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Studios_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Company_Labels_Id"); + + b.HasIndex("Company_Networks_Id"); + + b.HasIndex("Company_Parent_Id"); + + b.HasIndex("Company_Publishers_Id"); + + b.HasIndex("Company_Studios_Id"); + + b.ToTable("Company"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("Genre"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Group_Groups_Id") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Group_Groups_Id"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LibraryRoot_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryRoot_Id"); + + b.HasIndex("UrlId") + .IsUnique(); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator("Discriminator").HasValue("LibraryItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Library_Id") + .HasColumnType("INTEGER"); + + b.Property("NetworkPath") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Library_Id"); + + b.ToTable("LibraryRoot"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("MediaFile_MediaFiles_Id") + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFile_MediaFiles_Id"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaFileStream_MediaFileStreams_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("StreamNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileStream_MediaFileStreams_Id"); + + b.ToTable("MediaFileStream"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateModified") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property("OriginalTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator("Discriminator").HasValue("Metadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MetadataProvider"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MetadataProviderId_Sources_Id") + .HasColumnType("INTEGER"); + + b.Property("MetadataProvider_Id") + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Sources_Id"); + + b.HasIndex("MetadataProvider_Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("MetadataProviderId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_GroupPermissions_Id") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_GroupPermissions_Id"); + + b.HasIndex("Permission_Permissions_Id"); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateModified") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SourceId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Artwork_Artwork_Id") + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("Person_Id") + .HasColumnType("INTEGER"); + + b.Property("Role") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Artwork_Artwork_Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.HasIndex("Person_Id"); + + b.ToTable("PersonRole"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Id"); + + b.ToTable("Preference"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ProviderData") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("ProviderMapping_ProviderMappings_Id") + .HasColumnType("INTEGER"); + + b.Property("ProviderName") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("ProviderSecrets") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderMapping_ProviderMappings_Id"); + + b.ToTable("ProviderMapping"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RatingSource_RatingType_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("REAL"); + + b.Property("Votes") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.HasIndex("RatingSource_RatingType_Id"); + + b.ToTable("Rating"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MaximumValue") + .HasColumnType("REAL"); + + b.Property("MetadataProviderId_Source_Id") + .HasColumnType("INTEGER"); + + b.Property("MinimumValue") + .HasColumnType("REAL"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Source_Id"); + + b.ToTable("RatingSource"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Release_Releases_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Release_Releases_Id"); + + b.ToTable("Release"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioLanguagePreference") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("GroupedFolders") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LatestItemExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("MyMediaExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("OrderedViews") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePrefernce") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("SubtitleMode") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Book", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Book"); + + b.HasDiscriminator().HasValue("Book"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItem", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator().HasValue("CustomItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Episode_Episodes_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Episode_Episodes_Id"); + + b.ToTable("Episode"); + + b.HasDiscriminator().HasValue("Episode"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Movie", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Movie"); + + b.HasDiscriminator().HasValue("Movie"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbum", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("MusicAlbum"); + + b.HasDiscriminator().HasValue("MusicAlbum"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Photo", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Photo"); + + b.HasDiscriminator().HasValue("Photo"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Season_Seasons_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Season_Seasons_Id"); + + b.ToTable("Season"); + + b.HasDiscriminator().HasValue("Season"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Series", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("AirsDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("AirsTime") + .HasColumnType("TEXT"); + + b.Property("FirstAired") + .HasColumnType("TEXT"); + + b.ToTable("Series"); + + b.HasDiscriminator().HasValue("Series"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("TrackNumber") + .HasColumnType("INTEGER"); + + b.Property("Track_Tracks_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Track_Tracks_Id"); + + b.ToTable("Track"); + + b.HasDiscriminator().HasValue("Track"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("BookMetadata_BookMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("ISBN") + .HasColumnType("INTEGER"); + + b.HasIndex("BookMetadata_BookMetadata_Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator().HasValue("BookMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("CompanyMetadata_CompanyMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("Description") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Headquarters") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Homepage") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("CompanyMetadata_CompanyMetadata_Id"); + + b.ToTable("CompanyMetadata"); + + b.HasDiscriminator().HasValue("CompanyMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("CustomItemMetadata_CustomItemMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("CustomItemMetadata_CustomItemMetadata_Id"); + + b.ToTable("CustomItemMetadata"); + + b.HasDiscriminator().HasValue("CustomItemMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("EpisodeMetadata_EpisodeMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("EpisodeMetadata_EpisodeMetadata_Id"); + + b.ToTable("EpisodeMetadata"); + + b.HasDiscriminator().HasValue("EpisodeMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Country") + .HasColumnName("MovieMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("MovieMetadata_MovieMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnName("MovieMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnName("MovieMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Tagline") + .HasColumnName("MovieMetadata_Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("MovieMetadata_MovieMetadata_Id"); + + b.ToTable("MovieMetadata"); + + b.HasDiscriminator().HasValue("MovieMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Barcode") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Country") + .HasColumnName("MusicAlbumMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("LabelNumber") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("MusicAlbumMetadata_MusicAlbumMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("MusicAlbumMetadata_MusicAlbumMetadata_Id"); + + b.ToTable("MusicAlbumMetadata"); + + b.HasDiscriminator().HasValue("MusicAlbumMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("PhotoMetadata_PhotoMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("PhotoMetadata_PhotoMetadata_Id"); + + b.ToTable("PhotoMetadata"); + + b.HasDiscriminator().HasValue("PhotoMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Outline") + .HasColumnName("SeasonMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("SeasonMetadata_SeasonMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonMetadata_SeasonMetadata_Id"); + + b.ToTable("SeasonMetadata"); + + b.HasDiscriminator().HasValue("SeasonMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Country") + .HasColumnName("SeriesMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("Outline") + .HasColumnName("SeriesMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnName("SeriesMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("SeriesMetadata_SeriesMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnName("SeriesMetadata_Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("SeriesMetadata_SeriesMetadata_Id"); + + b.ToTable("SeriesMetadata"); + + b.HasDiscriminator().HasValue("SeriesMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("TrackMetadata_TrackMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("TrackMetadata_TrackMetadata_Id"); + + b.ToTable("TrackMetadata"); + + b.HasDiscriminator().HasValue("TrackMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Artwork") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => + { + b.HasOne("Jellyfin.Data.Entities.Release", null) + .WithMany("Chapters") + .HasForeignKey("Chapter_Chapters_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.HasOne("Jellyfin.Data.Entities.Collection", null) + .WithMany("CollectionItem") + .HasForeignKey("CollectionItem_CollectionItem_Id"); + + b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Next") + .WithMany() + .HasForeignKey("CollectionItem_Next_Id"); + + b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Previous") + .WithMany() + .HasForeignKey("CollectionItem_Previous_Id"); + + b.HasOne("Jellyfin.Data.Entities.LibraryItem", "LibraryItem") + .WithMany() + .HasForeignKey("LibraryItem_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbumMetadata", null) + .WithMany("Labels") + .HasForeignKey("Company_Labels_Id"); + + b.HasOne("Jellyfin.Data.Entities.SeriesMetadata", null) + .WithMany("Networks") + .HasForeignKey("Company_Networks_Id"); + + b.HasOne("Jellyfin.Data.Entities.Company", "Parent") + .WithMany() + .HasForeignKey("Company_Parent_Id"); + + b.HasOne("Jellyfin.Data.Entities.BookMetadata", null) + .WithMany("Publishers") + .HasForeignKey("Company_Publishers_Id"); + + b.HasOne("Jellyfin.Data.Entities.MovieMetadata", null) + .WithMany("Studios") + .HasForeignKey("Company_Studios_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Genres") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Groups") + .HasForeignKey("Group_Groups_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.HasOne("Jellyfin.Data.Entities.LibraryRoot", "LibraryRoot") + .WithMany() + .HasForeignKey("LibraryRoot_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.HasOne("Jellyfin.Data.Entities.Library", "Library") + .WithMany() + .HasForeignKey("Library_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.HasOne("Jellyfin.Data.Entities.Release", null) + .WithMany("MediaFiles") + .HasForeignKey("MediaFile_MediaFiles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.HasOne("Jellyfin.Data.Entities.MediaFile", null) + .WithMany("MediaFileStreams") + .HasForeignKey("MediaFileStream_MediaFileStreams_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.HasOne("Jellyfin.Data.Entities.Person", null) + .WithMany("Sources") + .HasForeignKey("MetadataProviderId_Sources_Id"); + + b.HasOne("Jellyfin.Data.Entities.PersonRole", null) + .WithMany("Sources") + .HasForeignKey("MetadataProviderId_Sources_Id"); + + b.HasOne("Jellyfin.Data.Entities.MetadataProvider", "MetadataProvider") + .WithMany() + .HasForeignKey("MetadataProvider_Id"); + + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Sources") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("GroupPermissions") + .HasForeignKey("Permission_GroupPermissions_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.HasOne("Jellyfin.Data.Entities.Artwork", "Artwork") + .WithMany() + .HasForeignKey("Artwork_Artwork_Id"); + + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("PersonRoles") + .HasForeignKey("PersonRole_PersonRoles_Id"); + + b.HasOne("Jellyfin.Data.Entities.Person", "Person") + .WithMany() + .HasForeignKey("Person_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("ProviderMappings") + .HasForeignKey("ProviderMapping_ProviderMappings_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ProviderMappings") + .HasForeignKey("ProviderMapping_ProviderMappings_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Ratings") + .HasForeignKey("PersonRole_PersonRoles_Id"); + + b.HasOne("Jellyfin.Data.Entities.RatingSource", "RatingType") + .WithMany() + .HasForeignKey("RatingSource_RatingType_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => + { + b.HasOne("Jellyfin.Data.Entities.MetadataProviderId", "Source") + .WithMany() + .HasForeignKey("MetadataProviderId_Source_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.HasOne("Jellyfin.Data.Entities.Book", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id"); + + b.HasOne("Jellyfin.Data.Entities.CustomItem", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id1"); + + b.HasOne("Jellyfin.Data.Entities.Episode", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id2"); + + b.HasOne("Jellyfin.Data.Entities.Movie", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id3"); + + b.HasOne("Jellyfin.Data.Entities.Photo", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id4"); + + b.HasOne("Jellyfin.Data.Entities.Track", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id5"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => + { + b.HasOne("Jellyfin.Data.Entities.Season", null) + .WithMany("Episodes") + .HasForeignKey("Episode_Episodes_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => + { + b.HasOne("Jellyfin.Data.Entities.Series", null) + .WithMany("Seasons") + .HasForeignKey("Season_Seasons_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) + .WithMany("Tracks") + .HasForeignKey("Track_Tracks_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Book", null) + .WithMany("BookMetadata") + .HasForeignKey("BookMetadata_BookMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Company", null) + .WithMany("CompanyMetadata") + .HasForeignKey("CompanyMetadata_CompanyMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.CustomItem", null) + .WithMany("CustomItemMetadata") + .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Episode", null) + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Movie", null) + .WithMany("MovieMetadata") + .HasForeignKey("MovieMetadata_MovieMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) + .WithMany("MusicAlbumMetadata") + .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Photo", null) + .WithMany("PhotoMetadata") + .HasForeignKey("PhotoMetadata_PhotoMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Season", null) + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonMetadata_SeasonMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Series", null) + .WithMany("SeriesMetadata") + .HasForeignKey("SeriesMetadata_SeriesMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Track", null) + .WithMany("TrackMetadata") + .HasForeignKey("TrackMetadata_TrackMetadata_Id"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs new file mode 100644 index 0000000000..f6f2f1a81b --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs @@ -0,0 +1,1294 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class InitialSchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "jellyfin"); + + migrationBuilder.CreateTable( + name: "ActivityLog", + 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_ActivityLog", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Collection", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: true), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Collection", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Library", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: false), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Library", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "MetadataProvider", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: false), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MetadataProvider", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Person", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UrlId = table.Column(nullable: false), + Name = table.Column(maxLength: 1024, nullable: false), + SourceId = table.Column(maxLength: 255, nullable: true), + DateAdded = table.Column(nullable: false), + DateModified = table.Column(nullable: false), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Person", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "User", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Username = table.Column(maxLength: 255, nullable: false), + Password = table.Column(maxLength: 65535, nullable: true), + MustUpdatePassword = table.Column(nullable: false), + AudioLanguagePreference = table.Column(maxLength: 255, nullable: false), + AuthenticationProviderId = table.Column(maxLength: 255, nullable: false), + GroupedFolders = table.Column(maxLength: 65535, nullable: true), + InvalidLoginAttemptCount = table.Column(nullable: false), + LatestItemExcludes = table.Column(maxLength: 65535, nullable: true), + LoginAttemptsBeforeLockout = table.Column(nullable: true), + MyMediaExcludes = table.Column(maxLength: 65535, nullable: true), + OrderedViews = table.Column(maxLength: 65535, nullable: true), + SubtitleMode = table.Column(maxLength: 255, nullable: false), + PlayDefaultAudioTrack = table.Column(nullable: false), + SubtitleLanguagePrefernce = table.Column(maxLength: 255, nullable: true), + DisplayMissingEpisodes = table.Column(nullable: true), + DisplayCollectionsView = table.Column(nullable: true), + HidePlayedInLatest = table.Column(nullable: true), + RememberAudioSelections = table.Column(nullable: true), + RememberSubtitleSelections = table.Column(nullable: true), + EnableNextEpisodeAutoPlay = table.Column(nullable: true), + EnableUserPreferenceAccess = table.Column(nullable: true), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "LibraryRoot", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column(maxLength: 65535, nullable: false), + NetworkPath = table.Column(maxLength: 65535, nullable: true), + RowVersion = table.Column(nullable: false), + Library_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LibraryRoot", x => x.Id); + table.ForeignKey( + name: "FK_LibraryRoot_Library_Library_Id", + column: x => x.Library_Id, + principalSchema: "jellyfin", + principalTable: "Library", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Group", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 255, nullable: false), + RowVersion = table.Column(nullable: false), + Group_Groups_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Group", x => x.Id); + table.ForeignKey( + name: "FK_Group_User_Group_Groups_Id", + column: x => x.Group_Groups_Id, + principalSchema: "jellyfin", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "LibraryItem", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UrlId = table.Column(nullable: false), + DateAdded = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + LibraryRoot_Id = table.Column(nullable: true), + Discriminator = table.Column(nullable: false), + EpisodeNumber = table.Column(nullable: true), + Episode_Episodes_Id = table.Column(nullable: true), + SeasonNumber = table.Column(nullable: true), + Season_Seasons_Id = table.Column(nullable: true), + AirsDayOfWeek = table.Column(nullable: true), + AirsTime = table.Column(nullable: true), + FirstAired = table.Column(nullable: true), + TrackNumber = table.Column(nullable: true), + Track_Tracks_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_LibraryItem", x => x.Id); + table.ForeignKey( + name: "FK_LibraryItem_LibraryItem_Episode_Episodes_Id", + column: x => x.Episode_Episodes_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_LibraryItem_LibraryRoot_LibraryRoot_Id", + column: x => x.LibraryRoot_Id, + principalSchema: "jellyfin", + principalTable: "LibraryRoot", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_LibraryItem_LibraryItem_Season_Seasons_Id", + column: x => x.Season_Seasons_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_LibraryItem_LibraryItem_Track_Tracks_Id", + column: x => x.Track_Tracks_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Permission", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column(nullable: false), + Value = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + Permission_GroupPermissions_Id = table.Column(nullable: true), + Permission_Permissions_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Permission", x => x.Id); + table.ForeignKey( + name: "FK_Permission_Group_Permission_GroupPermissions_Id", + column: x => x.Permission_GroupPermissions_Id, + principalSchema: "jellyfin", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Permission_User_Permission_Permissions_Id", + column: x => x.Permission_Permissions_Id, + principalSchema: "jellyfin", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Preference", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column(nullable: false), + Value = table.Column(maxLength: 65535, nullable: false), + RowVersion = table.Column(nullable: false), + Preference_Preferences_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Preference", x => x.Id); + table.ForeignKey( + name: "FK_Preference_Group_Preference_Preferences_Id", + column: x => x.Preference_Preferences_Id, + principalSchema: "jellyfin", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Preference_User_Preference_Preferences_Id", + column: x => x.Preference_Preferences_Id, + principalSchema: "jellyfin", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "ProviderMapping", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ProviderName = table.Column(maxLength: 255, nullable: false), + ProviderSecrets = table.Column(maxLength: 65535, nullable: false), + ProviderData = table.Column(maxLength: 65535, nullable: false), + RowVersion = table.Column(nullable: false), + ProviderMapping_ProviderMappings_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProviderMapping", x => x.Id); + table.ForeignKey( + name: "FK_ProviderMapping_Group_ProviderMapping_ProviderMappings_Id", + column: x => x.ProviderMapping_ProviderMappings_Id, + principalSchema: "jellyfin", + principalTable: "Group", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ProviderMapping_User_ProviderMapping_ProviderMappings_Id", + column: x => x.ProviderMapping_ProviderMappings_Id, + principalSchema: "jellyfin", + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "CollectionItem", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RowVersion = table.Column(nullable: false), + LibraryItem_Id = table.Column(nullable: true), + CollectionItem_Next_Id = table.Column(nullable: true), + CollectionItem_Previous_Id = table.Column(nullable: true), + CollectionItem_CollectionItem_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_CollectionItem", x => x.Id); + table.ForeignKey( + name: "FK_CollectionItem_Collection_CollectionItem_CollectionItem_Id", + column: x => x.CollectionItem_CollectionItem_Id, + principalSchema: "jellyfin", + principalTable: "Collection", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CollectionItem_CollectionItem_CollectionItem_Next_Id", + column: x => x.CollectionItem_Next_Id, + principalSchema: "jellyfin", + principalTable: "CollectionItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CollectionItem_CollectionItem_CollectionItem_Previous_Id", + column: x => x.CollectionItem_Previous_Id, + principalSchema: "jellyfin", + principalTable: "CollectionItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CollectionItem_LibraryItem_LibraryItem_Id", + column: x => x.LibraryItem_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Release", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: false), + RowVersion = table.Column(nullable: false), + Release_Releases_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Release", x => x.Id); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id1", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id2", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id3", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id4", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Release_LibraryItem_Release_Releases_Id5", + column: x => x.Release_Releases_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Chapter", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: true), + Language = table.Column(maxLength: 3, nullable: false), + TimeStart = table.Column(nullable: false), + TimeEnd = table.Column(nullable: true), + RowVersion = table.Column(nullable: false), + Chapter_Chapters_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Chapter", x => x.Id); + table.ForeignKey( + name: "FK_Chapter_Release_Chapter_Chapters_Id", + column: x => x.Chapter_Chapters_Id, + principalSchema: "jellyfin", + principalTable: "Release", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "MediaFile", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column(maxLength: 65535, nullable: false), + Kind = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + MediaFile_MediaFiles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaFile", x => x.Id); + table.ForeignKey( + name: "FK_MediaFile_Release_MediaFile_MediaFiles_Id", + column: x => x.MediaFile_MediaFiles_Id, + principalSchema: "jellyfin", + principalTable: "Release", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "MediaFileStream", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + StreamNumber = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + MediaFileStream_MediaFileStreams_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaFileStream", x => x.Id); + table.ForeignKey( + name: "FK_MediaFileStream_MediaFile_MediaFileStream_MediaFileStreams_Id", + column: x => x.MediaFileStream_MediaFileStreams_Id, + principalSchema: "jellyfin", + principalTable: "MediaFile", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "PersonRole", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Role = table.Column(maxLength: 1024, nullable: true), + Type = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + Person_Id = table.Column(nullable: true), + Artwork_Artwork_Id = table.Column(nullable: true), + PersonRole_PersonRoles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_PersonRole", x => x.Id); + table.ForeignKey( + name: "FK_PersonRole_Person_Person_Id", + column: x => x.Person_Id, + principalSchema: "jellyfin", + principalTable: "Person", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Metadata", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(maxLength: 1024, nullable: false), + OriginalTitle = table.Column(maxLength: 1024, nullable: true), + SortTitle = table.Column(maxLength: 1024, nullable: true), + Language = table.Column(maxLength: 3, nullable: false), + ReleaseDate = table.Column(nullable: true), + DateAdded = table.Column(nullable: false), + DateModified = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + Discriminator = table.Column(nullable: false), + ISBN = table.Column(nullable: true), + BookMetadata_BookMetadata_Id = table.Column(nullable: true), + Description = table.Column(maxLength: 65535, nullable: true), + Headquarters = table.Column(maxLength: 255, nullable: true), + Country = table.Column(maxLength: 2, nullable: true), + Homepage = table.Column(maxLength: 1024, nullable: true), + CompanyMetadata_CompanyMetadata_Id = table.Column(nullable: true), + CustomItemMetadata_CustomItemMetadata_Id = table.Column(nullable: true), + Outline = table.Column(maxLength: 1024, nullable: true), + Plot = table.Column(maxLength: 65535, nullable: true), + Tagline = table.Column(maxLength: 1024, nullable: true), + EpisodeMetadata_EpisodeMetadata_Id = table.Column(nullable: true), + MovieMetadata_Outline = table.Column(maxLength: 1024, nullable: true), + MovieMetadata_Plot = table.Column(maxLength: 65535, nullable: true), + MovieMetadata_Tagline = table.Column(maxLength: 1024, nullable: true), + MovieMetadata_Country = table.Column(maxLength: 2, nullable: true), + MovieMetadata_MovieMetadata_Id = table.Column(nullable: true), + Barcode = table.Column(maxLength: 255, nullable: true), + LabelNumber = table.Column(maxLength: 255, nullable: true), + MusicAlbumMetadata_Country = table.Column(maxLength: 2, nullable: true), + MusicAlbumMetadata_MusicAlbumMetadata_Id = table.Column(nullable: true), + PhotoMetadata_PhotoMetadata_Id = table.Column(nullable: true), + SeasonMetadata_Outline = table.Column(maxLength: 1024, nullable: true), + SeasonMetadata_SeasonMetadata_Id = table.Column(nullable: true), + SeriesMetadata_Outline = table.Column(maxLength: 1024, nullable: true), + SeriesMetadata_Plot = table.Column(maxLength: 65535, nullable: true), + SeriesMetadata_Tagline = table.Column(maxLength: 1024, nullable: true), + SeriesMetadata_Country = table.Column(maxLength: 2, nullable: true), + SeriesMetadata_SeriesMetadata_Id = table.Column(nullable: true), + TrackMetadata_TrackMetadata_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Metadata", x => x.Id); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_BookMetadata_BookMetadata_Id", + column: x => x.BookMetadata_BookMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_CustomItemMetadata_CustomItemMetadata_Id", + column: x => x.CustomItemMetadata_CustomItemMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_EpisodeMetadata_EpisodeMetadata_Id", + column: x => x.EpisodeMetadata_EpisodeMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_MovieMetadata_MovieMetadata_Id", + column: x => x.MovieMetadata_MovieMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_MusicAlbumMetadata_MusicAlbumMetadata_Id", + column: x => x.MusicAlbumMetadata_MusicAlbumMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_PhotoMetadata_PhotoMetadata_Id", + column: x => x.PhotoMetadata_PhotoMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_SeasonMetadata_SeasonMetadata_Id", + column: x => x.SeasonMetadata_SeasonMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_SeriesMetadata_SeriesMetadata_Id", + column: x => x.SeriesMetadata_SeriesMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Metadata_LibraryItem_TrackMetadata_TrackMetadata_Id", + column: x => x.TrackMetadata_TrackMetadata_Id, + principalSchema: "jellyfin", + principalTable: "LibraryItem", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Artwork", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column(maxLength: 65535, nullable: false), + Kind = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + PersonRole_PersonRoles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Artwork", x => x.Id); + table.ForeignKey( + name: "FK_Artwork_Metadata_PersonRole_PersonRoles_Id", + column: x => x.PersonRole_PersonRoles_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Company", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RowVersion = table.Column(nullable: false), + Company_Parent_Id = table.Column(nullable: true), + Company_Labels_Id = table.Column(nullable: true), + Company_Networks_Id = table.Column(nullable: true), + Company_Publishers_Id = table.Column(nullable: true), + Company_Studios_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Company", x => x.Id); + table.ForeignKey( + name: "FK_Company_Metadata_Company_Labels_Id", + column: x => x.Company_Labels_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Company_Metadata_Company_Networks_Id", + column: x => x.Company_Networks_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Company_Company_Company_Parent_Id", + column: x => x.Company_Parent_Id, + principalSchema: "jellyfin", + principalTable: "Company", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Company_Metadata_Company_Publishers_Id", + column: x => x.Company_Publishers_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Company_Metadata_Company_Studios_Id", + column: x => x.Company_Studios_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Genre", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 255, nullable: false), + RowVersion = table.Column(nullable: false), + PersonRole_PersonRoles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Genre", x => x.Id); + table.ForeignKey( + name: "FK_Genre_Metadata_PersonRole_PersonRoles_Id", + column: x => x.PersonRole_PersonRoles_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "MetadataProviderId", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ProviderId = table.Column(maxLength: 255, nullable: false), + RowVersion = table.Column(nullable: false), + MetadataProvider_Id = table.Column(nullable: true), + MetadataProviderId_Sources_Id = table.Column(nullable: true), + PersonRole_PersonRoles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MetadataProviderId", x => x.Id); + table.ForeignKey( + name: "FK_MetadataProviderId_Person_MetadataProviderId_Sources_Id", + column: x => x.MetadataProviderId_Sources_Id, + principalSchema: "jellyfin", + principalTable: "Person", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_MetadataProviderId_PersonRole_MetadataProviderId_Sources_Id", + column: x => x.MetadataProviderId_Sources_Id, + principalSchema: "jellyfin", + principalTable: "PersonRole", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_MetadataProviderId_MetadataProvider_MetadataProvider_Id", + column: x => x.MetadataProvider_Id, + principalSchema: "jellyfin", + principalTable: "MetadataProvider", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_MetadataProviderId_Metadata_PersonRole_PersonRoles_Id", + column: x => x.PersonRole_PersonRoles_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "RatingSource", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(maxLength: 1024, nullable: true), + MaximumValue = table.Column(nullable: false), + MinimumValue = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + MetadataProviderId_Source_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RatingSource", x => x.Id); + table.ForeignKey( + name: "FK_RatingSource_MetadataProviderId_MetadataProviderId_Source_Id", + column: x => x.MetadataProviderId_Source_Id, + principalSchema: "jellyfin", + principalTable: "MetadataProviderId", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Rating", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Value = table.Column(nullable: false), + Votes = table.Column(nullable: true), + RowVersion = table.Column(nullable: false), + RatingSource_RatingType_Id = table.Column(nullable: true), + PersonRole_PersonRoles_Id = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Rating", x => x.Id); + table.ForeignKey( + name: "FK_Rating_Metadata_PersonRole_PersonRoles_Id", + column: x => x.PersonRole_PersonRoles_Id, + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Rating_RatingSource_RatingSource_RatingType_Id", + column: x => x.RatingSource_RatingType_Id, + principalSchema: "jellyfin", + principalTable: "RatingSource", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Artwork_Kind", + schema: "jellyfin", + table: "Artwork", + column: "Kind"); + + migrationBuilder.CreateIndex( + name: "IX_Artwork_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "Artwork", + column: "PersonRole_PersonRoles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Chapter_Chapter_Chapters_Id", + schema: "jellyfin", + table: "Chapter", + column: "Chapter_Chapters_Id"); + + migrationBuilder.CreateIndex( + name: "IX_CollectionItem_CollectionItem_CollectionItem_Id", + schema: "jellyfin", + table: "CollectionItem", + column: "CollectionItem_CollectionItem_Id"); + + migrationBuilder.CreateIndex( + name: "IX_CollectionItem_CollectionItem_Next_Id", + schema: "jellyfin", + table: "CollectionItem", + column: "CollectionItem_Next_Id"); + + migrationBuilder.CreateIndex( + name: "IX_CollectionItem_CollectionItem_Previous_Id", + schema: "jellyfin", + table: "CollectionItem", + column: "CollectionItem_Previous_Id"); + + migrationBuilder.CreateIndex( + name: "IX_CollectionItem_LibraryItem_Id", + schema: "jellyfin", + table: "CollectionItem", + column: "LibraryItem_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Company_Company_Labels_Id", + schema: "jellyfin", + table: "Company", + column: "Company_Labels_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Company_Company_Networks_Id", + schema: "jellyfin", + table: "Company", + column: "Company_Networks_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Company_Company_Parent_Id", + schema: "jellyfin", + table: "Company", + column: "Company_Parent_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Company_Company_Publishers_Id", + schema: "jellyfin", + table: "Company", + column: "Company_Publishers_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Company_Company_Studios_Id", + schema: "jellyfin", + table: "Company", + column: "Company_Studios_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Genre_Name", + schema: "jellyfin", + table: "Genre", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Genre_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "Genre", + column: "PersonRole_PersonRoles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Group_Group_Groups_Id", + schema: "jellyfin", + table: "Group", + column: "Group_Groups_Id"); + + migrationBuilder.CreateIndex( + name: "IX_LibraryItem_Episode_Episodes_Id", + schema: "jellyfin", + table: "LibraryItem", + column: "Episode_Episodes_Id"); + + migrationBuilder.CreateIndex( + name: "IX_LibraryItem_LibraryRoot_Id", + schema: "jellyfin", + table: "LibraryItem", + column: "LibraryRoot_Id"); + + migrationBuilder.CreateIndex( + name: "IX_LibraryItem_UrlId", + schema: "jellyfin", + table: "LibraryItem", + column: "UrlId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_LibraryItem_Season_Seasons_Id", + schema: "jellyfin", + table: "LibraryItem", + column: "Season_Seasons_Id"); + + migrationBuilder.CreateIndex( + name: "IX_LibraryItem_Track_Tracks_Id", + schema: "jellyfin", + table: "LibraryItem", + column: "Track_Tracks_Id"); + + migrationBuilder.CreateIndex( + name: "IX_LibraryRoot_Library_Id", + schema: "jellyfin", + table: "LibraryRoot", + column: "Library_Id"); + + migrationBuilder.CreateIndex( + name: "IX_MediaFile_MediaFile_MediaFiles_Id", + schema: "jellyfin", + table: "MediaFile", + column: "MediaFile_MediaFiles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_MediaFileStream_MediaFileStream_MediaFileStreams_Id", + schema: "jellyfin", + table: "MediaFileStream", + column: "MediaFileStream_MediaFileStreams_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_BookMetadata_BookMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "BookMetadata_BookMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_CompanyMetadata_CompanyMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "CompanyMetadata_CompanyMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_CustomItemMetadata_CustomItemMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "CustomItemMetadata_CustomItemMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_EpisodeMetadata_EpisodeMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "EpisodeMetadata_EpisodeMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_MovieMetadata_MovieMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "MovieMetadata_MovieMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_MusicAlbumMetadata_MusicAlbumMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "MusicAlbumMetadata_MusicAlbumMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_PhotoMetadata_PhotoMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "PhotoMetadata_PhotoMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_SeasonMetadata_SeasonMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "SeasonMetadata_SeasonMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_SeriesMetadata_SeriesMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "SeriesMetadata_SeriesMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Metadata_TrackMetadata_TrackMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "TrackMetadata_TrackMetadata_Id"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataProviderId_MetadataProviderId_Sources_Id", + schema: "jellyfin", + table: "MetadataProviderId", + column: "MetadataProviderId_Sources_Id"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataProviderId_MetadataProvider_Id", + schema: "jellyfin", + table: "MetadataProviderId", + column: "MetadataProvider_Id"); + + migrationBuilder.CreateIndex( + name: "IX_MetadataProviderId_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "MetadataProviderId", + column: "PersonRole_PersonRoles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Permission_Permission_GroupPermissions_Id", + schema: "jellyfin", + table: "Permission", + column: "Permission_GroupPermissions_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Permission_Permission_Permissions_Id", + schema: "jellyfin", + table: "Permission", + column: "Permission_Permissions_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PersonRole_Artwork_Artwork_Id", + schema: "jellyfin", + table: "PersonRole", + column: "Artwork_Artwork_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PersonRole_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "PersonRole", + column: "PersonRole_PersonRoles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_PersonRole_Person_Id", + schema: "jellyfin", + table: "PersonRole", + column: "Person_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Preference_Preference_Preferences_Id", + schema: "jellyfin", + table: "Preference", + column: "Preference_Preferences_Id"); + + migrationBuilder.CreateIndex( + name: "IX_ProviderMapping_ProviderMapping_ProviderMappings_Id", + schema: "jellyfin", + table: "ProviderMapping", + column: "ProviderMapping_ProviderMappings_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Rating_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "Rating", + column: "PersonRole_PersonRoles_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Rating_RatingSource_RatingType_Id", + schema: "jellyfin", + table: "Rating", + column: "RatingSource_RatingType_Id"); + + migrationBuilder.CreateIndex( + name: "IX_RatingSource_MetadataProviderId_Source_Id", + schema: "jellyfin", + table: "RatingSource", + column: "MetadataProviderId_Source_Id"); + + migrationBuilder.CreateIndex( + name: "IX_Release_Release_Releases_Id", + schema: "jellyfin", + table: "Release", + column: "Release_Releases_Id"); + + migrationBuilder.AddForeignKey( + name: "FK_PersonRole_Metadata_PersonRole_PersonRoles_Id", + schema: "jellyfin", + table: "PersonRole", + column: "PersonRole_PersonRoles_Id", + principalSchema: "jellyfin", + principalTable: "Metadata", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_PersonRole_Artwork_Artwork_Artwork_Id", + schema: "jellyfin", + table: "PersonRole", + column: "Artwork_Artwork_Id", + principalSchema: "jellyfin", + principalTable: "Artwork", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Metadata_Company_CompanyMetadata_CompanyMetadata_Id", + schema: "jellyfin", + table: "Metadata", + column: "CompanyMetadata_CompanyMetadata_Id", + principalSchema: "jellyfin", + principalTable: "Company", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Company_Metadata_Company_Labels_Id", + schema: "jellyfin", + table: "Company"); + + migrationBuilder.DropForeignKey( + name: "FK_Company_Metadata_Company_Networks_Id", + schema: "jellyfin", + table: "Company"); + + migrationBuilder.DropForeignKey( + name: "FK_Company_Metadata_Company_Publishers_Id", + schema: "jellyfin", + table: "Company"); + + migrationBuilder.DropForeignKey( + name: "FK_Company_Metadata_Company_Studios_Id", + schema: "jellyfin", + table: "Company"); + + migrationBuilder.DropTable( + name: "ActivityLog", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Chapter", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "CollectionItem", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Genre", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "MediaFileStream", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Permission", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Preference", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "ProviderMapping", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Rating", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Collection", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "MediaFile", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Group", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "RatingSource", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Release", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "User", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "MetadataProviderId", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "PersonRole", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "MetadataProvider", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Artwork", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Person", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Metadata", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "LibraryItem", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Company", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "LibraryRoot", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Library", + 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..8cdd101af4 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -0,0 +1,1511 @@ +#pragma warning disable CS1591 + +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +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("ActivityLog"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Kind"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("Artwork"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Chapter_Chapters_Id") + .HasColumnType("INTEGER"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("TimeEnd") + .HasColumnType("INTEGER"); + + b.Property("TimeStart") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Chapter_Chapters_Id"); + + b.ToTable("Chapter"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_CollectionItem_Id") + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_Next_Id") + .HasColumnType("INTEGER"); + + b.Property("CollectionItem_Previous_Id") + .HasColumnType("INTEGER"); + + b.Property("LibraryItem_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CollectionItem_CollectionItem_Id"); + + b.HasIndex("CollectionItem_Next_Id"); + + b.HasIndex("CollectionItem_Previous_Id"); + + b.HasIndex("LibraryItem_Id"); + + b.ToTable("CollectionItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Company_Labels_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Networks_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Parent_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Publishers_Id") + .HasColumnType("INTEGER"); + + b.Property("Company_Studios_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Company_Labels_Id"); + + b.HasIndex("Company_Networks_Id"); + + b.HasIndex("Company_Parent_Id"); + + b.HasIndex("Company_Publishers_Id"); + + b.HasIndex("Company_Studios_Id"); + + b.ToTable("Company"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("Genre"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Group_Groups_Id") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Group_Groups_Id"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LibraryRoot_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryRoot_Id"); + + b.HasIndex("UrlId") + .IsUnique(); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator("Discriminator").HasValue("LibraryItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Library_Id") + .HasColumnType("INTEGER"); + + b.Property("NetworkPath") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Library_Id"); + + b.ToTable("LibraryRoot"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("MediaFile_MediaFiles_Id") + .HasColumnType("INTEGER"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFile_MediaFiles_Id"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MediaFileStream_MediaFileStreams_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("StreamNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileStream_MediaFileStreams_Id"); + + b.ToTable("MediaFileStream"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateModified") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property("OriginalTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SortTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator("Discriminator").HasValue("Metadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MetadataProvider"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MetadataProviderId_Sources_Id") + .HasColumnType("INTEGER"); + + b.Property("MetadataProvider_Id") + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Sources_Id"); + + b.HasIndex("MetadataProvider_Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.ToTable("MetadataProviderId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_GroupPermissions_Id") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_GroupPermissions_Id"); + + b.HasIndex("Permission_Permissions_Id"); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateAdded") + .HasColumnType("TEXT"); + + b.Property("DateModified") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SourceId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Artwork_Artwork_Id") + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("Person_Id") + .HasColumnType("INTEGER"); + + b.Property("Role") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Artwork_Artwork_Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.HasIndex("Person_Id"); + + b.ToTable("PersonRole"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Id"); + + b.ToTable("Preference"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ProviderData") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("ProviderMapping_ProviderMappings_Id") + .HasColumnType("INTEGER"); + + b.Property("ProviderName") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("ProviderSecrets") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderMapping_ProviderMappings_Id"); + + b.ToTable("ProviderMapping"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property("RatingSource_RatingType_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("REAL"); + + b.Property("Votes") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("PersonRole_PersonRoles_Id"); + + b.HasIndex("RatingSource_RatingType_Id"); + + b.ToTable("Rating"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MaximumValue") + .HasColumnType("REAL"); + + b.Property("MetadataProviderId_Source_Id") + .HasColumnType("INTEGER"); + + b.Property("MinimumValue") + .HasColumnType("REAL"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Source_Id"); + + b.ToTable("RatingSource"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Release_Releases_Id") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Release_Releases_Id"); + + b.ToTable("Release"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AudioLanguagePreference") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("GroupedFolders") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LatestItemExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("MyMediaExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("OrderedViews") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePrefernce") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("SubtitleMode") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Book", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Book"); + + b.HasDiscriminator().HasValue("Book"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItem", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator().HasValue("CustomItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property("Episode_Episodes_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Episode_Episodes_Id"); + + b.ToTable("Episode"); + + b.HasDiscriminator().HasValue("Episode"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Movie", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Movie"); + + b.HasDiscriminator().HasValue("Movie"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbum", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("MusicAlbum"); + + b.HasDiscriminator().HasValue("MusicAlbum"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Photo", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.ToTable("Photo"); + + b.HasDiscriminator().HasValue("Photo"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property("Season_Seasons_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Season_Seasons_Id"); + + b.ToTable("Season"); + + b.HasDiscriminator().HasValue("Season"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Series", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("AirsDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("AirsTime") + .HasColumnType("TEXT"); + + b.Property("FirstAired") + .HasColumnType("TEXT"); + + b.ToTable("Series"); + + b.HasDiscriminator().HasValue("Series"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => + { + b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); + + b.Property("TrackNumber") + .HasColumnType("INTEGER"); + + b.Property("Track_Tracks_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("Track_Tracks_Id"); + + b.ToTable("Track"); + + b.HasDiscriminator().HasValue("Track"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("BookMetadata_BookMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("ISBN") + .HasColumnType("INTEGER"); + + b.HasIndex("BookMetadata_BookMetadata_Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator().HasValue("BookMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("CompanyMetadata_CompanyMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("Description") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Headquarters") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Homepage") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("CompanyMetadata_CompanyMetadata_Id"); + + b.ToTable("CompanyMetadata"); + + b.HasDiscriminator().HasValue("CompanyMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("CustomItemMetadata_CustomItemMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("CustomItemMetadata_CustomItemMetadata_Id"); + + b.ToTable("CustomItemMetadata"); + + b.HasDiscriminator().HasValue("CustomItemMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("EpisodeMetadata_EpisodeMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("EpisodeMetadata_EpisodeMetadata_Id"); + + b.ToTable("EpisodeMetadata"); + + b.HasDiscriminator().HasValue("EpisodeMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Country") + .HasColumnName("MovieMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("MovieMetadata_MovieMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Outline") + .HasColumnName("MovieMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnName("MovieMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("Tagline") + .HasColumnName("MovieMetadata_Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("MovieMetadata_MovieMetadata_Id"); + + b.ToTable("MovieMetadata"); + + b.HasDiscriminator().HasValue("MovieMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Barcode") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("Country") + .HasColumnName("MusicAlbumMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("LabelNumber") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("MusicAlbumMetadata_MusicAlbumMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("MusicAlbumMetadata_MusicAlbumMetadata_Id"); + + b.ToTable("MusicAlbumMetadata"); + + b.HasDiscriminator().HasValue("MusicAlbumMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("PhotoMetadata_PhotoMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("PhotoMetadata_PhotoMetadata_Id"); + + b.ToTable("PhotoMetadata"); + + b.HasDiscriminator().HasValue("PhotoMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Outline") + .HasColumnName("SeasonMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("SeasonMetadata_SeasonMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("SeasonMetadata_SeasonMetadata_Id"); + + b.ToTable("SeasonMetadata"); + + b.HasDiscriminator().HasValue("SeasonMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("Country") + .HasColumnName("SeriesMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property("Outline") + .HasColumnName("SeriesMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property("Plot") + .HasColumnName("SeriesMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("SeriesMetadata_SeriesMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property("Tagline") + .HasColumnName("SeriesMetadata_Tagline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasIndex("SeriesMetadata_SeriesMetadata_Id"); + + b.ToTable("SeriesMetadata"); + + b.HasDiscriminator().HasValue("SeriesMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => + { + b.HasBaseType("Jellyfin.Data.Entities.Metadata"); + + b.Property("TrackMetadata_TrackMetadata_Id") + .HasColumnType("INTEGER"); + + b.HasIndex("TrackMetadata_TrackMetadata_Id"); + + b.ToTable("TrackMetadata"); + + b.HasDiscriminator().HasValue("TrackMetadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Artwork") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => + { + b.HasOne("Jellyfin.Data.Entities.Release", null) + .WithMany("Chapters") + .HasForeignKey("Chapter_Chapters_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.HasOne("Jellyfin.Data.Entities.Collection", null) + .WithMany("CollectionItem") + .HasForeignKey("CollectionItem_CollectionItem_Id"); + + b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Next") + .WithMany() + .HasForeignKey("CollectionItem_Next_Id"); + + b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Previous") + .WithMany() + .HasForeignKey("CollectionItem_Previous_Id"); + + b.HasOne("Jellyfin.Data.Entities.LibraryItem", "LibraryItem") + .WithMany() + .HasForeignKey("LibraryItem_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbumMetadata", null) + .WithMany("Labels") + .HasForeignKey("Company_Labels_Id"); + + b.HasOne("Jellyfin.Data.Entities.SeriesMetadata", null) + .WithMany("Networks") + .HasForeignKey("Company_Networks_Id"); + + b.HasOne("Jellyfin.Data.Entities.Company", "Parent") + .WithMany() + .HasForeignKey("Company_Parent_Id"); + + b.HasOne("Jellyfin.Data.Entities.BookMetadata", null) + .WithMany("Publishers") + .HasForeignKey("Company_Publishers_Id"); + + b.HasOne("Jellyfin.Data.Entities.MovieMetadata", null) + .WithMany("Studios") + .HasForeignKey("Company_Studios_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Genres") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Groups") + .HasForeignKey("Group_Groups_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.HasOne("Jellyfin.Data.Entities.LibraryRoot", "LibraryRoot") + .WithMany() + .HasForeignKey("LibraryRoot_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.HasOne("Jellyfin.Data.Entities.Library", "Library") + .WithMany() + .HasForeignKey("Library_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.HasOne("Jellyfin.Data.Entities.Release", null) + .WithMany("MediaFiles") + .HasForeignKey("MediaFile_MediaFiles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.HasOne("Jellyfin.Data.Entities.MediaFile", null) + .WithMany("MediaFileStreams") + .HasForeignKey("MediaFileStream_MediaFileStreams_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.HasOne("Jellyfin.Data.Entities.Person", null) + .WithMany("Sources") + .HasForeignKey("MetadataProviderId_Sources_Id"); + + b.HasOne("Jellyfin.Data.Entities.PersonRole", null) + .WithMany("Sources") + .HasForeignKey("MetadataProviderId_Sources_Id"); + + b.HasOne("Jellyfin.Data.Entities.MetadataProvider", "MetadataProvider") + .WithMany() + .HasForeignKey("MetadataProvider_Id"); + + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Sources") + .HasForeignKey("PersonRole_PersonRoles_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("GroupPermissions") + .HasForeignKey("Permission_GroupPermissions_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.HasOne("Jellyfin.Data.Entities.Artwork", "Artwork") + .WithMany() + .HasForeignKey("Artwork_Artwork_Id"); + + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("PersonRoles") + .HasForeignKey("PersonRole_PersonRoles_Id"); + + b.HasOne("Jellyfin.Data.Entities.Person", "Person") + .WithMany() + .HasForeignKey("Person_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => + { + b.HasOne("Jellyfin.Data.Entities.Group", null) + .WithMany("ProviderMappings") + .HasForeignKey("ProviderMapping_ProviderMappings_Id"); + + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("ProviderMappings") + .HasForeignKey("ProviderMapping_ProviderMappings_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.HasOne("Jellyfin.Data.Entities.Metadata", null) + .WithMany("Ratings") + .HasForeignKey("PersonRole_PersonRoles_Id"); + + b.HasOne("Jellyfin.Data.Entities.RatingSource", "RatingType") + .WithMany() + .HasForeignKey("RatingSource_RatingType_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => + { + b.HasOne("Jellyfin.Data.Entities.MetadataProviderId", "Source") + .WithMany() + .HasForeignKey("MetadataProviderId_Source_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.HasOne("Jellyfin.Data.Entities.Book", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id"); + + b.HasOne("Jellyfin.Data.Entities.CustomItem", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id1"); + + b.HasOne("Jellyfin.Data.Entities.Episode", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id2"); + + b.HasOne("Jellyfin.Data.Entities.Movie", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id3"); + + b.HasOne("Jellyfin.Data.Entities.Photo", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id4"); + + b.HasOne("Jellyfin.Data.Entities.Track", null) + .WithMany("Releases") + .HasForeignKey("Release_Releases_Id") + .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id5"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => + { + b.HasOne("Jellyfin.Data.Entities.Season", null) + .WithMany("Episodes") + .HasForeignKey("Episode_Episodes_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => + { + b.HasOne("Jellyfin.Data.Entities.Series", null) + .WithMany("Seasons") + .HasForeignKey("Season_Seasons_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) + .WithMany("Tracks") + .HasForeignKey("Track_Tracks_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Book", null) + .WithMany("BookMetadata") + .HasForeignKey("BookMetadata_BookMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Company", null) + .WithMany("CompanyMetadata") + .HasForeignKey("CompanyMetadata_CompanyMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.CustomItem", null) + .WithMany("CustomItemMetadata") + .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Episode", null) + .WithMany("EpisodeMetadata") + .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Movie", null) + .WithMany("MovieMetadata") + .HasForeignKey("MovieMetadata_MovieMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) + .WithMany("MusicAlbumMetadata") + .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Photo", null) + .WithMany("PhotoMetadata") + .HasForeignKey("PhotoMetadata_PhotoMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Season", null) + .WithMany("SeasonMetadata") + .HasForeignKey("SeasonMetadata_SeasonMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Series", null) + .WithMany("SeriesMetadata") + .HasForeignKey("SeriesMetadata_SeriesMetadata_Id"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => + { + b.HasOne("Jellyfin.Data.Entities.Track", null) + .WithMany("TrackMetadata") + .HasForeignKey("TrackMetadata_TrackMetadata_Id"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 88114d9994..4194070aa9 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -13,6 +13,9 @@ true true enable + + + True @@ -41,6 +44,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index b5ea04dcac..82e3045862 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -16,7 +16,8 @@ namespace Jellyfin.Server.Migrations internal static readonly IMigrationRoutine[] Migrations = { new Routines.DisableTranscodingThrottling(), - new Routines.CreateUserLoggingConfigFile() + new Routines.CreateUserLoggingConfigFile(), + new Routines.MigrateActivityLogDb() }; /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs new file mode 100644 index 0000000000..9f1f5b92eb --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -0,0 +1,109 @@ +#pragma warning disable CS1591 + +using System; +using System.IO; +using Emby.Server.Implementations.Data; +using Jellyfin.Data.Entities; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using SQLitePCL.pretty; + +namespace Jellyfin.Server.Migrations.Routines +{ + public class MigrateActivityLogDb : IMigrationRoutine + { + private const string DbFilename = "activitylog.db"; + + public Guid Id => Guid.Parse("3793eb59-bc8c-456c-8b9f-bd5a62a42978"); + + public string Name => "MigrateActivityLogDatabase"; + + public void Perform(CoreAppHost host, ILogger logger) + { + var dataPath = host.ServerConfigurationManager.ApplicationPaths.DataPath; + using (var connection = SQLite3.Open( + Path.Combine(dataPath, DbFilename), + ConnectionFlags.ReadOnly, + null)) + { + logger.LogInformation("Migrating the database may take a while, do not stop Jellyfin."); + using var dbContext = host.ServiceProvider.GetService(); + + 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(); + + foreach (var entry in queryResult) + { + var newEntry = new ActivityLog( + entry[1].ToString(), + entry[4].ToString(), + entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString()), + entry[7].ReadDateTime(), + ParseLogLevel(entry[8].ToString())); + + 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(); + } + + dbContext.ActivityLogs.Add(newEntry); + dbContext.SaveChanges(); + } + } + + try + { + File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old")); + } + catch (IOException e) + { + logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); + } + } + + private LogLevel ParseLogLevel(string entry) + { + if (string.Equals(entry, "Debug", StringComparison.OrdinalIgnoreCase)) + { + return LogLevel.Debug; + } + + if (string.Equals(entry, "Information", StringComparison.OrdinalIgnoreCase) + || string.Equals(entry, "Info", StringComparison.OrdinalIgnoreCase)) + { + return LogLevel.Information; + } + + if (string.Equals(entry, "Warning", StringComparison.OrdinalIgnoreCase) + || string.Equals(entry, "Warn", StringComparison.OrdinalIgnoreCase)) + { + return LogLevel.Warning; + } + + if (string.Equals(entry, "Error", StringComparison.OrdinalIgnoreCase)) + { + return LogLevel.Error; + } + + return LogLevel.Trace; + } + } +} diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index a54640b2fd..997b1c45a8 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -759,13 +759,14 @@ 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, + DateTime.UtcNow, + LogLevel.Trace) { - 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/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index f95fa7ca0b..0a5fc9433b 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -53,7 +53,7 @@ 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 result = _activityManager.GetPagedResult(request.StartIndex, request.Limit); return ToOptimizedResult(result); } 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..6742dc8fc4 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -1,6 +1,10 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; @@ -10,10 +14,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, IEnumerable> 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/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b41d0af1d1..5c6e313e07 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -37,6 +37,9 @@ + + + ../jellyfin.ruleset diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a1dbe80476..6d01b0dcde 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.3 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}" EndProject @@ -46,23 +46,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution 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}" 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -114,10 +116,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,10 +180,22 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DF194677-DFD3-42AF-9F75-D44D5A416478} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {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} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} EndGlobalSection @@ -207,12 +217,4 @@ Global $0.DotNetNamingPolicy = $2 $2.DirectoryNamespaceAssociation = PrefixedHierarchical EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {DF194677-DFD3-42AF-9F75-D44D5A416478} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {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} - EndGlobalSection EndGlobal From 37dcbfbc3980ed962c28fe7f1ee9d8a78f65ec19 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 2 May 2020 19:26:24 -0400 Subject: [PATCH 048/137] Update code to only add implemented parts of the schema --- Jellyfin.Server.Implementations/JellyfinDb.cs | 8 +- .../20200430215054_InitialSchema.Designer.cs | 1513 ----------------- .../20200430215054_InitialSchema.cs | 1294 -------------- .../20200502231229_InitialSchema.Designer.cs | 73 + .../20200502231229_InitialSchema.cs | 46 + .../Migrations/DesignTimeJellyfinDbFactory.cs | 3 + .../Migrations/JellyfinDbModelSnapshot.cs | 1445 +--------------- 7 files changed, 127 insertions(+), 4255 deletions(-) delete mode 100644 Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs delete mode 100644 Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 9c1a23877b..6fc8d251b8 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -16,7 +16,7 @@ namespace Jellyfin.Server.Implementations public partial class JellyfinDb : DbContext { public virtual DbSet ActivityLogs { get; set; } - public virtual DbSet Artwork { 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; } @@ -63,7 +63,7 @@ namespace Jellyfin.Server.Implementations public virtual DbSet SeriesMetadata { get; set; } public virtual DbSet Tracks { get; set; } public virtual DbSet TrackMetadata { get; set; } - public virtual DbSet Users { get; set; } + public virtual DbSet Users { get; set; } */ /// /// Gets or sets the default connection string. @@ -94,13 +94,13 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); - modelBuilder.Entity().HasIndex(t => t.Kind); + /*modelBuilder.Entity().HasIndex(t => t.Kind); modelBuilder.Entity().HasIndex(t => t.Name) .IsUnique(); modelBuilder.Entity().HasIndex(t => t.UrlId) - .IsUnique(); + .IsUnique();*/ OnModelCreatedImpl(modelBuilder); } diff --git a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs deleted file mode 100644 index 3fb0fd51e1..0000000000 --- a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.Designer.cs +++ /dev/null @@ -1,1513 +0,0 @@ -#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("20200430215054_InitialSchema")] - partial class InitialSchema - { - 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("ActivityLog"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Kind"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("Artwork"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Chapter_Chapters_Id") - .HasColumnType("INTEGER"); - - b.Property("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("TimeEnd") - .HasColumnType("INTEGER"); - - b.Property("TimeStart") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Chapter_Chapters_Id"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Collection"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_CollectionItem_Id") - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_Next_Id") - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_Previous_Id") - .HasColumnType("INTEGER"); - - b.Property("LibraryItem_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("CollectionItem_CollectionItem_Id"); - - b.HasIndex("CollectionItem_Next_Id"); - - b.HasIndex("CollectionItem_Previous_Id"); - - b.HasIndex("LibraryItem_Id"); - - b.ToTable("CollectionItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Company_Labels_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Networks_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Parent_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Publishers_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Studios_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Company_Labels_Id"); - - b.HasIndex("Company_Networks_Id"); - - b.HasIndex("Company_Parent_Id"); - - b.HasIndex("Company_Publishers_Id"); - - b.HasIndex("Company_Studios_Id"); - - b.ToTable("Company"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("Genre"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Group_Groups_Id") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Group_Groups_Id"); - - b.ToTable("Group"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Library"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LibraryRoot_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("LibraryRoot_Id"); - - b.HasIndex("UrlId") - .IsUnique(); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator("Discriminator").HasValue("LibraryItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Library_Id") - .HasColumnType("INTEGER"); - - b.Property("NetworkPath") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Library_Id"); - - b.ToTable("LibraryRoot"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("MediaFile_MediaFiles_Id") - .HasColumnType("INTEGER"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFile_MediaFiles_Id"); - - b.ToTable("MediaFile"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MediaFileStream_MediaFileStreams_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("StreamNumber") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileStream_MediaFileStreams_Id"); - - b.ToTable("MediaFileStream"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("DateModified") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property("OriginalTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SortTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Title") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasKey("Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator("Discriminator").HasValue("Metadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("MetadataProvider"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MetadataProviderId_Sources_Id") - .HasColumnType("INTEGER"); - - b.Property("MetadataProvider_Id") - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("ProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Sources_Id"); - - b.HasIndex("MetadataProvider_Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("MetadataProviderId"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Permission_GroupPermissions_Id") - .HasColumnType("INTEGER"); - - b.Property("Permission_Permissions_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Permission_GroupPermissions_Id"); - - b.HasIndex("Permission_Permissions_Id"); - - b.ToTable("Permission"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("DateModified") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SourceId") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Artwork_Artwork_Id") - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("Person_Id") - .HasColumnType("INTEGER"); - - b.Property("Role") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Artwork_Artwork_Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.HasIndex("Person_Id"); - - b.ToTable("PersonRole"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Preference_Preferences_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.HasKey("Id"); - - b.HasIndex("Preference_Preferences_Id"); - - b.ToTable("Preference"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ProviderData") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("ProviderMapping_ProviderMappings_Id") - .HasColumnType("INTEGER"); - - b.Property("ProviderName") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("ProviderSecrets") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ProviderMapping_ProviderMappings_Id"); - - b.ToTable("ProviderMapping"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RatingSource_RatingType_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("REAL"); - - b.Property("Votes") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.HasIndex("RatingSource_RatingType_Id"); - - b.ToTable("Rating"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MaximumValue") - .HasColumnType("REAL"); - - b.Property("MetadataProviderId_Source_Id") - .HasColumnType("INTEGER"); - - b.Property("MinimumValue") - .HasColumnType("REAL"); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Source_Id"); - - b.ToTable("RatingSource"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Release_Releases_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Release_Releases_Id"); - - b.ToTable("Release"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AudioLanguagePreference") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("AuthenticationProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("DisplayCollectionsView") - .HasColumnType("INTEGER"); - - b.Property("DisplayMissingEpisodes") - .HasColumnType("INTEGER"); - - b.Property("EnableNextEpisodeAutoPlay") - .HasColumnType("INTEGER"); - - b.Property("EnableUserPreferenceAccess") - .HasColumnType("INTEGER"); - - b.Property("GroupedFolders") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("HidePlayedInLatest") - .HasColumnType("INTEGER"); - - b.Property("InvalidLoginAttemptCount") - .HasColumnType("INTEGER"); - - b.Property("LatestItemExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("LoginAttemptsBeforeLockout") - .HasColumnType("INTEGER"); - - b.Property("MustUpdatePassword") - .HasColumnType("INTEGER"); - - b.Property("MyMediaExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("OrderedViews") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("PlayDefaultAudioTrack") - .HasColumnType("INTEGER"); - - b.Property("RememberAudioSelections") - .HasColumnType("INTEGER"); - - b.Property("RememberSubtitleSelections") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SubtitleLanguagePrefernce") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("SubtitleMode") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.HasKey("Id"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Book", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Book"); - - b.HasDiscriminator().HasValue("Book"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItem", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator().HasValue("CustomItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property("Episode_Episodes_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Episode_Episodes_Id"); - - b.ToTable("Episode"); - - b.HasDiscriminator().HasValue("Episode"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Movie", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Movie"); - - b.HasDiscriminator().HasValue("Movie"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbum", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("MusicAlbum"); - - b.HasDiscriminator().HasValue("MusicAlbum"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Photo", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Photo"); - - b.HasDiscriminator().HasValue("Photo"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("SeasonNumber") - .HasColumnType("INTEGER"); - - b.Property("Season_Seasons_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Season_Seasons_Id"); - - b.ToTable("Season"); - - b.HasDiscriminator().HasValue("Season"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Series", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("AirsDayOfWeek") - .HasColumnType("INTEGER"); - - b.Property("AirsTime") - .HasColumnType("TEXT"); - - b.Property("FirstAired") - .HasColumnType("TEXT"); - - b.ToTable("Series"); - - b.HasDiscriminator().HasValue("Series"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("TrackNumber") - .HasColumnType("INTEGER"); - - b.Property("Track_Tracks_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Track_Tracks_Id"); - - b.ToTable("Track"); - - b.HasDiscriminator().HasValue("Track"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("BookMetadata_BookMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("ISBN") - .HasColumnType("INTEGER"); - - b.HasIndex("BookMetadata_BookMetadata_Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator().HasValue("BookMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("CompanyMetadata_CompanyMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("Description") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Headquarters") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Homepage") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("CompanyMetadata_CompanyMetadata_Id"); - - b.ToTable("CompanyMetadata"); - - b.HasDiscriminator().HasValue("CompanyMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("CustomItemMetadata_CustomItemMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("CustomItemMetadata_CustomItemMetadata_Id"); - - b.ToTable("CustomItemMetadata"); - - b.HasDiscriminator().HasValue("CustomItemMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("EpisodeMetadata_EpisodeMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("EpisodeMetadata_EpisodeMetadata_Id"); - - b.ToTable("EpisodeMetadata"); - - b.HasDiscriminator().HasValue("EpisodeMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Country") - .HasColumnName("MovieMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("MovieMetadata_MovieMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Outline") - .HasColumnName("MovieMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnName("MovieMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Tagline") - .HasColumnName("MovieMetadata_Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("MovieMetadata_MovieMetadata_Id"); - - b.ToTable("MovieMetadata"); - - b.HasDiscriminator().HasValue("MovieMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Barcode") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Country") - .HasColumnName("MusicAlbumMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("LabelNumber") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("MusicAlbumMetadata_MusicAlbumMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("MusicAlbumMetadata_MusicAlbumMetadata_Id"); - - b.ToTable("MusicAlbumMetadata"); - - b.HasDiscriminator().HasValue("MusicAlbumMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("PhotoMetadata_PhotoMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("PhotoMetadata_PhotoMetadata_Id"); - - b.ToTable("PhotoMetadata"); - - b.HasDiscriminator().HasValue("PhotoMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Outline") - .HasColumnName("SeasonMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("SeasonMetadata_SeasonMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("SeasonMetadata_SeasonMetadata_Id"); - - b.ToTable("SeasonMetadata"); - - b.HasDiscriminator().HasValue("SeasonMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Country") - .HasColumnName("SeriesMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("Outline") - .HasColumnName("SeriesMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnName("SeriesMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("SeriesMetadata_SeriesMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Tagline") - .HasColumnName("SeriesMetadata_Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("SeriesMetadata_SeriesMetadata_Id"); - - b.ToTable("SeriesMetadata"); - - b.HasDiscriminator().HasValue("SeriesMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("TrackMetadata_TrackMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("TrackMetadata_TrackMetadata_Id"); - - b.ToTable("TrackMetadata"); - - b.HasDiscriminator().HasValue("TrackMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Artwork") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => - { - b.HasOne("Jellyfin.Data.Entities.Release", null) - .WithMany("Chapters") - .HasForeignKey("Chapter_Chapters_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.HasOne("Jellyfin.Data.Entities.Collection", null) - .WithMany("CollectionItem") - .HasForeignKey("CollectionItem_CollectionItem_Id"); - - b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Next") - .WithMany() - .HasForeignKey("CollectionItem_Next_Id"); - - b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Previous") - .WithMany() - .HasForeignKey("CollectionItem_Previous_Id"); - - b.HasOne("Jellyfin.Data.Entities.LibraryItem", "LibraryItem") - .WithMany() - .HasForeignKey("LibraryItem_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbumMetadata", null) - .WithMany("Labels") - .HasForeignKey("Company_Labels_Id"); - - b.HasOne("Jellyfin.Data.Entities.SeriesMetadata", null) - .WithMany("Networks") - .HasForeignKey("Company_Networks_Id"); - - b.HasOne("Jellyfin.Data.Entities.Company", "Parent") - .WithMany() - .HasForeignKey("Company_Parent_Id"); - - b.HasOne("Jellyfin.Data.Entities.BookMetadata", null) - .WithMany("Publishers") - .HasForeignKey("Company_Publishers_Id"); - - b.HasOne("Jellyfin.Data.Entities.MovieMetadata", null) - .WithMany("Studios") - .HasForeignKey("Company_Studios_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Genres") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => - { - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Groups") - .HasForeignKey("Group_Groups_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.HasOne("Jellyfin.Data.Entities.LibraryRoot", "LibraryRoot") - .WithMany() - .HasForeignKey("LibraryRoot_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.HasOne("Jellyfin.Data.Entities.Library", "Library") - .WithMany() - .HasForeignKey("Library_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.HasOne("Jellyfin.Data.Entities.Release", null) - .WithMany("MediaFiles") - .HasForeignKey("MediaFile_MediaFiles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.HasOne("Jellyfin.Data.Entities.MediaFile", null) - .WithMany("MediaFileStreams") - .HasForeignKey("MediaFileStream_MediaFileStreams_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.HasOne("Jellyfin.Data.Entities.Person", null) - .WithMany("Sources") - .HasForeignKey("MetadataProviderId_Sources_Id"); - - b.HasOne("Jellyfin.Data.Entities.PersonRole", null) - .WithMany("Sources") - .HasForeignKey("MetadataProviderId_Sources_Id"); - - b.HasOne("Jellyfin.Data.Entities.MetadataProvider", "MetadataProvider") - .WithMany() - .HasForeignKey("MetadataProvider_Id"); - - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Sources") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("GroupPermissions") - .HasForeignKey("Permission_GroupPermissions_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Permissions") - .HasForeignKey("Permission_Permissions_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.HasOne("Jellyfin.Data.Entities.Artwork", "Artwork") - .WithMany() - .HasForeignKey("Artwork_Artwork_Id"); - - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("PersonRoles") - .HasForeignKey("PersonRole_PersonRoles_Id"); - - b.HasOne("Jellyfin.Data.Entities.Person", "Person") - .WithMany() - .HasForeignKey("Person_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("ProviderMappings") - .HasForeignKey("ProviderMapping_ProviderMappings_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("ProviderMappings") - .HasForeignKey("ProviderMapping_ProviderMappings_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Ratings") - .HasForeignKey("PersonRole_PersonRoles_Id"); - - b.HasOne("Jellyfin.Data.Entities.RatingSource", "RatingType") - .WithMany() - .HasForeignKey("RatingSource_RatingType_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => - { - b.HasOne("Jellyfin.Data.Entities.MetadataProviderId", "Source") - .WithMany() - .HasForeignKey("MetadataProviderId_Source_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.HasOne("Jellyfin.Data.Entities.Book", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id"); - - b.HasOne("Jellyfin.Data.Entities.CustomItem", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id1"); - - b.HasOne("Jellyfin.Data.Entities.Episode", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id2"); - - b.HasOne("Jellyfin.Data.Entities.Movie", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id3"); - - b.HasOne("Jellyfin.Data.Entities.Photo", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id4"); - - b.HasOne("Jellyfin.Data.Entities.Track", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id5"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => - { - b.HasOne("Jellyfin.Data.Entities.Season", null) - .WithMany("Episodes") - .HasForeignKey("Episode_Episodes_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => - { - b.HasOne("Jellyfin.Data.Entities.Series", null) - .WithMany("Seasons") - .HasForeignKey("Season_Seasons_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) - .WithMany("Tracks") - .HasForeignKey("Track_Tracks_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Book", null) - .WithMany("BookMetadata") - .HasForeignKey("BookMetadata_BookMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Company", null) - .WithMany("CompanyMetadata") - .HasForeignKey("CompanyMetadata_CompanyMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.CustomItem", null) - .WithMany("CustomItemMetadata") - .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Episode", null) - .WithMany("EpisodeMetadata") - .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Movie", null) - .WithMany("MovieMetadata") - .HasForeignKey("MovieMetadata_MovieMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) - .WithMany("MusicAlbumMetadata") - .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Photo", null) - .WithMany("PhotoMetadata") - .HasForeignKey("PhotoMetadata_PhotoMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Season", null) - .WithMany("SeasonMetadata") - .HasForeignKey("SeasonMetadata_SeasonMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Series", null) - .WithMany("SeriesMetadata") - .HasForeignKey("SeriesMetadata_SeriesMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Track", null) - .WithMany("TrackMetadata") - .HasForeignKey("TrackMetadata_TrackMetadata_Id"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs b/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs deleted file mode 100644 index f6f2f1a81b..0000000000 --- a/Jellyfin.Server.Implementations/Migrations/20200430215054_InitialSchema.cs +++ /dev/null @@ -1,1294 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1601 - -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Jellyfin.Server.Implementations.Migrations -{ - public partial class InitialSchema : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "jellyfin"); - - migrationBuilder.CreateTable( - name: "ActivityLog", - 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_ActivityLog", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Collection", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: true), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Collection", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Library", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: false), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Library", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "MetadataProvider", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: false), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_MetadataProvider", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Person", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UrlId = table.Column(nullable: false), - Name = table.Column(maxLength: 1024, nullable: false), - SourceId = table.Column(maxLength: 255, nullable: true), - DateAdded = table.Column(nullable: false), - DateModified = table.Column(nullable: false), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Person", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "User", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Username = table.Column(maxLength: 255, nullable: false), - Password = table.Column(maxLength: 65535, nullable: true), - MustUpdatePassword = table.Column(nullable: false), - AudioLanguagePreference = table.Column(maxLength: 255, nullable: false), - AuthenticationProviderId = table.Column(maxLength: 255, nullable: false), - GroupedFolders = table.Column(maxLength: 65535, nullable: true), - InvalidLoginAttemptCount = table.Column(nullable: false), - LatestItemExcludes = table.Column(maxLength: 65535, nullable: true), - LoginAttemptsBeforeLockout = table.Column(nullable: true), - MyMediaExcludes = table.Column(maxLength: 65535, nullable: true), - OrderedViews = table.Column(maxLength: 65535, nullable: true), - SubtitleMode = table.Column(maxLength: 255, nullable: false), - PlayDefaultAudioTrack = table.Column(nullable: false), - SubtitleLanguagePrefernce = table.Column(maxLength: 255, nullable: true), - DisplayMissingEpisodes = table.Column(nullable: true), - DisplayCollectionsView = table.Column(nullable: true), - HidePlayedInLatest = table.Column(nullable: true), - RememberAudioSelections = table.Column(nullable: true), - RememberSubtitleSelections = table.Column(nullable: true), - EnableNextEpisodeAutoPlay = table.Column(nullable: true), - EnableUserPreferenceAccess = table.Column(nullable: true), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "LibraryRoot", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column(maxLength: 65535, nullable: false), - NetworkPath = table.Column(maxLength: 65535, nullable: true), - RowVersion = table.Column(nullable: false), - Library_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_LibraryRoot", x => x.Id); - table.ForeignKey( - name: "FK_LibraryRoot_Library_Library_Id", - column: x => x.Library_Id, - principalSchema: "jellyfin", - principalTable: "Library", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Group", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 255, nullable: false), - RowVersion = table.Column(nullable: false), - Group_Groups_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Group", x => x.Id); - table.ForeignKey( - name: "FK_Group_User_Group_Groups_Id", - column: x => x.Group_Groups_Id, - principalSchema: "jellyfin", - principalTable: "User", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "LibraryItem", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UrlId = table.Column(nullable: false), - DateAdded = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - LibraryRoot_Id = table.Column(nullable: true), - Discriminator = table.Column(nullable: false), - EpisodeNumber = table.Column(nullable: true), - Episode_Episodes_Id = table.Column(nullable: true), - SeasonNumber = table.Column(nullable: true), - Season_Seasons_Id = table.Column(nullable: true), - AirsDayOfWeek = table.Column(nullable: true), - AirsTime = table.Column(nullable: true), - FirstAired = table.Column(nullable: true), - TrackNumber = table.Column(nullable: true), - Track_Tracks_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_LibraryItem", x => x.Id); - table.ForeignKey( - name: "FK_LibraryItem_LibraryItem_Episode_Episodes_Id", - column: x => x.Episode_Episodes_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_LibraryItem_LibraryRoot_LibraryRoot_Id", - column: x => x.LibraryRoot_Id, - principalSchema: "jellyfin", - principalTable: "LibraryRoot", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_LibraryItem_LibraryItem_Season_Seasons_Id", - column: x => x.Season_Seasons_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_LibraryItem_LibraryItem_Track_Tracks_Id", - column: x => x.Track_Tracks_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Permission", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column(nullable: false), - Value = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - Permission_GroupPermissions_Id = table.Column(nullable: true), - Permission_Permissions_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Permission", x => x.Id); - table.ForeignKey( - name: "FK_Permission_Group_Permission_GroupPermissions_Id", - column: x => x.Permission_GroupPermissions_Id, - principalSchema: "jellyfin", - principalTable: "Group", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Permission_User_Permission_Permissions_Id", - column: x => x.Permission_Permissions_Id, - principalSchema: "jellyfin", - principalTable: "User", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Preference", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column(nullable: false), - Value = table.Column(maxLength: 65535, nullable: false), - RowVersion = table.Column(nullable: false), - Preference_Preferences_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Preference", x => x.Id); - table.ForeignKey( - name: "FK_Preference_Group_Preference_Preferences_Id", - column: x => x.Preference_Preferences_Id, - principalSchema: "jellyfin", - principalTable: "Group", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Preference_User_Preference_Preferences_Id", - column: x => x.Preference_Preferences_Id, - principalSchema: "jellyfin", - principalTable: "User", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "ProviderMapping", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ProviderName = table.Column(maxLength: 255, nullable: false), - ProviderSecrets = table.Column(maxLength: 65535, nullable: false), - ProviderData = table.Column(maxLength: 65535, nullable: false), - RowVersion = table.Column(nullable: false), - ProviderMapping_ProviderMappings_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ProviderMapping", x => x.Id); - table.ForeignKey( - name: "FK_ProviderMapping_Group_ProviderMapping_ProviderMappings_Id", - column: x => x.ProviderMapping_ProviderMappings_Id, - principalSchema: "jellyfin", - principalTable: "Group", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_ProviderMapping_User_ProviderMapping_ProviderMappings_Id", - column: x => x.ProviderMapping_ProviderMappings_Id, - principalSchema: "jellyfin", - principalTable: "User", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "CollectionItem", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RowVersion = table.Column(nullable: false), - LibraryItem_Id = table.Column(nullable: true), - CollectionItem_Next_Id = table.Column(nullable: true), - CollectionItem_Previous_Id = table.Column(nullable: true), - CollectionItem_CollectionItem_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_CollectionItem", x => x.Id); - table.ForeignKey( - name: "FK_CollectionItem_Collection_CollectionItem_CollectionItem_Id", - column: x => x.CollectionItem_CollectionItem_Id, - principalSchema: "jellyfin", - principalTable: "Collection", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_CollectionItem_CollectionItem_CollectionItem_Next_Id", - column: x => x.CollectionItem_Next_Id, - principalSchema: "jellyfin", - principalTable: "CollectionItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_CollectionItem_CollectionItem_CollectionItem_Previous_Id", - column: x => x.CollectionItem_Previous_Id, - principalSchema: "jellyfin", - principalTable: "CollectionItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_CollectionItem_LibraryItem_LibraryItem_Id", - column: x => x.LibraryItem_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Release", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: false), - RowVersion = table.Column(nullable: false), - Release_Releases_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Release", x => x.Id); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id1", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id2", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id3", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id4", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Release_LibraryItem_Release_Releases_Id5", - column: x => x.Release_Releases_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Chapter", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: true), - Language = table.Column(maxLength: 3, nullable: false), - TimeStart = table.Column(nullable: false), - TimeEnd = table.Column(nullable: true), - RowVersion = table.Column(nullable: false), - Chapter_Chapters_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Chapter", x => x.Id); - table.ForeignKey( - name: "FK_Chapter_Release_Chapter_Chapters_Id", - column: x => x.Chapter_Chapters_Id, - principalSchema: "jellyfin", - principalTable: "Release", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "MediaFile", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column(maxLength: 65535, nullable: false), - Kind = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - MediaFile_MediaFiles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MediaFile", x => x.Id); - table.ForeignKey( - name: "FK_MediaFile_Release_MediaFile_MediaFiles_Id", - column: x => x.MediaFile_MediaFiles_Id, - principalSchema: "jellyfin", - principalTable: "Release", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "MediaFileStream", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - StreamNumber = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - MediaFileStream_MediaFileStreams_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MediaFileStream", x => x.Id); - table.ForeignKey( - name: "FK_MediaFileStream_MediaFile_MediaFileStream_MediaFileStreams_Id", - column: x => x.MediaFileStream_MediaFileStreams_Id, - principalSchema: "jellyfin", - principalTable: "MediaFile", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "PersonRole", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Role = table.Column(maxLength: 1024, nullable: true), - Type = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - Person_Id = table.Column(nullable: true), - Artwork_Artwork_Id = table.Column(nullable: true), - PersonRole_PersonRoles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_PersonRole", x => x.Id); - table.ForeignKey( - name: "FK_PersonRole_Person_Person_Id", - column: x => x.Person_Id, - principalSchema: "jellyfin", - principalTable: "Person", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Metadata", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Title = table.Column(maxLength: 1024, nullable: false), - OriginalTitle = table.Column(maxLength: 1024, nullable: true), - SortTitle = table.Column(maxLength: 1024, nullable: true), - Language = table.Column(maxLength: 3, nullable: false), - ReleaseDate = table.Column(nullable: true), - DateAdded = table.Column(nullable: false), - DateModified = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - Discriminator = table.Column(nullable: false), - ISBN = table.Column(nullable: true), - BookMetadata_BookMetadata_Id = table.Column(nullable: true), - Description = table.Column(maxLength: 65535, nullable: true), - Headquarters = table.Column(maxLength: 255, nullable: true), - Country = table.Column(maxLength: 2, nullable: true), - Homepage = table.Column(maxLength: 1024, nullable: true), - CompanyMetadata_CompanyMetadata_Id = table.Column(nullable: true), - CustomItemMetadata_CustomItemMetadata_Id = table.Column(nullable: true), - Outline = table.Column(maxLength: 1024, nullable: true), - Plot = table.Column(maxLength: 65535, nullable: true), - Tagline = table.Column(maxLength: 1024, nullable: true), - EpisodeMetadata_EpisodeMetadata_Id = table.Column(nullable: true), - MovieMetadata_Outline = table.Column(maxLength: 1024, nullable: true), - MovieMetadata_Plot = table.Column(maxLength: 65535, nullable: true), - MovieMetadata_Tagline = table.Column(maxLength: 1024, nullable: true), - MovieMetadata_Country = table.Column(maxLength: 2, nullable: true), - MovieMetadata_MovieMetadata_Id = table.Column(nullable: true), - Barcode = table.Column(maxLength: 255, nullable: true), - LabelNumber = table.Column(maxLength: 255, nullable: true), - MusicAlbumMetadata_Country = table.Column(maxLength: 2, nullable: true), - MusicAlbumMetadata_MusicAlbumMetadata_Id = table.Column(nullable: true), - PhotoMetadata_PhotoMetadata_Id = table.Column(nullable: true), - SeasonMetadata_Outline = table.Column(maxLength: 1024, nullable: true), - SeasonMetadata_SeasonMetadata_Id = table.Column(nullable: true), - SeriesMetadata_Outline = table.Column(maxLength: 1024, nullable: true), - SeriesMetadata_Plot = table.Column(maxLength: 65535, nullable: true), - SeriesMetadata_Tagline = table.Column(maxLength: 1024, nullable: true), - SeriesMetadata_Country = table.Column(maxLength: 2, nullable: true), - SeriesMetadata_SeriesMetadata_Id = table.Column(nullable: true), - TrackMetadata_TrackMetadata_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Metadata", x => x.Id); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_BookMetadata_BookMetadata_Id", - column: x => x.BookMetadata_BookMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_CustomItemMetadata_CustomItemMetadata_Id", - column: x => x.CustomItemMetadata_CustomItemMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_EpisodeMetadata_EpisodeMetadata_Id", - column: x => x.EpisodeMetadata_EpisodeMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_MovieMetadata_MovieMetadata_Id", - column: x => x.MovieMetadata_MovieMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_MusicAlbumMetadata_MusicAlbumMetadata_Id", - column: x => x.MusicAlbumMetadata_MusicAlbumMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_PhotoMetadata_PhotoMetadata_Id", - column: x => x.PhotoMetadata_PhotoMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_SeasonMetadata_SeasonMetadata_Id", - column: x => x.SeasonMetadata_SeasonMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_SeriesMetadata_SeriesMetadata_Id", - column: x => x.SeriesMetadata_SeriesMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Metadata_LibraryItem_TrackMetadata_TrackMetadata_Id", - column: x => x.TrackMetadata_TrackMetadata_Id, - principalSchema: "jellyfin", - principalTable: "LibraryItem", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Artwork", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column(maxLength: 65535, nullable: false), - Kind = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - PersonRole_PersonRoles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Artwork", x => x.Id); - table.ForeignKey( - name: "FK_Artwork_Metadata_PersonRole_PersonRoles_Id", - column: x => x.PersonRole_PersonRoles_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Company", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RowVersion = table.Column(nullable: false), - Company_Parent_Id = table.Column(nullable: true), - Company_Labels_Id = table.Column(nullable: true), - Company_Networks_Id = table.Column(nullable: true), - Company_Publishers_Id = table.Column(nullable: true), - Company_Studios_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Company", x => x.Id); - table.ForeignKey( - name: "FK_Company_Metadata_Company_Labels_Id", - column: x => x.Company_Labels_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Company_Metadata_Company_Networks_Id", - column: x => x.Company_Networks_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Company_Company_Company_Parent_Id", - column: x => x.Company_Parent_Id, - principalSchema: "jellyfin", - principalTable: "Company", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Company_Metadata_Company_Publishers_Id", - column: x => x.Company_Publishers_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Company_Metadata_Company_Studios_Id", - column: x => x.Company_Studios_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Genre", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 255, nullable: false), - RowVersion = table.Column(nullable: false), - PersonRole_PersonRoles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Genre", x => x.Id); - table.ForeignKey( - name: "FK_Genre_Metadata_PersonRole_PersonRoles_Id", - column: x => x.PersonRole_PersonRoles_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "MetadataProviderId", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ProviderId = table.Column(maxLength: 255, nullable: false), - RowVersion = table.Column(nullable: false), - MetadataProvider_Id = table.Column(nullable: true), - MetadataProviderId_Sources_Id = table.Column(nullable: true), - PersonRole_PersonRoles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MetadataProviderId", x => x.Id); - table.ForeignKey( - name: "FK_MetadataProviderId_Person_MetadataProviderId_Sources_Id", - column: x => x.MetadataProviderId_Sources_Id, - principalSchema: "jellyfin", - principalTable: "Person", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_MetadataProviderId_PersonRole_MetadataProviderId_Sources_Id", - column: x => x.MetadataProviderId_Sources_Id, - principalSchema: "jellyfin", - principalTable: "PersonRole", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_MetadataProviderId_MetadataProvider_MetadataProvider_Id", - column: x => x.MetadataProvider_Id, - principalSchema: "jellyfin", - principalTable: "MetadataProvider", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_MetadataProviderId_Metadata_PersonRole_PersonRoles_Id", - column: x => x.PersonRole_PersonRoles_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "RatingSource", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(maxLength: 1024, nullable: true), - MaximumValue = table.Column(nullable: false), - MinimumValue = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - MetadataProviderId_Source_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_RatingSource", x => x.Id); - table.ForeignKey( - name: "FK_RatingSource_MetadataProviderId_MetadataProviderId_Source_Id", - column: x => x.MetadataProviderId_Source_Id, - principalSchema: "jellyfin", - principalTable: "MetadataProviderId", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Rating", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Value = table.Column(nullable: false), - Votes = table.Column(nullable: true), - RowVersion = table.Column(nullable: false), - RatingSource_RatingType_Id = table.Column(nullable: true), - PersonRole_PersonRoles_Id = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Rating", x => x.Id); - table.ForeignKey( - name: "FK_Rating_Metadata_PersonRole_PersonRoles_Id", - column: x => x.PersonRole_PersonRoles_Id, - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Rating_RatingSource_RatingSource_RatingType_Id", - column: x => x.RatingSource_RatingType_Id, - principalSchema: "jellyfin", - principalTable: "RatingSource", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_Artwork_Kind", - schema: "jellyfin", - table: "Artwork", - column: "Kind"); - - migrationBuilder.CreateIndex( - name: "IX_Artwork_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "Artwork", - column: "PersonRole_PersonRoles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Chapter_Chapter_Chapters_Id", - schema: "jellyfin", - table: "Chapter", - column: "Chapter_Chapters_Id"); - - migrationBuilder.CreateIndex( - name: "IX_CollectionItem_CollectionItem_CollectionItem_Id", - schema: "jellyfin", - table: "CollectionItem", - column: "CollectionItem_CollectionItem_Id"); - - migrationBuilder.CreateIndex( - name: "IX_CollectionItem_CollectionItem_Next_Id", - schema: "jellyfin", - table: "CollectionItem", - column: "CollectionItem_Next_Id"); - - migrationBuilder.CreateIndex( - name: "IX_CollectionItem_CollectionItem_Previous_Id", - schema: "jellyfin", - table: "CollectionItem", - column: "CollectionItem_Previous_Id"); - - migrationBuilder.CreateIndex( - name: "IX_CollectionItem_LibraryItem_Id", - schema: "jellyfin", - table: "CollectionItem", - column: "LibraryItem_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Company_Company_Labels_Id", - schema: "jellyfin", - table: "Company", - column: "Company_Labels_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Company_Company_Networks_Id", - schema: "jellyfin", - table: "Company", - column: "Company_Networks_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Company_Company_Parent_Id", - schema: "jellyfin", - table: "Company", - column: "Company_Parent_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Company_Company_Publishers_Id", - schema: "jellyfin", - table: "Company", - column: "Company_Publishers_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Company_Company_Studios_Id", - schema: "jellyfin", - table: "Company", - column: "Company_Studios_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Genre_Name", - schema: "jellyfin", - table: "Genre", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Genre_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "Genre", - column: "PersonRole_PersonRoles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Group_Group_Groups_Id", - schema: "jellyfin", - table: "Group", - column: "Group_Groups_Id"); - - migrationBuilder.CreateIndex( - name: "IX_LibraryItem_Episode_Episodes_Id", - schema: "jellyfin", - table: "LibraryItem", - column: "Episode_Episodes_Id"); - - migrationBuilder.CreateIndex( - name: "IX_LibraryItem_LibraryRoot_Id", - schema: "jellyfin", - table: "LibraryItem", - column: "LibraryRoot_Id"); - - migrationBuilder.CreateIndex( - name: "IX_LibraryItem_UrlId", - schema: "jellyfin", - table: "LibraryItem", - column: "UrlId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_LibraryItem_Season_Seasons_Id", - schema: "jellyfin", - table: "LibraryItem", - column: "Season_Seasons_Id"); - - migrationBuilder.CreateIndex( - name: "IX_LibraryItem_Track_Tracks_Id", - schema: "jellyfin", - table: "LibraryItem", - column: "Track_Tracks_Id"); - - migrationBuilder.CreateIndex( - name: "IX_LibraryRoot_Library_Id", - schema: "jellyfin", - table: "LibraryRoot", - column: "Library_Id"); - - migrationBuilder.CreateIndex( - name: "IX_MediaFile_MediaFile_MediaFiles_Id", - schema: "jellyfin", - table: "MediaFile", - column: "MediaFile_MediaFiles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_MediaFileStream_MediaFileStream_MediaFileStreams_Id", - schema: "jellyfin", - table: "MediaFileStream", - column: "MediaFileStream_MediaFileStreams_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_BookMetadata_BookMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "BookMetadata_BookMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_CompanyMetadata_CompanyMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "CompanyMetadata_CompanyMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_CustomItemMetadata_CustomItemMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "CustomItemMetadata_CustomItemMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_EpisodeMetadata_EpisodeMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "EpisodeMetadata_EpisodeMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_MovieMetadata_MovieMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "MovieMetadata_MovieMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_MusicAlbumMetadata_MusicAlbumMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "MusicAlbumMetadata_MusicAlbumMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_PhotoMetadata_PhotoMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "PhotoMetadata_PhotoMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_SeasonMetadata_SeasonMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "SeasonMetadata_SeasonMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_SeriesMetadata_SeriesMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "SeriesMetadata_SeriesMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Metadata_TrackMetadata_TrackMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "TrackMetadata_TrackMetadata_Id"); - - migrationBuilder.CreateIndex( - name: "IX_MetadataProviderId_MetadataProviderId_Sources_Id", - schema: "jellyfin", - table: "MetadataProviderId", - column: "MetadataProviderId_Sources_Id"); - - migrationBuilder.CreateIndex( - name: "IX_MetadataProviderId_MetadataProvider_Id", - schema: "jellyfin", - table: "MetadataProviderId", - column: "MetadataProvider_Id"); - - migrationBuilder.CreateIndex( - name: "IX_MetadataProviderId_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "MetadataProviderId", - column: "PersonRole_PersonRoles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Permission_Permission_GroupPermissions_Id", - schema: "jellyfin", - table: "Permission", - column: "Permission_GroupPermissions_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Permission_Permission_Permissions_Id", - schema: "jellyfin", - table: "Permission", - column: "Permission_Permissions_Id"); - - migrationBuilder.CreateIndex( - name: "IX_PersonRole_Artwork_Artwork_Id", - schema: "jellyfin", - table: "PersonRole", - column: "Artwork_Artwork_Id"); - - migrationBuilder.CreateIndex( - name: "IX_PersonRole_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "PersonRole", - column: "PersonRole_PersonRoles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_PersonRole_Person_Id", - schema: "jellyfin", - table: "PersonRole", - column: "Person_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Preference_Preference_Preferences_Id", - schema: "jellyfin", - table: "Preference", - column: "Preference_Preferences_Id"); - - migrationBuilder.CreateIndex( - name: "IX_ProviderMapping_ProviderMapping_ProviderMappings_Id", - schema: "jellyfin", - table: "ProviderMapping", - column: "ProviderMapping_ProviderMappings_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Rating_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "Rating", - column: "PersonRole_PersonRoles_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Rating_RatingSource_RatingType_Id", - schema: "jellyfin", - table: "Rating", - column: "RatingSource_RatingType_Id"); - - migrationBuilder.CreateIndex( - name: "IX_RatingSource_MetadataProviderId_Source_Id", - schema: "jellyfin", - table: "RatingSource", - column: "MetadataProviderId_Source_Id"); - - migrationBuilder.CreateIndex( - name: "IX_Release_Release_Releases_Id", - schema: "jellyfin", - table: "Release", - column: "Release_Releases_Id"); - - migrationBuilder.AddForeignKey( - name: "FK_PersonRole_Metadata_PersonRole_PersonRoles_Id", - schema: "jellyfin", - table: "PersonRole", - column: "PersonRole_PersonRoles_Id", - principalSchema: "jellyfin", - principalTable: "Metadata", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_PersonRole_Artwork_Artwork_Artwork_Id", - schema: "jellyfin", - table: "PersonRole", - column: "Artwork_Artwork_Id", - principalSchema: "jellyfin", - principalTable: "Artwork", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_Metadata_Company_CompanyMetadata_CompanyMetadata_Id", - schema: "jellyfin", - table: "Metadata", - column: "CompanyMetadata_CompanyMetadata_Id", - principalSchema: "jellyfin", - principalTable: "Company", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Company_Metadata_Company_Labels_Id", - schema: "jellyfin", - table: "Company"); - - migrationBuilder.DropForeignKey( - name: "FK_Company_Metadata_Company_Networks_Id", - schema: "jellyfin", - table: "Company"); - - migrationBuilder.DropForeignKey( - name: "FK_Company_Metadata_Company_Publishers_Id", - schema: "jellyfin", - table: "Company"); - - migrationBuilder.DropForeignKey( - name: "FK_Company_Metadata_Company_Studios_Id", - schema: "jellyfin", - table: "Company"); - - migrationBuilder.DropTable( - name: "ActivityLog", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Chapter", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "CollectionItem", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Genre", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "MediaFileStream", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Permission", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Preference", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "ProviderMapping", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Rating", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Collection", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "MediaFile", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Group", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "RatingSource", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Release", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "User", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "MetadataProviderId", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "PersonRole", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "MetadataProvider", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Artwork", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Person", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Metadata", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "LibraryItem", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Company", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "LibraryRoot", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Library", - schema: "jellyfin"); - } - } -} diff --git a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs new file mode 100644 index 0000000000..e1ee9b34aa --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs @@ -0,0 +1,73 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +// +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("20200502231229_InitialSchema")] + partial class InitialSchema + { + 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("ActivityLog"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs b/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs new file mode 100644 index 0000000000..42fac865ce --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.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 InitialSchema : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "jellyfin"); + + migrationBuilder.CreateTable( + name: "ActivityLog", + 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_ActivityLog", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ActivityLog", + schema: "jellyfin"); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs index 72a4a8c3b6..23a0fdc784 100644 --- a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 8cdd101af4..27f5fe24b0 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -1,6 +1,4 @@ -#pragma warning disable CS1591 - -// +// using System; using Jellyfin.Server.Implementations; using Microsoft.EntityFrameworkCore; @@ -64,1447 +62,6 @@ namespace Jellyfin.Server.Implementations.Migrations b.ToTable("ActivityLog"); }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Kind"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("Artwork"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Chapter_Chapters_Id") - .HasColumnType("INTEGER"); - - b.Property("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("TimeEnd") - .HasColumnType("INTEGER"); - - b.Property("TimeStart") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Chapter_Chapters_Id"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Collection"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_CollectionItem_Id") - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_Next_Id") - .HasColumnType("INTEGER"); - - b.Property("CollectionItem_Previous_Id") - .HasColumnType("INTEGER"); - - b.Property("LibraryItem_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("CollectionItem_CollectionItem_Id"); - - b.HasIndex("CollectionItem_Next_Id"); - - b.HasIndex("CollectionItem_Previous_Id"); - - b.HasIndex("LibraryItem_Id"); - - b.ToTable("CollectionItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Company_Labels_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Networks_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Parent_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Publishers_Id") - .HasColumnType("INTEGER"); - - b.Property("Company_Studios_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Company_Labels_Id"); - - b.HasIndex("Company_Networks_Id"); - - b.HasIndex("Company_Parent_Id"); - - b.HasIndex("Company_Publishers_Id"); - - b.HasIndex("Company_Studios_Id"); - - b.ToTable("Company"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("Genre"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Group_Groups_Id") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Group_Groups_Id"); - - b.ToTable("Group"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Library"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("LibraryRoot_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("LibraryRoot_Id"); - - b.HasIndex("UrlId") - .IsUnique(); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator("Discriminator").HasValue("LibraryItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Library_Id") - .HasColumnType("INTEGER"); - - b.Property("NetworkPath") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Library_Id"); - - b.ToTable("LibraryRoot"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("MediaFile_MediaFiles_Id") - .HasColumnType("INTEGER"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFile_MediaFiles_Id"); - - b.ToTable("MediaFile"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MediaFileStream_MediaFileStreams_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("StreamNumber") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileStream_MediaFileStreams_Id"); - - b.ToTable("MediaFileStream"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("DateModified") - .HasColumnType("TEXT"); - - b.Property("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property("OriginalTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SortTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Title") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasKey("Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator("Discriminator").HasValue("Metadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("MetadataProvider"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MetadataProviderId_Sources_Id") - .HasColumnType("INTEGER"); - - b.Property("MetadataProvider_Id") - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("ProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Sources_Id"); - - b.HasIndex("MetadataProvider_Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.ToTable("MetadataProviderId"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Permission_GroupPermissions_Id") - .HasColumnType("INTEGER"); - - b.Property("Permission_Permissions_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Permission_GroupPermissions_Id"); - - b.HasIndex("Permission_Permissions_Id"); - - b.ToTable("Permission"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateAdded") - .HasColumnType("TEXT"); - - b.Property("DateModified") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SourceId") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Artwork_Artwork_Id") - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("Person_Id") - .HasColumnType("INTEGER"); - - b.Property("Role") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Type") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Artwork_Artwork_Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.HasIndex("Person_Id"); - - b.ToTable("PersonRole"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Preference_Preferences_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.HasKey("Id"); - - b.HasIndex("Preference_Preferences_Id"); - - b.ToTable("Preference"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ProviderData") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("ProviderMapping_ProviderMappings_Id") - .HasColumnType("INTEGER"); - - b.Property("ProviderName") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("ProviderSecrets") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ProviderMapping_ProviderMappings_Id"); - - b.ToTable("ProviderMapping"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property("RatingSource_RatingType_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("REAL"); - - b.Property("Votes") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("PersonRole_PersonRoles_Id"); - - b.HasIndex("RatingSource_RatingType_Id"); - - b.ToTable("Rating"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("MaximumValue") - .HasColumnType("REAL"); - - b.Property("MetadataProviderId_Source_Id") - .HasColumnType("INTEGER"); - - b.Property("MinimumValue") - .HasColumnType("REAL"); - - b.Property("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Source_Id"); - - b.ToTable("RatingSource"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Release_Releases_Id") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Release_Releases_Id"); - - b.ToTable("Release"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("AudioLanguagePreference") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("AuthenticationProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("DisplayCollectionsView") - .HasColumnType("INTEGER"); - - b.Property("DisplayMissingEpisodes") - .HasColumnType("INTEGER"); - - b.Property("EnableNextEpisodeAutoPlay") - .HasColumnType("INTEGER"); - - b.Property("EnableUserPreferenceAccess") - .HasColumnType("INTEGER"); - - b.Property("GroupedFolders") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("HidePlayedInLatest") - .HasColumnType("INTEGER"); - - b.Property("InvalidLoginAttemptCount") - .HasColumnType("INTEGER"); - - b.Property("LatestItemExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("LoginAttemptsBeforeLockout") - .HasColumnType("INTEGER"); - - b.Property("MustUpdatePassword") - .HasColumnType("INTEGER"); - - b.Property("MyMediaExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("OrderedViews") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("PlayDefaultAudioTrack") - .HasColumnType("INTEGER"); - - b.Property("RememberAudioSelections") - .HasColumnType("INTEGER"); - - b.Property("RememberSubtitleSelections") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SubtitleLanguagePrefernce") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("SubtitleMode") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.HasKey("Id"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Book", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Book"); - - b.HasDiscriminator().HasValue("Book"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItem", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator().HasValue("CustomItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property("Episode_Episodes_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Episode_Episodes_Id"); - - b.ToTable("Episode"); - - b.HasDiscriminator().HasValue("Episode"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Movie", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Movie"); - - b.HasDiscriminator().HasValue("Movie"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbum", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("MusicAlbum"); - - b.HasDiscriminator().HasValue("MusicAlbum"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Photo", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.ToTable("Photo"); - - b.HasDiscriminator().HasValue("Photo"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("SeasonNumber") - .HasColumnType("INTEGER"); - - b.Property("Season_Seasons_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Season_Seasons_Id"); - - b.ToTable("Season"); - - b.HasDiscriminator().HasValue("Season"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Series", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("AirsDayOfWeek") - .HasColumnType("INTEGER"); - - b.Property("AirsTime") - .HasColumnType("TEXT"); - - b.Property("FirstAired") - .HasColumnType("TEXT"); - - b.ToTable("Series"); - - b.HasDiscriminator().HasValue("Series"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => - { - b.HasBaseType("Jellyfin.Data.Entities.LibraryItem"); - - b.Property("TrackNumber") - .HasColumnType("INTEGER"); - - b.Property("Track_Tracks_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("Track_Tracks_Id"); - - b.ToTable("Track"); - - b.HasDiscriminator().HasValue("Track"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("BookMetadata_BookMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("ISBN") - .HasColumnType("INTEGER"); - - b.HasIndex("BookMetadata_BookMetadata_Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator().HasValue("BookMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("CompanyMetadata_CompanyMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("Description") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Headquarters") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Homepage") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("CompanyMetadata_CompanyMetadata_Id"); - - b.ToTable("CompanyMetadata"); - - b.HasDiscriminator().HasValue("CompanyMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("CustomItemMetadata_CustomItemMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("CustomItemMetadata_CustomItemMetadata_Id"); - - b.ToTable("CustomItemMetadata"); - - b.HasDiscriminator().HasValue("CustomItemMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("EpisodeMetadata_EpisodeMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("EpisodeMetadata_EpisodeMetadata_Id"); - - b.ToTable("EpisodeMetadata"); - - b.HasDiscriminator().HasValue("EpisodeMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Country") - .HasColumnName("MovieMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("MovieMetadata_MovieMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Outline") - .HasColumnName("MovieMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnName("MovieMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("Tagline") - .HasColumnName("MovieMetadata_Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("MovieMetadata_MovieMetadata_Id"); - - b.ToTable("MovieMetadata"); - - b.HasDiscriminator().HasValue("MovieMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Barcode") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("Country") - .HasColumnName("MusicAlbumMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("LabelNumber") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("MusicAlbumMetadata_MusicAlbumMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("MusicAlbumMetadata_MusicAlbumMetadata_Id"); - - b.ToTable("MusicAlbumMetadata"); - - b.HasDiscriminator().HasValue("MusicAlbumMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("PhotoMetadata_PhotoMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("PhotoMetadata_PhotoMetadata_Id"); - - b.ToTable("PhotoMetadata"); - - b.HasDiscriminator().HasValue("PhotoMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Outline") - .HasColumnName("SeasonMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("SeasonMetadata_SeasonMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("SeasonMetadata_SeasonMetadata_Id"); - - b.ToTable("SeasonMetadata"); - - b.HasDiscriminator().HasValue("SeasonMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("Country") - .HasColumnName("SeriesMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property("Outline") - .HasColumnName("SeriesMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property("Plot") - .HasColumnName("SeriesMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("SeriesMetadata_SeriesMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property("Tagline") - .HasColumnName("SeriesMetadata_Tagline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasIndex("SeriesMetadata_SeriesMetadata_Id"); - - b.ToTable("SeriesMetadata"); - - b.HasDiscriminator().HasValue("SeriesMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => - { - b.HasBaseType("Jellyfin.Data.Entities.Metadata"); - - b.Property("TrackMetadata_TrackMetadata_Id") - .HasColumnType("INTEGER"); - - b.HasIndex("TrackMetadata_TrackMetadata_Id"); - - b.ToTable("TrackMetadata"); - - b.HasDiscriminator().HasValue("TrackMetadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Artwork") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Chapter", b => - { - b.HasOne("Jellyfin.Data.Entities.Release", null) - .WithMany("Chapters") - .HasForeignKey("Chapter_Chapters_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.HasOne("Jellyfin.Data.Entities.Collection", null) - .WithMany("CollectionItem") - .HasForeignKey("CollectionItem_CollectionItem_Id"); - - b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Next") - .WithMany() - .HasForeignKey("CollectionItem_Next_Id"); - - b.HasOne("Jellyfin.Data.Entities.CollectionItem", "Previous") - .WithMany() - .HasForeignKey("CollectionItem_Previous_Id"); - - b.HasOne("Jellyfin.Data.Entities.LibraryItem", "LibraryItem") - .WithMany() - .HasForeignKey("LibraryItem_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Company", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbumMetadata", null) - .WithMany("Labels") - .HasForeignKey("Company_Labels_Id"); - - b.HasOne("Jellyfin.Data.Entities.SeriesMetadata", null) - .WithMany("Networks") - .HasForeignKey("Company_Networks_Id"); - - b.HasOne("Jellyfin.Data.Entities.Company", "Parent") - .WithMany() - .HasForeignKey("Company_Parent_Id"); - - b.HasOne("Jellyfin.Data.Entities.BookMetadata", null) - .WithMany("Publishers") - .HasForeignKey("Company_Publishers_Id"); - - b.HasOne("Jellyfin.Data.Entities.MovieMetadata", null) - .WithMany("Studios") - .HasForeignKey("Company_Studios_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Genre", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Genres") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Group", b => - { - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Groups") - .HasForeignKey("Group_Groups_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.HasOne("Jellyfin.Data.Entities.LibraryRoot", "LibraryRoot") - .WithMany() - .HasForeignKey("LibraryRoot_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.HasOne("Jellyfin.Data.Entities.Library", "Library") - .WithMany() - .HasForeignKey("Library_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.HasOne("Jellyfin.Data.Entities.Release", null) - .WithMany("MediaFiles") - .HasForeignKey("MediaFile_MediaFiles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.HasOne("Jellyfin.Data.Entities.MediaFile", null) - .WithMany("MediaFileStreams") - .HasForeignKey("MediaFileStream_MediaFileStreams_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.HasOne("Jellyfin.Data.Entities.Person", null) - .WithMany("Sources") - .HasForeignKey("MetadataProviderId_Sources_Id"); - - b.HasOne("Jellyfin.Data.Entities.PersonRole", null) - .WithMany("Sources") - .HasForeignKey("MetadataProviderId_Sources_Id"); - - b.HasOne("Jellyfin.Data.Entities.MetadataProvider", "MetadataProvider") - .WithMany() - .HasForeignKey("MetadataProvider_Id"); - - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Sources") - .HasForeignKey("PersonRole_PersonRoles_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("GroupPermissions") - .HasForeignKey("Permission_GroupPermissions_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Permissions") - .HasForeignKey("Permission_Permissions_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.HasOne("Jellyfin.Data.Entities.Artwork", "Artwork") - .WithMany() - .HasForeignKey("Artwork_Artwork_Id"); - - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("PersonRoles") - .HasForeignKey("PersonRole_PersonRoles_Id"); - - b.HasOne("Jellyfin.Data.Entities.Person", "Person") - .WithMany() - .HasForeignKey("Person_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b => - { - b.HasOne("Jellyfin.Data.Entities.Group", null) - .WithMany("ProviderMappings") - .HasForeignKey("ProviderMapping_ProviderMappings_Id"); - - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("ProviderMappings") - .HasForeignKey("ProviderMapping_ProviderMappings_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.HasOne("Jellyfin.Data.Entities.Metadata", null) - .WithMany("Ratings") - .HasForeignKey("PersonRole_PersonRoles_Id"); - - b.HasOne("Jellyfin.Data.Entities.RatingSource", "RatingType") - .WithMany() - .HasForeignKey("RatingSource_RatingType_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.RatingSource", b => - { - b.HasOne("Jellyfin.Data.Entities.MetadataProviderId", "Source") - .WithMany() - .HasForeignKey("MetadataProviderId_Source_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.HasOne("Jellyfin.Data.Entities.Book", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id"); - - b.HasOne("Jellyfin.Data.Entities.CustomItem", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id1"); - - b.HasOne("Jellyfin.Data.Entities.Episode", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id2"); - - b.HasOne("Jellyfin.Data.Entities.Movie", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id3"); - - b.HasOne("Jellyfin.Data.Entities.Photo", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id4"); - - b.HasOne("Jellyfin.Data.Entities.Track", null) - .WithMany("Releases") - .HasForeignKey("Release_Releases_Id") - .HasConstraintName("FK_Release_LibraryItem_Release_Releases_Id5"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Episode", b => - { - b.HasOne("Jellyfin.Data.Entities.Season", null) - .WithMany("Episodes") - .HasForeignKey("Episode_Episodes_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Season", b => - { - b.HasOne("Jellyfin.Data.Entities.Series", null) - .WithMany("Seasons") - .HasForeignKey("Season_Seasons_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Track", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) - .WithMany("Tracks") - .HasForeignKey("Track_Tracks_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.BookMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Book", null) - .WithMany("BookMetadata") - .HasForeignKey("BookMetadata_BookMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CompanyMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Company", null) - .WithMany("CompanyMetadata") - .HasForeignKey("CompanyMetadata_CompanyMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.CustomItem", null) - .WithMany("CustomItemMetadata") - .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.EpisodeMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Episode", null) - .WithMany("EpisodeMetadata") - .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MovieMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Movie", null) - .WithMany("MovieMetadata") - .HasForeignKey("MovieMetadata_MovieMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MusicAlbumMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.MusicAlbum", null) - .WithMany("MusicAlbumMetadata") - .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PhotoMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Photo", null) - .WithMany("PhotoMetadata") - .HasForeignKey("PhotoMetadata_PhotoMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeasonMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Season", null) - .WithMany("SeasonMetadata") - .HasForeignKey("SeasonMetadata_SeasonMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.SeriesMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Series", null) - .WithMany("SeriesMetadata") - .HasForeignKey("SeriesMetadata_SeriesMetadata_Id"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.TrackMetadata", b => - { - b.HasOne("Jellyfin.Data.Entities.Track", null) - .WithMany("TrackMetadata") - .HasForeignKey("TrackMetadata_TrackMetadata_Id"); - }); #pragma warning restore 612, 618 } } From 519d65b9c81af1970e9cd1f1055b47f34dbb5249 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 01:10:51 -0400 Subject: [PATCH 049/137] Remove unnecessary gitignore entry Visual Studio no longer generates this file when opening the solution since the project SDK has been set to the correct value --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 53b7f1ff0d..46f036ad91 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,6 @@ CorePlugins*/ ProgramData-Server*/ ProgramData-UI*/ MediaBrowser.WebDashboard/jellyfin-web/** -tests/**/launchSettings.json ################# ## Visual Studio From a7bc1d43411243eee5671d183dd8e7631401cdb9 Mon Sep 17 00:00:00 2001 From: Christoph Potas Date: Sun, 3 May 2020 16:13:05 +0200 Subject: [PATCH 050/137] ~ set Jellyfin.Server as startup project Signed-off-by: Christoph Potas --- MediaBrowser.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a1dbe80476..82e990f11d 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,8 +38,6 @@ 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 From ea82e2158c6ffda98d59d711cad36ff74ef14af8 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 15:31:17 -0400 Subject: [PATCH 051/137] Make sure logger factories are disposed during integration tests --- .../JellyfinApplicationFactory.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs index 5300d36610..558539761d 100644 --- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Concurrent; using System.IO; using Emby.Server.Implementations; @@ -22,7 +23,7 @@ namespace MediaBrowser.Api.Tests public class JellyfinApplicationFactory : WebApplicationFactory { private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); - private static readonly ConcurrentBag _appHosts = new ConcurrentBag(); + private static readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); /// /// Initializes a new instance of . @@ -70,15 +71,17 @@ namespace MediaBrowser.Api.Tests // Create a copy of the application configuration to use for startup var startupConfig = Program.CreateAppConfiguration(commandLineOpts, appPaths); - // Create the app host and initialize it 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())); - _appHosts.Add(appHost); + _disposableComponents.Add(appHost); var serviceCollection = new ServiceCollection(); appHost.Init(serviceCollection); @@ -104,12 +107,12 @@ namespace MediaBrowser.Api.Tests /// protected override void Dispose(bool disposing) { - foreach (var host in _appHosts) + foreach (var disposable in _disposableComponents) { - host.Dispose(); + disposable.Dispose(); } - _appHosts.Clear(); + _disposableComponents.Clear(); base.Dispose(disposing); } From e66b0001830c36e83a4f2fda125264d9b9527243 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 16:23:42 -0400 Subject: [PATCH 052/137] Enable StyleCop and FxCop analyzers for integration tests project Use a custom ruleset that derives from the base solution ruleset, overriding rules where necessary --- .../JellyfinApplicationFactory.cs | 2 +- .../MediaBrowser.Api.Tests.csproj | 10 +++++++++ .../MediaBrowser.Api.Tests.ruleset | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs index 558539761d..c39ed07de3 100644 --- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs @@ -26,7 +26,7 @@ namespace MediaBrowser.Api.Tests private static readonly ConcurrentBag _disposableComponents = new ConcurrentBag(); /// - /// Initializes a new instance of . + /// Initializes a new instance of the class. /// public JellyfinApplicationFactory() { diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj index b99d61f5a8..8ae110c32c 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -20,4 +20,14 @@ + + + + + + + + MediaBrowser.Api.Tests.ruleset + + diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset new file mode 100644 index 0000000000..5cf77ac4c3 --- /dev/null +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + From 927e7a24ed1c79957f9a39e7ffde4198f49ceb22 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 16:29:13 -0400 Subject: [PATCH 053/137] Add main ruleset as a solution item so it appears in Visual Studio --- MediaBrowser.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 82e990f11d..49859f0330 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -41,6 +41,7 @@ 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 From 151182b9a91632a4e750b1d806e33204abe18e2b Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 16:30:39 -0400 Subject: [PATCH 054/137] Fix settings in editorconfig file --- .editorconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) 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 From 8322d412f07d495fcfc030fcfa88624f226f4b36 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 3 May 2020 16:51:39 -0400 Subject: [PATCH 055/137] Move ruleset up one directory so it can be shared by all test projects --- MediaBrowser.sln | 3 +++ tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj | 2 +- .../MediaBrowser.Api.Tests.ruleset => jellyfin-tests.ruleset} | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) rename tests/{MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset => jellyfin-tests.ruleset} (94%) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 05d13816b1..18e578fb66 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -49,6 +49,9 @@ EndProject 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Common.Tests", "tests\Jellyfin.Common.Tests\Jellyfin.Common.Tests.csproj", "{DF194677-DFD3-42AF-9F75-D44D5A416478}" EndProject diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj index 8ae110c32c..f30e486900 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -27,7 +27,7 @@ - MediaBrowser.Api.Tests.ruleset + ../jellyfin-tests.ruleset diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset b/tests/jellyfin-tests.ruleset similarity index 94% rename from tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset rename to tests/jellyfin-tests.ruleset index 5cf77ac4c3..5a113e955e 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.ruleset +++ b/tests/jellyfin-tests.ruleset @@ -2,7 +2,7 @@ - + From 675cbd8a168a2096e87208de6eb9fe78bdc8d921 Mon Sep 17 00:00:00 2001 From: artiume Date: Sun, 3 May 2020 18:23:25 -0400 Subject: [PATCH 056/137] Update MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 131 ++++++++++++++++------------ 1 file changed, 76 insertions(+), 55 deletions(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 06efedb300..dea17b4efb 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -17,115 +17,134 @@ 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", - ".webm", - ".mts", - ".m2v", ".rec" + ".ts", + ".rmvb", + ".webm", + ".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" }, { ".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" }, + { ".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" }, + { ".ogg", "video/ogg" }, + { ".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" }, + // There's also audio/x-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(); @@ -157,6 +176,7 @@ namespace MediaBrowser.Model.Net } var ext = Path.GetExtension(path); + var beg = Path.GetFullPath(path); if (_mimeTypeLookup.TryGetValue(ext, out string result)) { @@ -184,6 +204,7 @@ namespace MediaBrowser.Model.Net // Misc if (string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase)) + || string.Equals(beg, "._*", StringComparison.OrdinalIgnoreCase)) { return "application/octet-stream"; } From 6aca2485329a01549aa5f926d8747cfa347fd291 Mon Sep 17 00:00:00 2001 From: Tin Pavelic Date: Sun, 3 May 2020 11:11:15 +0000 Subject: [PATCH 057/137] Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- .../Localization/Core/hr.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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" } From 27328118a0c1d3d271a9fda481ac05717ad6d044 Mon Sep 17 00:00:00 2001 From: x7aN Date: Sun, 3 May 2020 13:05:11 +0000 Subject: [PATCH 058/137] Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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", From 64ab8f8e7adfb4e7511011eca55159235c0ffb31 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Sun, 3 May 2020 11:13:02 +0000 Subject: [PATCH 059/137] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- .../Localization/Core/sl-SI.json | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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" } From 25651362bf5f23f240efb1dad924c3edca17e778 Mon Sep 17 00:00:00 2001 From: artiume Date: Mon, 4 May 2020 12:13:50 -0400 Subject: [PATCH 060/137] Update MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index dea17b4efb..49d8cacdf1 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -176,7 +176,6 @@ namespace MediaBrowser.Model.Net } var ext = Path.GetExtension(path); - var beg = Path.GetFullPath(path); if (_mimeTypeLookup.TryGetValue(ext, out string result)) { @@ -204,7 +203,6 @@ namespace MediaBrowser.Model.Net // Misc if (string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase)) - || string.Equals(beg, "._*", StringComparison.OrdinalIgnoreCase)) { return "application/octet-stream"; } From 661b0e9489bd9d836bc22b7ec864290da05c81df Mon Sep 17 00:00:00 2001 From: Nazar Bulavko Date: Mon, 4 May 2020 23:09:35 +0000 Subject: [PATCH 061/137] Added translation using Weblate (Ukrainian) --- Emby.Server.Implementations/Localization/Core/uk.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/uk.json diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -0,0 +1 @@ +{} From 183514ebe217cfb1b52b932dabe49d6d7708bf1a Mon Sep 17 00:00:00 2001 From: artiume Date: Tue, 5 May 2020 07:25:32 -0400 Subject: [PATCH 062/137] add azw3 --- MediaBrowser.Model/Net/MimeTypes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 49d8cacdf1..a1f2c1ce30 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -58,6 +58,7 @@ namespace MediaBrowser.Model.Net // 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" }, From 432aae0fcc848659bf938e8ecdafdfb2a7c1472e Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 5 May 2020 11:17:03 -0400 Subject: [PATCH 063/137] Add missing comma --- MediaBrowser.Model/Net/MimeTypes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index a1f2c1ce30..0ace4561e1 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Model.Net ".ogg", ".ogm", ".ogv", - ".rec" + ".rec", ".ts", ".rmvb", ".webm", From dcdafa48595af02a9499210f1285a5be413a8826 Mon Sep 17 00:00:00 2001 From: Brandon L Date: Tue, 5 May 2020 18:08:07 +0000 Subject: [PATCH 064/137] Translated using Weblate (French (Canada)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr_CA/ --- .../Localization/Core/fr-CA.json | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 2c9dae6a17..c2349ba5bb 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -94,5 +94,23 @@ "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 des sous-titres manquants", + "TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines d'internet.", + "TaskRefreshChannels": "Rafraîchir des chaines", + "TaskCleanTranscodeDescription": "Retirer des fichiers de transcodage de plus qu'un jour.", + "TaskCleanTranscode": "Nettoyer le directoire de transcodage", + "TaskUpdatePluginsDescription": "Télécharger et installer des mises à jours des plugins qui sont configurés m.à.j. automisés.", + "TaskUpdatePlugins": "Mise à jour des plugins", + "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.", + "TaskRefreshPeople": "Rafraîchir les acteurs", + "TaskCleanLogsDescription": "Retire les données qui ont plus que {0} jours.", + "TaskCleanLogs": "Nettoyer les données de directoire", + "TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour des nouveaux fichiers et rafraîchit les métadonnées.", + "TaskRefreshChapterImages": "Extraire des images du chapitre", + "TaskRefreshChapterImagesDescription": "Créer des vignettes pour des vidéos qui ont des chapitres", + "TaskRefreshLibrary": "Analyser la bibliothèque de média", + "TaskCleanCache": "Nettoyer le cache de directoire", + "TasksApplicationCategory": "Application" } From 0334b54ae7fb7678279d7dda8825fb887e1055c3 Mon Sep 17 00:00:00 2001 From: Nazar Bulavko Date: Mon, 4 May 2020 23:11:12 +0000 Subject: [PATCH 065/137] Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- .../Localization/Core/uk.json | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index 0967ef424b..b2e0b66fe1 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -1 +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": "Альбоми" +} From 0aff46631fb7aca1363fd86e3083563de85cdada Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 6 May 2020 07:38:19 -0400 Subject: [PATCH 066/137] Update MediaBrowser.Model/Net/MimeTypes.cs Co-authored-by: dkanada --- MediaBrowser.Model/Net/MimeTypes.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 0ace4561e1..fd5efb1f65 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -46,7 +46,6 @@ namespace MediaBrowser.Model.Net ".webm", ".wmv", ".wtv", - }; // http://en.wikipedia.org/wiki/Internet_media_type From 1058c80a411ee9c177760a1250766ef9fc52965d Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 6 May 2020 07:45:40 -0400 Subject: [PATCH 067/137] Update MediaBrowser.Model/Net/MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index fd5efb1f65..fc6a452692 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -134,7 +134,9 @@ namespace MediaBrowser.Model.Net { ".m4b", "audio/m4b" }, { ".mid", "audio/midi" }, { ".midi", "audio/midi" }, - // There's also audio/x-midi + // There's also audio/x-midi, but no testing has been done to associate an extension with two seperate mime types. + // { ".mid", "audio/x-midi" }, + // { ".midi", "audio/x-midi" }, { ".mp3", "audio/mpeg" }, { ".oga", "audio/ogg" }, { ".ogg", "audio/ogg" }, From d34b7f801b2feca804733328daa9b5b74d39a17b Mon Sep 17 00:00:00 2001 From: miguel marsa canals Date: Wed, 6 May 2020 09:47:34 +0000 Subject: [PATCH 068/137] Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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}", From 13884643292eeb236ba3e0fefec046bd9fb5a140 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 6 May 2020 08:23:56 -0400 Subject: [PATCH 069/137] Update MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index fc6a452692..ff92f996d3 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -134,9 +134,9 @@ namespace MediaBrowser.Model.Net { ".m4b", "audio/m4b" }, { ".mid", "audio/midi" }, { ".midi", "audio/midi" }, - // There's also audio/x-midi, but no testing has been done to associate an extension with two seperate mime types. - // { ".mid", "audio/x-midi" }, - // { ".midi", "audio/x-midi" }, + // There's also audio/x-midi, but no testing has been done to associate an extension with two seperate mime types. + // { ".mid", "audio/x-midi" }, + // { ".midi", "audio/x-midi" }, { ".mp3", "audio/mpeg" }, { ".oga", "audio/ogg" }, { ".ogg", "audio/ogg" }, From ba2134de13a94017038a23dee5656e1e0831783a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 6 May 2020 16:04:07 +0100 Subject: [PATCH 070/137] Made changes to message and exception class --- Jellyfin.Server/Program.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 069a10b1a0..f6d8dbbdf4 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -290,9 +290,9 @@ namespace Jellyfin.Server listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); } - catch (Exception ex) + catch (InvalidOperationException ex) { - _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); + _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."); } } } @@ -320,9 +320,9 @@ namespace Jellyfin.Server listenOptions.Protocols = HttpProtocols.Http1AndHttp2; }); } - catch (Exception ex) + catch (InvalidOperationException ex) { - _logger.LogError(ex, "Error whilst listing to https - Is a development certificate installed?"); + _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."); } } } From 57cf19f058a12810b0d52dc43d84c1796697ce84 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Wed, 6 May 2020 17:21:21 +0200 Subject: [PATCH 071/137] Fix variable declaration and follow sonarcloud suggestions --- Emby.Server.Implementations/Library/UserManager.cs | 5 +++-- MediaBrowser.Model/Dto/PublicUserDto.cs | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 903d43faa6..6537a6a86a 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -620,8 +620,9 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(user)); } - bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); - bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(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) && diff --git a/MediaBrowser.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs index d5fd431eb6..d4eec8b9df 100644 --- a/MediaBrowser.Model/Dto/PublicUserDto.cs +++ b/MediaBrowser.Model/Dto/PublicUserDto.cs @@ -1,6 +1,4 @@ using System; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Users; namespace MediaBrowser.Model.Dto { @@ -29,9 +27,10 @@ namespace MediaBrowser.Model.Dto /// /// 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 changeing password at the + /// first login. /// /// true if this instance has configured password; otherwise, false. - // FIXME this shouldn't be here, but it's necessary when changing password at the first login public bool HasConfiguredPassword { get; set; } /// From 1c210d930c36bcb4e0bacce238f905628ef75966 Mon Sep 17 00:00:00 2001 From: Oliver Moolman Date: Wed, 6 May 2020 13:33:16 +0000 Subject: [PATCH 072/137] Translated using Weblate (Afrikaans) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/af/ --- Emby.Server.Implementations/Localization/Core/af.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 41b667c1374794421a1f9d324ef5156609de8464 Mon Sep 17 00:00:00 2001 From: Stefan Petrushevski Date: Wed, 6 May 2020 19:48:52 +0000 Subject: [PATCH 073/137] Translated using Weblate (Macedonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/ --- Emby.Server.Implementations/Localization/Core/mk.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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": "Одржување" } From 5c6339d8fd4b12237c6cb8eb9d115d59c9c27ddf Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Thu, 7 May 2020 09:14:00 +0200 Subject: [PATCH 074/137] Fix typo in PublicUserDto Co-authored-by: Vasily --- MediaBrowser.Model/Dto/PublicUserDto.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Dto/PublicUserDto.cs b/MediaBrowser.Model/Dto/PublicUserDto.cs index d4eec8b9df..b6bfaf2e9b 100644 --- a/MediaBrowser.Model/Dto/PublicUserDto.cs +++ b/MediaBrowser.Model/Dto/PublicUserDto.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Model.Dto /// /// 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 changeing password at the + /// 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. @@ -45,4 +45,4 @@ namespace MediaBrowser.Model.Dto return Name ?? base.ToString(); } } -} \ No newline at end of file +} From 3e14b1b50fcd9e841ab0c08525af179cc034a4e2 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 7 May 2020 15:58:20 -0400 Subject: [PATCH 075/137] Remove ogg video mimetype --- MediaBrowser.Model/Net/MimeTypes.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index ff92f996d3..fe2fbe7e4f 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -117,7 +117,6 @@ namespace MediaBrowser.Model.Net { ".mkv", "video/x-matroska" }, { ".mov", "video/quicktime" }, { ".mpd", "video/vnd.mpeg.dash.mpd" }, - { ".ogg", "video/ogg" }, { ".ogv", "video/ogg" }, { ".ts", "video/mp2t" }, { ".webm", "video/webm" }, @@ -134,9 +133,6 @@ namespace MediaBrowser.Model.Net { ".m4b", "audio/m4b" }, { ".mid", "audio/midi" }, { ".midi", "audio/midi" }, - // There's also audio/x-midi, but no testing has been done to associate an extension with two seperate mime types. - // { ".mid", "audio/x-midi" }, - // { ".midi", "audio/x-midi" }, { ".mp3", "audio/mpeg" }, { ".oga", "audio/ogg" }, { ".ogg", "audio/ogg" }, From a517bd2e52571dacf4a536f633d9102735422f13 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 8 May 2020 14:32:41 +0300 Subject: [PATCH 076/137] Re-raise the exception that caused LiveTV stream to not open --- .../LiveTv/TunerHosts/SharedHttpStream.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 0e600202aa..f13b65722c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -118,6 +118,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts //OpenedMediaSource.SupportsDirectStream = true; //OpenedMediaSource.SupportsTranscoding = true; await taskCompletionSource.Task.ConfigureAwait(false); + if (taskCompletionSource.Task.Exception != null) + { + // Error happened during opening the stream, re-raise the exception to inform the caller + throw taskCompletionSource.Task.Exception; + } } private Task StartStreaming(HttpResponseInfo response, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) @@ -139,12 +144,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts cancellationToken).ConfigureAwait(false); } } - catch (OperationCanceledException) + catch (OperationCanceledException ex) { + Logger.LogWarning(ex, "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); } EnableStreamSharing = false; From 3401d55f41cac1840b8332b2b0843f58c09ad848 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 8 May 2020 23:11:43 +0300 Subject: [PATCH 077/137] Fixed yet another case of hanging on a bad stream --- .../LiveTv/TunerHosts/SharedHttpStream.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index f13b65722c..e41ced28b5 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; @@ -123,6 +124,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts // Error happened during opening the stream, re-raise the exception 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) @@ -146,7 +154,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } catch (OperationCanceledException ex) { - Logger.LogWarning(ex, "Copying of {0} to {1} was canceled", GetType().Name, TempFilePath); + Logger.LogInformation("Copying of {0} to {1} was canceled", GetType().Name, TempFilePath); openTaskCompletionSource.TrySetException(ex); } catch (Exception ex) @@ -154,6 +162,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts 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); From 0e8505fb961aa0e29528a8404ca6bc25a90f75a1 Mon Sep 17 00:00:00 2001 From: newton181 Date: Fri, 8 May 2020 21:51:54 +0000 Subject: [PATCH 078/137] Translated using Weblate (Spanish (Mexico)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/ --- Emby.Server.Implementations/Localization/Core/es-MX.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 58122cc06785739e67885b6aded0ef434ca1c6de Mon Sep 17 00:00:00 2001 From: andra5 Date: Fri, 8 May 2020 21:22:30 +0000 Subject: [PATCH 079/137] Translated using Weblate (German (Swiss)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gsw/ --- .../Localization/Core/gsw.json | 81 +++++++++++-------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index 9611e33f57..c8291a2020 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,16 @@ "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" } From f33876e7e351129ea2296b537b38f9c87fd67b70 Mon Sep 17 00:00:00 2001 From: timothyc824 Date: Sat, 9 May 2020 14:13:12 +0000 Subject: [PATCH 080/137] Translated using Weblate (Chinese (Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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": "應用程式" } From a262ecd9c77b0e906288de0890aee2d5dcad6ae4 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 9 May 2020 19:49:55 +0200 Subject: [PATCH 081/137] Add positionning cues to WebVTT writer --- MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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; From 9137069f6de03d8606928ca70f18c3e14aeaac71 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sun, 10 May 2020 13:53:04 +0200 Subject: [PATCH 082/137] Add more information to TmdbSeriesProvider --- .../Tmdb/TV/TmdbSeriesProvider.cs | 66 ++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index 7195dc42a7..4697133e64 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -26,11 +26,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; private readonly IServerConfigurationManager _configurationManager; @@ -39,6 +34,11 @@ namespace MediaBrowser.Providers.Tmdb.TV private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; + + internal static TmdbSeriesProvider Current { get; private set; } + public TmdbSeriesProvider( IJsonSerializer jsonSerializer, IFileSystem fileSystem, @@ -217,10 +217,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 +239,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 +307,50 @@ 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 }); + } } } } From d5ad53e4bb6bc60e9ab9e9f2a71a772bfe67c286 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sun, 10 May 2020 15:16:19 +0200 Subject: [PATCH 083/137] Add Director to role mapper for TMDb --- MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs | 8 +++++++- MediaBrowser.Providers/Tmdb/TmdbUtils.cs | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index 4697133e64..bee4dba16d 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -313,7 +313,13 @@ namespace MediaBrowser.Providers.Tmdb.TV { foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) { - var personInfo = new PersonInfo {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)) { diff --git a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs index 035b99c1a6..cf740fe54c 100644 --- a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs @@ -14,6 +14,12 @@ namespace MediaBrowser.Providers.Tmdb public static string MapCrewToPersonType(Crew crew) { + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) + && crew.Job.IndexOf("director", StringComparison.InvariantCultureIgnoreCase) != -1) + { + return PersonType.Director; + } + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) && crew.Job.IndexOf("producer", StringComparison.InvariantCultureIgnoreCase) != -1) { From 55cfa96b9f8127c6327702fe98407d771bb987b7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 10 May 2020 10:54:41 -0400 Subject: [PATCH 084/137] Apply review suggestions --- Jellyfin.Data/DbContexts/Jellyfin.cs | 1140 ----------------- Jellyfin.Data/Entities/Artwork.cs | 7 - Jellyfin.Data/Entities/Book.cs | 8 +- Jellyfin.Data/Entities/BookMetadata.cs | 7 +- Jellyfin.Data/Entities/Chapter.cs | 6 - Jellyfin.Data/Entities/Collection.cs | 6 - Jellyfin.Data/Entities/CollectionItem.cs | 6 - Jellyfin.Data/Entities/Company.cs | 5 - Jellyfin.Data/Entities/CompanyMetadata.cs | 9 +- Jellyfin.Data/Entities/CustomItem.cs | 7 +- Jellyfin.Data/Entities/CustomItemMetadata.cs | 10 +- Jellyfin.Data/Entities/Episode.cs | 8 +- Jellyfin.Data/Entities/EpisodeMetadata.cs | 9 +- Jellyfin.Data/Entities/Genre.cs | 6 - Jellyfin.Data/Entities/Group.cs | 5 - Jellyfin.Data/Entities/Library.cs | 6 - Jellyfin.Data/Entities/LibraryItem.cs | 6 - Jellyfin.Data/Entities/LibraryRoot.cs | 6 - Jellyfin.Data/Entities/MediaFile.cs | 5 - Jellyfin.Data/Entities/MediaFileStream.cs | 6 - Jellyfin.Data/Entities/Metadata.cs | 5 - Jellyfin.Data/Entities/MetadataProvider.cs | 6 - Jellyfin.Data/Entities/MetadataProviderId.cs | 6 - Jellyfin.Data/Entities/Movie.cs | 8 +- Jellyfin.Data/Entities/MovieMetadata.cs | 7 +- Jellyfin.Data/Entities/MusicAlbum.cs | 8 +- Jellyfin.Data/Entities/MusicAlbumMetadata.cs | 7 +- Jellyfin.Data/Entities/Permission.cs | 4 - Jellyfin.Data/Entities/Person.cs | 5 - Jellyfin.Data/Entities/PersonRole.cs | 5 - Jellyfin.Data/Entities/Photo.cs | 8 +- Jellyfin.Data/Entities/PhotoMetadata.cs | 9 +- Jellyfin.Data/Entities/Preference.cs | 6 - Jellyfin.Data/Entities/ProviderMapping.cs | 6 - Jellyfin.Data/Entities/Rating.cs | 6 - Jellyfin.Data/Entities/RatingSource.cs | 6 - Jellyfin.Data/Entities/Release.cs | 5 - Jellyfin.Data/Entities/Season.cs | 8 +- Jellyfin.Data/Entities/SeasonMetadata.cs | 8 +- Jellyfin.Data/Entities/Series.cs | 8 +- Jellyfin.Data/Entities/SeriesMetadata.cs | 7 +- Jellyfin.Data/Entities/Track.cs | 8 +- Jellyfin.Data/Entities/TrackMetadata.cs | 9 +- Jellyfin.Data/Entities/User.cs | 5 - Jellyfin.Data/Enums/ArtKind.cs | 4 +- Jellyfin.Data/Enums/MediaFileKind.cs | 4 +- Jellyfin.Data/Enums/PermissionKind.cs | 4 +- Jellyfin.Data/Enums/PersonRoleType.cs | 4 +- Jellyfin.Data/Enums/PreferenceKind.cs | 4 +- Jellyfin.Data/Enums/Weekday.cs | 4 +- Jellyfin.Data/ISavingChanges.cs | 9 + .../Jellyfin.Server.Implementations.csproj | 34 + Jellyfin.Server.Implementations/JellyfinDb.cs | 115 ++ MediaBrowser.sln | 6 + 54 files changed, 189 insertions(+), 1427 deletions(-) delete mode 100644 Jellyfin.Data/DbContexts/Jellyfin.cs create mode 100644 Jellyfin.Data/ISavingChanges.cs create mode 100644 Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj create mode 100644 Jellyfin.Server.Implementations/JellyfinDb.cs 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/Artwork.cs b/Jellyfin.Data/Entities/Artwork.cs index da31d686e0..bf3029368a 100644 --- a/Jellyfin.Data/Entities/Artwork.cs +++ b/Jellyfin.Data/Entities/Artwork.cs @@ -1,15 +1,8 @@ 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 { - [Table("Artwork")] public partial class Artwork { partial void Init(); diff --git a/Jellyfin.Data/Entities/Book.cs b/Jellyfin.Data/Entities/Book.cs index 7dda26f412..42d24e31d5 100644 --- a/Jellyfin.Data/Entities/Book.cs +++ b/Jellyfin.Data/Entities/Book.cs @@ -1,15 +1,9 @@ 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 { - [Table("Book")] public partial class Book : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Book() : base() + protected Book() { BookMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/BookMetadata.cs b/Jellyfin.Data/Entities/BookMetadata.cs index 8afd371631..d52fe76051 100644 --- a/Jellyfin.Data/Entities/BookMetadata.cs +++ b/Jellyfin.Data/Entities/BookMetadata.cs @@ -1,11 +1,6 @@ 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 { @@ -16,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected BookMetadata() : base() + protected BookMetadata() { Publishers = new HashSet(); diff --git a/Jellyfin.Data/Entities/Chapter.cs b/Jellyfin.Data/Entities/Chapter.cs index 1ee6a9c07e..d48cb9b627 100644 --- a/Jellyfin.Data/Entities/Chapter.cs +++ b/Jellyfin.Data/Entities/Chapter.cs @@ -1,15 +1,9 @@ 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 { - [Table("Chapter")] public partial class Chapter { partial void Init(); diff --git a/Jellyfin.Data/Entities/Collection.cs b/Jellyfin.Data/Entities/Collection.cs index d3ccb13f52..e2fa3a5bd3 100644 --- a/Jellyfin.Data/Entities/Collection.cs +++ b/Jellyfin.Data/Entities/Collection.cs @@ -1,15 +1,9 @@ -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 { - [Table("Collection")] public partial class Collection { partial void Init(); diff --git a/Jellyfin.Data/Entities/CollectionItem.cs b/Jellyfin.Data/Entities/CollectionItem.cs index f40158b203..4a3d066396 100644 --- a/Jellyfin.Data/Entities/CollectionItem.cs +++ b/Jellyfin.Data/Entities/CollectionItem.cs @@ -1,15 +1,9 @@ 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 { - [Table("CollectionItem")] public partial class CollectionItem { partial void Init(); diff --git a/Jellyfin.Data/Entities/Company.cs b/Jellyfin.Data/Entities/Company.cs index 5b8a21423c..0650271c65 100644 --- a/Jellyfin.Data/Entities/Company.cs +++ b/Jellyfin.Data/Entities/Company.cs @@ -1,15 +1,10 @@ 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 { - [Table("Company")] public partial class Company { partial void Init(); diff --git a/Jellyfin.Data/Entities/CompanyMetadata.cs b/Jellyfin.Data/Entities/CompanyMetadata.cs index 18357b326c..b3ec9c1a7f 100644 --- a/Jellyfin.Data/Entities/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/CompanyMetadata.cs @@ -1,15 +1,8 @@ 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 { - [Table("CompanyMetadata")] public partial class CompanyMetadata : Metadata { partial void Init(); @@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected CompanyMetadata() : base() + protected CompanyMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/CustomItem.cs b/Jellyfin.Data/Entities/CustomItem.cs index 29f4b62a6e..2006717bf2 100644 --- a/Jellyfin.Data/Entities/CustomItem.cs +++ b/Jellyfin.Data/Entities/CustomItem.cs @@ -1,11 +1,6 @@ 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 { @@ -16,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected CustomItem() : base() + protected CustomItem() { CustomItemMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/CustomItemMetadata.cs b/Jellyfin.Data/Entities/CustomItemMetadata.cs index 8fbccfdd83..e09e4467ac 100644 --- a/Jellyfin.Data/Entities/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/CustomItemMetadata.cs @@ -1,15 +1,7 @@ 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 { - [Table("CustomItemMetadata")] public partial class CustomItemMetadata : Metadata { partial void Init(); @@ -17,7 +9,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected CustomItemMetadata() : base() + protected CustomItemMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/Episode.cs b/Jellyfin.Data/Entities/Episode.cs index be358a0fd3..6f6baa14de 100644 --- a/Jellyfin.Data/Entities/Episode.cs +++ b/Jellyfin.Data/Entities/Episode.cs @@ -1,15 +1,9 @@ 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 { - [Table("Episode")] public partial class Episode : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Episode() : base() + 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. diff --git a/Jellyfin.Data/Entities/EpisodeMetadata.cs b/Jellyfin.Data/Entities/EpisodeMetadata.cs index a1f4adf7bf..e5431bf223 100644 --- a/Jellyfin.Data/Entities/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/EpisodeMetadata.cs @@ -1,15 +1,8 @@ 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 { - [Table("EpisodeMetadata")] public partial class EpisodeMetadata : Metadata { partial void Init(); @@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected EpisodeMetadata() : base() + protected EpisodeMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/Genre.cs b/Jellyfin.Data/Entities/Genre.cs index d38265d80b..38f289a8e3 100644 --- a/Jellyfin.Data/Entities/Genre.cs +++ b/Jellyfin.Data/Entities/Genre.cs @@ -1,15 +1,9 @@ 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 { - [Table("Genre")] public partial class Genre { partial void Init(); diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index 4b58120fc0..54f9f49057 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,15 +1,10 @@ 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 { - [Table("Group")] public partial class Group { partial void Init(); diff --git a/Jellyfin.Data/Entities/Library.cs b/Jellyfin.Data/Entities/Library.cs index f3faa8699c..c11c09e916 100644 --- a/Jellyfin.Data/Entities/Library.cs +++ b/Jellyfin.Data/Entities/Library.cs @@ -1,15 +1,9 @@ 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 { - [Table("Library")] public partial class Library { partial void Init(); diff --git a/Jellyfin.Data/Entities/LibraryItem.cs b/Jellyfin.Data/Entities/LibraryItem.cs index 29547562bb..af6c640b97 100644 --- a/Jellyfin.Data/Entities/LibraryItem.cs +++ b/Jellyfin.Data/Entities/LibraryItem.cs @@ -1,15 +1,9 @@ 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 { - [Table("LibraryItem")] public abstract partial class LibraryItem { partial void Init(); diff --git a/Jellyfin.Data/Entities/LibraryRoot.cs b/Jellyfin.Data/Entities/LibraryRoot.cs index 932e3edb8e..bbc23e1c96 100644 --- a/Jellyfin.Data/Entities/LibraryRoot.cs +++ b/Jellyfin.Data/Entities/LibraryRoot.cs @@ -1,15 +1,9 @@ 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 { - [Table("LibraryRoot")] public partial class LibraryRoot { partial void Init(); diff --git a/Jellyfin.Data/Entities/MediaFile.cs b/Jellyfin.Data/Entities/MediaFile.cs index dbb65a6f7f..719539e5c2 100644 --- a/Jellyfin.Data/Entities/MediaFile.cs +++ b/Jellyfin.Data/Entities/MediaFile.cs @@ -1,15 +1,10 @@ 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 { - [Table("MediaFile")] public partial class MediaFile { partial void Init(); diff --git a/Jellyfin.Data/Entities/MediaFileStream.cs b/Jellyfin.Data/Entities/MediaFileStream.cs index 3ce18b8d71..7b3399731a 100644 --- a/Jellyfin.Data/Entities/MediaFileStream.cs +++ b/Jellyfin.Data/Entities/MediaFileStream.cs @@ -1,15 +1,9 @@ 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 { - [Table("MediaFileStream")] public partial class MediaFileStream { partial void Init(); diff --git a/Jellyfin.Data/Entities/Metadata.cs b/Jellyfin.Data/Entities/Metadata.cs index 5cba24ee3f..467ee68226 100644 --- a/Jellyfin.Data/Entities/Metadata.cs +++ b/Jellyfin.Data/Entities/Metadata.cs @@ -1,15 +1,10 @@ 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 { - [Table("Metadata")] public abstract partial class Metadata { partial void Init(); diff --git a/Jellyfin.Data/Entities/MetadataProvider.cs b/Jellyfin.Data/Entities/MetadataProvider.cs index bc6e04277a..4e4f107fb9 100644 --- a/Jellyfin.Data/Entities/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/MetadataProvider.cs @@ -1,15 +1,9 @@ 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 { - [Table("MetadataProvider")] public partial class MetadataProvider { partial void Init(); diff --git a/Jellyfin.Data/Entities/MetadataProviderId.cs b/Jellyfin.Data/Entities/MetadataProviderId.cs index d381856f3d..926f223dea 100644 --- a/Jellyfin.Data/Entities/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/MetadataProviderId.cs @@ -1,15 +1,9 @@ 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 { - [Table("MetadataProviderId")] public partial class MetadataProviderId { partial void Init(); diff --git a/Jellyfin.Data/Entities/Movie.cs b/Jellyfin.Data/Entities/Movie.cs index 23a340b1b3..b359b42fcd 100644 --- a/Jellyfin.Data/Entities/Movie.cs +++ b/Jellyfin.Data/Entities/Movie.cs @@ -1,15 +1,9 @@ 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 { - [Table("Movie")] public partial class Movie : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Movie() : base() + protected Movie() { Releases = new HashSet(); MovieMetadata = new HashSet(); diff --git a/Jellyfin.Data/Entities/MovieMetadata.cs b/Jellyfin.Data/Entities/MovieMetadata.cs index 090761877e..319ae94e5a 100644 --- a/Jellyfin.Data/Entities/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/MovieMetadata.cs @@ -1,15 +1,10 @@ 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 { - [Table("MovieMetadata")] public partial class MovieMetadata : Metadata { partial void Init(); @@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected MovieMetadata() : base() + protected MovieMetadata() { Studios = new HashSet(); diff --git a/Jellyfin.Data/Entities/MusicAlbum.cs b/Jellyfin.Data/Entities/MusicAlbum.cs index fc9c122865..00cb8fe007 100644 --- a/Jellyfin.Data/Entities/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/MusicAlbum.cs @@ -1,15 +1,9 @@ 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 { - [Table("MusicAlbum")] public partial class MusicAlbum : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected MusicAlbum() : base() + protected MusicAlbum() { MusicAlbumMetadata = new HashSet(); Tracks = new HashSet(); diff --git a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs index 4bfe780d1e..b52ca65646 100644 --- a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs @@ -1,15 +1,10 @@ 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 { - [Table("MusicAlbumMetadata")] public partial class MusicAlbumMetadata : Metadata { partial void Init(); @@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected MusicAlbumMetadata() : base() + protected MusicAlbumMetadata() { Labels = new HashSet(); diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index 29ba9e1a45..0b5b52cbd0 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,15 +1,11 @@ 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 { - [Table("Permission")] public partial class Permission { partial void Init(); diff --git a/Jellyfin.Data/Entities/Person.cs b/Jellyfin.Data/Entities/Person.cs index 6a4ad52851..d893b7e394 100644 --- a/Jellyfin.Data/Entities/Person.cs +++ b/Jellyfin.Data/Entities/Person.cs @@ -1,15 +1,10 @@ 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 { - [Table("Person")] public partial class Person { partial void Init(); diff --git a/Jellyfin.Data/Entities/PersonRole.cs b/Jellyfin.Data/Entities/PersonRole.cs index 0c6cb2c6e1..9bd12c7fb0 100644 --- a/Jellyfin.Data/Entities/PersonRole.cs +++ b/Jellyfin.Data/Entities/PersonRole.cs @@ -1,15 +1,10 @@ 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 { - [Table("PersonRole")] public partial class PersonRole { partial void Init(); diff --git a/Jellyfin.Data/Entities/Photo.cs b/Jellyfin.Data/Entities/Photo.cs index 89f9764442..7abe628913 100644 --- a/Jellyfin.Data/Entities/Photo.cs +++ b/Jellyfin.Data/Entities/Photo.cs @@ -1,15 +1,9 @@ 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 { - [Table("Photo")] public partial class Photo : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Photo() : base() + protected Photo() { PhotoMetadata = new HashSet(); Releases = new HashSet(); diff --git a/Jellyfin.Data/Entities/PhotoMetadata.cs b/Jellyfin.Data/Entities/PhotoMetadata.cs index b3f796839f..c5502f707a 100644 --- a/Jellyfin.Data/Entities/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/PhotoMetadata.cs @@ -1,15 +1,8 @@ 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 { - [Table("PhotoMetadata")] public partial class PhotoMetadata : Metadata { partial void Init(); @@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected PhotoMetadata() : base() + protected PhotoMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 8b3ddb568f..505f52e6b0 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -1,15 +1,9 @@ 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 { - [Table("Preference")] public partial class Preference { partial void Init(); diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs index 0eb098a8ff..6197bd97b7 100644 --- a/Jellyfin.Data/Entities/ProviderMapping.cs +++ b/Jellyfin.Data/Entities/ProviderMapping.cs @@ -1,15 +1,9 @@ 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 { - [Table("ProviderMapping")] public partial class ProviderMapping { partial void Init(); diff --git a/Jellyfin.Data/Entities/Rating.cs b/Jellyfin.Data/Entities/Rating.cs index 46e8c3f117..f70ea8b338 100644 --- a/Jellyfin.Data/Entities/Rating.cs +++ b/Jellyfin.Data/Entities/Rating.cs @@ -1,15 +1,9 @@ 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 { - [Table("Rating")] public partial class Rating { partial void Init(); diff --git a/Jellyfin.Data/Entities/RatingSource.cs b/Jellyfin.Data/Entities/RatingSource.cs index 7e60fac437..070f1ae27e 100644 --- a/Jellyfin.Data/Entities/RatingSource.cs +++ b/Jellyfin.Data/Entities/RatingSource.cs @@ -1,18 +1,12 @@ 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 /// - [Table("RatingSource")] public partial class RatingSource { partial void Init(); diff --git a/Jellyfin.Data/Entities/Release.cs b/Jellyfin.Data/Entities/Release.cs index 91dd35a7f0..d1928fcf7e 100644 --- a/Jellyfin.Data/Entities/Release.cs +++ b/Jellyfin.Data/Entities/Release.cs @@ -1,15 +1,10 @@ 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 { - [Table("Release")] public partial class Release { partial void Init(); diff --git a/Jellyfin.Data/Entities/Season.cs b/Jellyfin.Data/Entities/Season.cs index 3928a4ba62..96e89cde05 100644 --- a/Jellyfin.Data/Entities/Season.cs +++ b/Jellyfin.Data/Entities/Season.cs @@ -1,15 +1,9 @@ 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 { - [Table("Season")] public partial class Season : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Season() : base() + 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. diff --git a/Jellyfin.Data/Entities/SeasonMetadata.cs b/Jellyfin.Data/Entities/SeasonMetadata.cs index f0e669a49e..64ecbfbfac 100644 --- a/Jellyfin.Data/Entities/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/SeasonMetadata.cs @@ -1,15 +1,9 @@ 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 { - [Table("SeasonMetadata")] public partial class SeasonMetadata : Metadata { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected SeasonMetadata() : base() + protected SeasonMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs index fecc229af9..097b9958e3 100644 --- a/Jellyfin.Data/Entities/Series.cs +++ b/Jellyfin.Data/Entities/Series.cs @@ -1,15 +1,9 @@ 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 { - [Table("Series")] public partial class Series : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Series() : base() + protected Series() { SeriesMetadata = new HashSet(); Seasons = new HashSet(); diff --git a/Jellyfin.Data/Entities/SeriesMetadata.cs b/Jellyfin.Data/Entities/SeriesMetadata.cs index 15818f9416..52691783f6 100644 --- a/Jellyfin.Data/Entities/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/SeriesMetadata.cs @@ -1,15 +1,10 @@ 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 { - [Table("SeriesMetadata")] public partial class SeriesMetadata : Metadata { partial void Init(); @@ -17,7 +12,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected SeriesMetadata() : base() + protected SeriesMetadata() { Networks = new HashSet(); diff --git a/Jellyfin.Data/Entities/Track.cs b/Jellyfin.Data/Entities/Track.cs index 50ee43042f..079d73d2bf 100644 --- a/Jellyfin.Data/Entities/Track.cs +++ b/Jellyfin.Data/Entities/Track.cs @@ -1,15 +1,9 @@ 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 { - [Table("Track")] public partial class Track : LibraryItem { partial void Init(); @@ -17,7 +11,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected Track() : base() + 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. diff --git a/Jellyfin.Data/Entities/TrackMetadata.cs b/Jellyfin.Data/Entities/TrackMetadata.cs index 84679ebb5d..86c9161f6e 100644 --- a/Jellyfin.Data/Entities/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/TrackMetadata.cs @@ -1,15 +1,8 @@ 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 { - [Table("TrackMetadata")] public partial class TrackMetadata : Metadata { partial void Init(); @@ -17,7 +10,7 @@ namespace Jellyfin.Data.Entities /// /// Default constructor. Protected due to required properties, but present because EF needs it. /// - protected TrackMetadata() : base() + protected TrackMetadata() { Init(); } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 715969dbf0..a81d5215bb 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,15 +1,10 @@ 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 { - [Table("User")] public partial class User { partial void Init(); diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs index 546e1533cc..6b69d68b28 100644 --- a/Jellyfin.Data/Enums/ArtKind.cs +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum ArtKind : Int32 + public enum ArtKind { Other, Poster, diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs index d249202282..12f48c5589 100644 --- a/Jellyfin.Data/Enums/MediaFileKind.cs +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum MediaFileKind : Int32 + public enum MediaFileKind { Main, Sidecar, diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs index 4447fdb773..1506471e86 100644 --- a/Jellyfin.Data/Enums/PermissionKind.cs +++ b/Jellyfin.Data/Enums/PermissionKind.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum PermissionKind : Int32 + public enum PermissionKind { IsAdministrator, IsHidden, diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs index 5621ffa4d0..6e52f2c851 100644 --- a/Jellyfin.Data/Enums/PersonRoleType.cs +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum PersonRoleType : Int32 + public enum PersonRoleType { Other, Director, diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs index e66a51cae1..cd2cb791af 100644 --- a/Jellyfin.Data/Enums/PreferenceKind.cs +++ b/Jellyfin.Data/Enums/PreferenceKind.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum PreferenceKind : Int32 + public enum PreferenceKind { MaxParentalRating, BlockedTags, diff --git a/Jellyfin.Data/Enums/Weekday.cs b/Jellyfin.Data/Enums/Weekday.cs index 58523a6c7f..b80a03a330 100644 --- a/Jellyfin.Data/Enums/Weekday.cs +++ b/Jellyfin.Data/Enums/Weekday.cs @@ -1,8 +1,6 @@ -using System; - namespace Jellyfin.Data.Enums { - public enum Weekday : Int32 + public enum Weekday { Sunday, Monday, 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.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj new file mode 100644 index 0000000000..a31f28f64a --- /dev/null +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp3.1 + false + true + true + + + + ../jellyfin.ruleset + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs new file mode 100644 index 0000000000..76343edf9d --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -0,0 +1,115 @@ +#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 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 entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + { + var saveEntity = entity.Entity as ISavingChanges; + saveEntity.OnSavingChanges(); + } + + return base.SaveChanges(); + } + } +} diff --git a/MediaBrowser.sln b/MediaBrowser.sln index a1dbe80476..a514d2e2b8 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -64,6 +64,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Controller.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Data", "Jellyfin.Data\Jellyfin.Data.csproj", "{F03299F2-469F-40EF-A655-3766F97A5702}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementations", "Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj", "{22C7DA3A-94F2-4E86-9CE6-86AB02B4F843}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -182,6 +184,10 @@ 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 + {22C7DA3A-94F2-4E86-9CE6-86AB02B4F843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22C7DA3A-94F2-4E86-9CE6-86AB02B4F843}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22C7DA3A-94F2-4E86-9CE6-86AB02B4F843}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22C7DA3A-94F2-4E86-9CE6-86AB02B4F843}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 43c22a58229892836df645031fb570f37994e19e Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 10 May 2020 14:36:11 -0400 Subject: [PATCH 085/137] Add GetLoopbackHttpApiUrl() helper method to replace forceHttps functionality Also refactor to use return a Uri instead of a string and use UriBuilder under the hood --- .../ApplicationHost.cs | 28 +++++++++---------- .../Browser/BrowserLauncher.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../HdHomerun/HdHomerunUdpStream.cs | 2 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 2 +- .../IServerApplicationHost.cs | 25 +++++++++++++---- 6 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8f20a4921f..2201c3cfc2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1229,28 +1229,28 @@ namespace Emby.Server.Implementations str.CopyTo(span.Slice(1)); span[^1] = ']'; - return GetLocalApiUrl(span); + return GetLocalApiUrl(span).ToString(); } - return GetLocalApiUrl(ipAddress.ToString()); + return GetLocalApiUrl(ipAddress.ToString()).ToString(); } /// - public string GetLocalApiUrl(ReadOnlySpan host) + public Uri GetLoopbackHttpApiUrl() { - var url = new StringBuilder(64); - url.Append(ListenWithHttps ? "https://" : "http://") - .Append(host) - .Append(':') - .Append(ListenWithHttps ? HttpsPort : HttpPort); + return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); + } - string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; - if (baseUrl.Length != 0) + /// + public Uri GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null) + { + 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 + }.Uri; } public Task> GetLocalIpAddresses(CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs index 96096e142a..ccb733b3ca 100644 --- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs +++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs @@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Browser { try { - string baseUrl = appHost.GetLocalApiUrl("localhost"); + Uri baseUrl = appHost.GetLocalApiUrl("localhost"); appHost.LaunchUrl(baseUrl + url); } catch (Exception ex) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 900f12062f..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") + "/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 03ee5bfb65..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") + "/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/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index d63588bbd1..083fcd0299 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts //OpenedMediaSource.Path = tempFile; //OpenedMediaSource.ReadAtNativeFramerate = true; - MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; MediaSource.Protocol = MediaProtocol.Http; //OpenedMediaSource.Path = TempFilePath; diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 8537e41800..0028bd6893 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -65,26 +65,41 @@ namespace MediaBrowser.Controller /// /// 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 . + /// IP address that can be found via . HTTPS will be preferred when available. /// /// A cancellation token that can be used to cancel the task. /// The server URL. Task GetLocalApiUrl(CancellationToken cancellationToken); /// - /// Gets a local (LAN) URL that can be used to access the API. + /// Gets a local (LAN) 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 to use in the URL. /// The API URL. - string GetLocalApiUrl(ReadOnlySpan hostname); + public Uri GetLoopbackHttpApiUrl(); /// - /// Gets a local (LAN) URL that can be used to access the API. + /// Gets a local (LAN) URL that can be used to access the API. HTTPS will be preferred when available. /// /// 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. + /// + /// 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. + Uri GetLocalApiUrl(ReadOnlySpan hostname, string scheme = null, int? port = null); + /// /// Open a URL in an external browser window. /// From 3abf870c1e321dbeb484e4e255000a27760d7bc9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 10 May 2020 18:07:56 -0400 Subject: [PATCH 086/137] Do not include a double slash in URLs when a base URL is not set --- Emby.Server.Implementations/ApplicationHost.cs | 15 ++++++++------- .../Browser/BrowserLauncher.cs | 10 +++++----- MediaBrowser.Controller/IServerApplicationHost.cs | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 10a7e879e9..693b049cd8 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1236,28 +1236,30 @@ namespace Emby.Server.Implementations str.CopyTo(span.Slice(1)); span[^1] = ']'; - return GetLocalApiUrl(span).ToString(); + return GetLocalApiUrl(span); } - return GetLocalApiUrl(ipAddress.ToString()).ToString(); + return GetLocalApiUrl(ipAddress.ToString()); } /// - public Uri GetLoopbackHttpApiUrl() + public string GetLoopbackHttpApiUrl() { return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); } /// - public Uri GetLocalApiUrl(ReadOnlySpan host, string scheme = null, int? port = null) + 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 { Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), Host = host.ToString(), Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), Path = ServerConfigurationManager.Configuration.BaseUrl - }.Uri; + }.ToString().TrimEnd('/'); } public Task> GetLocalIpAddresses(CancellationToken cancellationToken) @@ -1333,8 +1335,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 ccb733b3ca..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 { - Uri baseUrl = appHost.GetLocalApiUrl("localhost"); - appHost.LaunchUrl(baseUrl + url); + string baseUrl = appHost.GetLocalApiUrl("localhost"); + 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/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 0028bd6893..4f0ff1ee12 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Controller /// over HTTP (not HTTPS). /// /// The API URL. - public Uri GetLoopbackHttpApiUrl(); + string GetLoopbackHttpApiUrl(); /// /// Gets a local (LAN) URL that can be used to access the API. HTTPS will be preferred when available. @@ -98,7 +98,7 @@ namespace MediaBrowser.Controller /// preferring the HTTPS port, if available. /// /// The API URL. - Uri GetLocalApiUrl(ReadOnlySpan hostname, string scheme = null, int? port = null); + string GetLocalApiUrl(ReadOnlySpan hostname, string scheme = null, int? port = null); /// /// Open a URL in an external browser window. From a10aec695671cd2e90ff67bd7bc6e18b450a8b3f Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 10 May 2020 18:17:12 -0400 Subject: [PATCH 087/137] Fix merge --- Jellyfin.Server/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fba1db584c..b9895386f5 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -294,7 +294,7 @@ namespace Jellyfin.Server { _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); - if (appHost.ListenWithHttps && appHost.Certificate != null) + if (appHost.ListenWithHttps) { options.Listen(address, appHost.HttpsPort, listenOptions => { From 5b2973b01ad1176655de83cea3b6de4dcda0eefb Mon Sep 17 00:00:00 2001 From: Federico Antoniazzi Date: Mon, 11 May 2020 10:56:05 +0000 Subject: [PATCH 088/137] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 0758bbe9ce..6ce97cca36 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 {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", From 6c855e5f81e0b9ee84e1669fea3a31801add9a4f Mon Sep 17 00:00:00 2001 From: millallo Date: Mon, 11 May 2020 20:17:41 +0000 Subject: [PATCH 089/137] Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 6ce97cca36..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 fotografia {0}", + "CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}", "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", From b961c3c9ae21b2b9238228b978bbec212c1ff187 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Tue, 12 May 2020 15:05:58 +0200 Subject: [PATCH 090/137] Address suggestions --- .../Tmdb/TV/TmdbSeriesProvider.cs | 10 ++++-- MediaBrowser.Providers/Tmdb/TmdbUtils.cs | 31 +++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index bee4dba16d..ffc4a66d12 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -26,6 +26,8 @@ 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 IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; @@ -35,7 +37,6 @@ namespace MediaBrowser.Providers.Tmdb.TV private readonly ILibraryManager _libraryManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; internal static TmdbSeriesProvider Current { get; private set; } @@ -355,7 +356,12 @@ namespace MediaBrowser.Providers.Tmdb.TV continue; } - seriesResult.AddPerson(new PersonInfo { Name = person.Name.Trim(), Role = person.Job, Type = type }); + 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 cf740fe54c..7dacc74044 100644 --- a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs @@ -4,24 +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("director", StringComparison.InvariantCultureIgnoreCase) != -1) + && crew.Job.Contains("director", StringComparison.InvariantCultureIgnoreCase)) { return PersonType.Director; } if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) - && crew.Job.IndexOf("producer", StringComparison.InvariantCultureIgnoreCase) != -1) + && crew.Job.Contains("producer", StringComparison.InvariantCultureIgnoreCase)) { return PersonType.Producer; } From 32c118222647f121c0b17055c0ef158763c0b5d2 Mon Sep 17 00:00:00 2001 From: rapmue Date: Tue, 12 May 2020 13:55:09 +0000 Subject: [PATCH 091/137] Translated using Weblate (German (Swiss)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gsw/ --- .../Localization/Core/gsw.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index c8291a2020..8780a884ba 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -103,5 +103,16 @@ "TasksChannelsCategory": "Internet Kanäle", "TasksApplicationCategory": "Applikation", "TasksLibraryCategory": "Bibliothek", - "TasksMaintenanceCategory": "Verwaltung" + "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." } From bac4bf96a0e642f80f35bd6cdf3de17a9302b6c6 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 12 May 2020 12:50:17 -0400 Subject: [PATCH 092/137] Fix build errors --- Jellyfin.Server/Migrations/MigrationRunner.cs | 2 +- .../Routines/MigrateActivityLogDb.cs | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index c4927f8770..c7fa2af0ca 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -18,7 +18,7 @@ namespace Jellyfin.Server.Migrations { typeof(Routines.DisableTranscodingThrottling), typeof(Routines.CreateUserLoggingConfigFile), - typeof(Routines.MigrateActivityLogDb() + typeof(Routines.MigrateActivityLogDb) }; /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index 9f1f5b92eb..fe7ef01eef 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -5,8 +5,8 @@ using System.IO; using Emby.Server.Implementations.Data; using Jellyfin.Data.Entities; using Jellyfin.Server.Implementations; +using MediaBrowser.Controller; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -16,20 +16,31 @@ namespace Jellyfin.Server.Migrations.Routines { private const string DbFilename = "activitylog.db"; + private readonly ILogger _logger; + private readonly JellyfinDbProvider _provider; + private readonly IServerApplicationPaths _paths; + + 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(CoreAppHost host, ILogger logger) + public void Perform() { - var dataPath = host.ServerConfigurationManager.ApplicationPaths.DataPath; + var dataPath = _paths.DataPath; using (var connection = SQLite3.Open( Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null)) { - logger.LogInformation("Migrating the database may take a while, do not stop Jellyfin."); - using var dbContext = host.ServiceProvider.GetService(); + _logger.LogInformation("Migrating the 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"); @@ -75,7 +86,7 @@ namespace Jellyfin.Server.Migrations.Routines } catch (IOException e) { - logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); + _logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); } } From 62420a6eb195f3119dc640b134d689d0de193a85 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 12 May 2020 16:03:15 -0400 Subject: [PATCH 093/137] Remove support for injecting ILogger directly --- .../ApplicationHost.cs | 7 ------ .../Resolvers/Audio/MusicAlbumResolver.cs | 4 ++-- .../Resolvers/Audio/MusicArtistResolver.cs | 6 ++--- .../LiveTv/TunerHosts/M3UTunerHost.cs | 4 ++-- MediaBrowser.Api/Movies/TrailersService.cs | 12 +++++++--- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- .../Plugins/Omdb/OmdbEpisodeProvider.cs | 24 +++++++++++-------- .../Plugins/Omdb/OmdbItemProvider.cs | 15 +++++++----- 8 files changed, 40 insertions(+), 34 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ffc916b980..b6e75b3862 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -546,13 +546,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(); 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/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/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/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.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; } } - } } From e69ba3531e2cc93c79ccc10b828d563c96753d10 Mon Sep 17 00:00:00 2001 From: D Z Date: Tue, 12 May 2020 20:42:04 +0000 Subject: [PATCH 094/137] Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- Emby.Server.Implementations/Localization/Core/he.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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": "ערוצי אינטרנט" } From 92299be64cf00fd65ccec2e84cdb1c7f41c73de9 Mon Sep 17 00:00:00 2001 From: tanvir-ahmed-siddique Date: Tue, 12 May 2020 21:31:54 +0000 Subject: [PATCH 095/137] Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- Emby.Server.Implementations/Localization/Core/bn.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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": "রক্ষণাবেক্ষণ" } From 51cdb30741e5ad3d6ec9dc8a5383dc84d60f747a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Wed, 13 May 2020 09:46:29 -0400 Subject: [PATCH 096/137] Apply documentation suggestions from code review Co-authored-by: Vasily --- MediaBrowser.Controller/IServerApplicationHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 4f0ff1ee12..db330210ae 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Controller Task GetLocalApiUrl(CancellationToken cancellationToken); /// - /// Gets a local (LAN) URL that can be used to access the API using the loop-back IP address (127.0.0.1) + /// 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 API URL. @@ -87,6 +87,7 @@ namespace MediaBrowser.Controller /// /// 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. /// From 512725a7d1c1b45fa588d76046c262b96614f58a Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Wed, 13 May 2020 18:02:54 +0200 Subject: [PATCH 097/137] Fix style issue in TmdbSeriesProvider --- MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index ffc4a66d12..6e3c26c263 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -350,8 +350,8 @@ namespace MediaBrowser.Providers.Tmdb.TV // Normalize this var type = TmdbUtils.MapCrewToPersonType(person); - if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && - !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { continue; } From 511d20a100398baca38f24adfabc56f6f3cfac9c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 13 May 2020 15:03:35 -0400 Subject: [PATCH 098/137] Apply review suggestions --- .../Activity/ActivityLogEntryPoint.cs | 110 +++++------------- .../Devices/DeviceManager.cs | 105 ----------------- Jellyfin.Data/Entities/ActivityLog.cs | 47 ++++---- .../Activity/ActivityManager.cs | 2 +- .../Routines/MigrateActivityLogDb.cs | 67 +++++------ MediaBrowser.Api/Devices/DeviceService.cs | 36 ------ MediaBrowser.Api/Library/LibraryService.cs | 4 +- MediaBrowser.Api/System/ActivityLogService.cs | 6 - .../Devices/IDeviceManager.cs | 21 ---- MediaBrowser.sln | 4 +- 10 files changed, 88 insertions(+), 314 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 54894fd65b..3983824a3e 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -8,7 +8,6 @@ using Jellyfin.Data.Entities; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Authentication; -using MediaBrowser.Controller.Devices; 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,36 +94,18 @@ namespace Emby.Server.Implementations.Activity _userManager.UserPolicyUpdated += OnUserPolicyUpdated; _userManager.UserLockedOut += OnUserLockedOut; - _deviceManager.CameraImageUploaded += OnCameraImageUploaded; - return Task.CompletedTask; } - private async void OnCameraImageUploaded(object sender, GenericEventArgs e) - { - await CreateLogEntry(new ActivityLog( - string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("CameraImageUploadedFrom"), - e.Argument.Device.Name), - NotificationType.CameraImageUploaded.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace)) - .ConfigureAwait(false); - } - private async void OnUserLockedOut(object sender, GenericEventArgs e) { await CreateLogEntry(new ActivityLog( - string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("UserLockedOutWithName"), - e.Argument.Name), - NotificationType.UserLockedOut.ToString(), - e.Argument.Id, - DateTime.UtcNow, - LogLevel.Trace)) + string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserLockedOutWithName"), + e.Argument.Name), + NotificationType.UserLockedOut.ToString(), + e.Argument.Id)) .ConfigureAwait(false); } @@ -139,11 +116,9 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, - Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), + Notifications.NotificationEntryPoint.GetItemName(e.Item)), "SubtitleDownloadFailure", - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace) + Guid.Empty) { ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -181,9 +156,7 @@ namespace Emby.Server.Implementations.Activity GetItemName(item), e.DeviceName), GetPlaybackStoppedNotificationType(item.MediaType), - user.Id, - DateTime.UtcNow, - LogLevel.Trace)) + user.Id)) .ConfigureAwait(false); } @@ -218,9 +191,7 @@ namespace Emby.Server.Implementations.Activity GetItemName(item), e.DeviceName), GetPlaybackNotificationType(item.MediaType), - user.Id, - DateTime.UtcNow, - LogLevel.Trace)) + user.Id)) .ConfigureAwait(false); } @@ -287,9 +258,7 @@ namespace Emby.Server.Implementations.Activity session.UserName, session.DeviceName), "SessionEnded", - session.UserId, - DateTime.UtcNow, - LogLevel.Trace) + session.UserId) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -308,9 +277,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name), "AuthenticationSucceeded", - user.Id, - DateTime.UtcNow, - LogLevel.Trace) + user.Id) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -327,10 +294,9 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username), "AuthenticationFailed", - Guid.Empty, - DateTime.UtcNow, - LogLevel.Error) + Guid.Empty) { + LogSeverity = LogLevel.Error, ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("LabelIpAddressValue"), @@ -346,9 +312,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name), "UserPolicyUpdated", - e.Argument.Id, - DateTime.UtcNow, - LogLevel.Trace)) + e.Argument.Id)) .ConfigureAwait(false); } @@ -360,9 +324,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name), "UserDeleted", - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace)) + Guid.Empty)) .ConfigureAwait(false); } @@ -374,9 +336,8 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name), "UserPasswordChanged", - e.Argument.Id, - DateTime.UtcNow, - LogLevel.Trace)).ConfigureAwait(false); + e.Argument.Id)) + .ConfigureAwait(false); } private async void OnUserCreated(object sender, GenericEventArgs e) @@ -387,9 +348,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name), "UserCreated", - e.Argument.Id, - DateTime.UtcNow, - LogLevel.Trace)) + e.Argument.Id)) .ConfigureAwait(false); } @@ -409,9 +368,7 @@ namespace Emby.Server.Implementations.Activity session.UserName, session.DeviceName), "SessionStarted", - session.UserId, - DateTime.UtcNow, - LogLevel.Trace) + session.UserId) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -428,9 +385,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name), NotificationType.PluginUpdateInstalled.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace) + Guid.Empty) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -448,9 +403,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name), NotificationType.PluginUninstalled.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace)) + Guid.Empty)) .ConfigureAwait(false); } @@ -462,9 +415,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name), NotificationType.PluginInstalled.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace) + Guid.Empty) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -483,9 +434,7 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name), NotificationType.InstallationFailed.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Trace) + Guid.Empty) { ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -529,10 +478,9 @@ namespace Emby.Server.Implementations.Activity await CreateLogEntry(new ActivityLog( string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), NotificationType.TaskFailed.ToString(), - Guid.Empty, - DateTime.UtcNow, - LogLevel.Error) + Guid.Empty) { + LogSeverity = LogLevel.Error, Overview = string.Join(Environment.NewLine, vals), ShortOverview = runningTime }).ConfigureAwait(false); @@ -567,8 +515,6 @@ namespace Emby.Server.Implementations.Activity _userManager.UserDeleted -= OnUserDeleted; _userManager.UserPolicyUpdated -= OnUserPolicyUpdated; _userManager.UserLockedOut -= OnUserLockedOut; - - _deviceManager.CameraImageUploaded -= OnCameraImageUploaded; } /// @@ -588,7 +534,7 @@ namespace Emby.Server.Implementations.Activity { int years = days / DaysInYear; values.Add(CreateValueString(years, "year")); - days = days % DaysInYear; + days %= DaysInYear; } // Number of months diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 579cb895e4..7017be6ebd 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -34,7 +34,6 @@ 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; @@ -43,9 +42,6 @@ namespace Emby.Server.Implementations.Devices public event EventHandler>> DeviceOptionsUpdated; - public event EventHandler> CameraImageUploaded; - - private readonly object _cameraUploadSyncLock = new object(); private readonly object _capabilitiesSyncLock = new object(); public DeviceManager( @@ -55,13 +51,11 @@ namespace Emby.Server.Implementations.Devices ILocalizationManager localizationManager, IUserManager userManager, IFileSystem fileSystem, - ILibraryMonitor libraryMonitor, IServerConfigurationManager config) { _json = json; _userManager = userManager; _fileSystem = fileSystem; - _libraryMonitor = libraryMonitor; _config = config; _libraryManager = libraryManager; _localizationManager = localizationManager; @@ -194,105 +188,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 diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 6338389913..df3026a770 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -1,15 +1,10 @@ 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; +using Microsoft.Extensions.Logging; namespace Jellyfin.Data.Entities { - [Table("ActivityLog")] public partial class ActivityLog { partial void Init(); @@ -35,23 +30,26 @@ namespace Jellyfin.Data.Entities /// /// /// - /// + /// /// - /// - public ActivityLog(string name, string type, Guid userid, DateTime datecreated, Microsoft.Extensions.Logging.LogLevel logseverity) + /// + public ActivityLog(string name, string type, Guid userId) { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (string.IsNullOrEmpty(type)) + { + throw new ArgumentNullException(nameof(type)); + } + this.Name = name; - - if (string.IsNullOrEmpty(type)) throw new ArgumentNullException(nameof(type)); this.Type = type; - - this.UserId = userid; - - this.DateCreated = datecreated; - - this.LogSeverity = logseverity; - + this.UserId = userId; + this.DateCreated = DateTime.UtcNow; + this.LogSeverity = LogLevel.Trace; Init(); } @@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities /// /// /// - /// + /// /// /// - public static ActivityLog Create(string name, string type, Guid userid, DateTime datecreated, Microsoft.Extensions.Logging.LogLevel logseverity) + public static ActivityLog Create(string name, string type, Guid userId) { - return new ActivityLog(name, type, userid, datecreated, logseverity); + return new ActivityLog(name, type, userId); } /************************************************************************* @@ -134,10 +132,10 @@ namespace Jellyfin.Data.Entities /// Required /// [Required] - public Microsoft.Extensions.Logging.LogLevel LogSeverity { get; set; } + public LogLevel LogSeverity { get; set; } /// - /// Required, ConcurrenyToken + /// Required, ConcurrencyToken. /// [ConcurrencyCheck] [Required] @@ -147,7 +145,6 @@ namespace Jellyfin.Data.Entities { RowVersion++; } - } } diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index d7bbf793c4..531b529dce 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -14,7 +14,7 @@ namespace Jellyfin.Server.Implementations.Activity /// public class ActivityManager : IActivityManager { - private JellyfinDbProvider _provider; + private readonly JellyfinDbProvider _provider; /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index fe7ef01eef..faa163d192 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -1,6 +1,5 @@ -#pragma warning disable CS1591 - using System; +using System.Collections.Generic; using System.IO; using Emby.Server.Implementations.Data; using Jellyfin.Data.Entities; @@ -12,6 +11,9 @@ 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"; @@ -20,6 +22,12 @@ namespace Jellyfin.Server.Migrations.Routines 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; @@ -27,19 +35,35 @@ namespace Jellyfin.Server.Migrations.Routines _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.LogInformation("Migrating the database may take a while, do not stop Jellyfin."); + _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"); @@ -56,9 +80,11 @@ namespace Jellyfin.Server.Migrations.Routines var newEntry = new ActivityLog( entry[1].ToString(), entry[4].ToString(), - entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString()), - entry[7].ReadDateTime(), - ParseLogLevel(entry[8].ToString())); + entry[6].SQLiteType == SQLiteType.Null ? Guid.Empty : Guid.Parse(entry[6].ToString())) + { + DateCreated = entry[7].ReadDateTime(), + LogSeverity = logLevelDictionary[entry[8].ToString()] + }; if (entry[2].SQLiteType != SQLiteType.Null) { @@ -75,6 +101,8 @@ namespace Jellyfin.Server.Migrations.Routines newEntry.ItemId = entry[5].ToString(); } + // Since code references the Id of the entries, this needs to be inserted in order. + // In order to do that, this is needed because EF Core doesn't provide a way to guarantee ordering for bulk inserts. dbContext.ActivityLogs.Add(newEntry); dbContext.SaveChanges(); } @@ -89,32 +117,5 @@ namespace Jellyfin.Server.Migrations.Routines _logger.LogError(e, "Error renaming legacy activity log database to 'activitylog.db.old'"); } } - - private LogLevel ParseLogLevel(string entry) - { - if (string.Equals(entry, "Debug", StringComparison.OrdinalIgnoreCase)) - { - return LogLevel.Debug; - } - - if (string.Equals(entry, "Information", StringComparison.OrdinalIgnoreCase) - || string.Equals(entry, "Info", StringComparison.OrdinalIgnoreCase)) - { - return LogLevel.Information; - } - - if (string.Equals(entry, "Warning", StringComparison.OrdinalIgnoreCase) - || string.Equals(entry, "Warn", StringComparison.OrdinalIgnoreCase)) - { - return LogLevel.Warning; - } - - if (string.Equals(entry, "Error", StringComparison.OrdinalIgnoreCase)) - { - return LogLevel.Error; - } - - return LogLevel.Trace; - } } } 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/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 997b1c45a8..93852e970c 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -762,9 +762,7 @@ namespace MediaBrowser.Api.Library _activityManager.Create(new Jellyfin.Data.Entities.ActivityLog( string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name), "UserDownloadingContent", - auth.UserId, - DateTime.UtcNow, - LogLevel.Trace) + auth.UserId) { ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device), }); diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index 0a5fc9433b..f2c37d7117 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -1,5 +1,3 @@ -using System; -using System.Globalization; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; @@ -49,10 +47,6 @@ namespace MediaBrowser.Api.System public object Get(GetActivityLogs request) { - DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? - (DateTime?)null : - DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - var result = _activityManager.GetPagedResult(request.StartIndex, request.Limit); return ToOptimizedResult(result); diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 77d5676310..4256bdb108 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -11,11 +11,6 @@ namespace MediaBrowser.Controller.Devices { public interface IDeviceManager { - /// - /// Occurs when [camera image uploaded]. - /// - event EventHandler> CameraImageUploaded; - /// /// Saves the capabilities. /// @@ -45,22 +40,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.sln b/MediaBrowser.sln index 210e2b6448..e100c0b1cd 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 +# 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 From 1e9b2613c690f4afe561eb8401705834bb51b6b8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 13 May 2020 15:35:14 -0400 Subject: [PATCH 099/137] Remove more unused code --- .../Devices/DeviceManager.cs | 58 +------------------ .../Devices/IDeviceManager.cs | 2 - 2 files changed, 2 insertions(+), 58 deletions(-) diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 7017be6ebd..158cc6a74e 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -20,7 +20,6 @@ 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; @@ -220,41 +219,6 @@ namespace Emby.Server.Implementations.Devices 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) @@ -311,27 +275,9 @@ namespace Emby.Server.Implementations.Devices _logger = logger; } - public async Task RunAsync() + public 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(); - } - } + return Task.CompletedTask; } #region IDisposable Support diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 4256bdb108..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; From 992574291821ba417ac624aeb0bf0022159b5c30 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 13 May 2020 17:55:31 -0400 Subject: [PATCH 100/137] Implement more review suggestions --- .../Devices/DeviceManager.cs | 129 ------------------ .../Routines/MigrateActivityLogDb.cs | 9 +- 2 files changed, 7 insertions(+), 131 deletions(-) diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 158cc6a74e..2283f2433a 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -5,26 +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.Querying; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; using MediaBrowser.Model.Users; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Devices { @@ -32,10 +24,7 @@ namespace Emby.Server.Implementations.Devices { private readonly IJsonSerializer _json; private readonly IUserManager _userManager; - private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localizationManager; private readonly IAuthenticationRepository _authRepo; private readonly Dictionary _capabilitiesCache; @@ -46,18 +35,12 @@ namespace Emby.Server.Implementations.Devices public DeviceManager( IAuthenticationRepository authRepo, IJsonSerializer json, - ILibraryManager libraryManager, - ILocalizationManager localizationManager, IUserManager userManager, - IFileSystem fileSystem, IServerConfigurationManager config) { _json = json; _userManager = userManager; - _fileSystem = fileSystem; _config = config; - _libraryManager = libraryManager; - _localizationManager = localizationManager; _authRepo = authRepo; _capabilitiesCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -187,38 +170,6 @@ namespace Emby.Server.Implementations.Devices return Path.Combine(GetDevicesPath(), id.GetMD5().ToString("N", CultureInfo.InvariantCulture)); } - 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); - } - public bool CanAccessDevice(User user, string deviceId) { if (user == null) @@ -258,84 +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 Task RunAsync() - { - return Task.CompletedTask; - } - - #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/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index faa163d192..1d684804da 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -77,13 +77,18 @@ namespace Jellyfin.Server.Migrations.Routines foreach (var entry in queryResult) { + 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 = logLevelDictionary[entry[8].ToString()] + LogSeverity = severity }; if (entry[2].SQLiteType != SQLiteType.Null) @@ -102,7 +107,7 @@ namespace Jellyfin.Server.Migrations.Routines } // Since code references the Id of the entries, this needs to be inserted in order. - // In order to do that, this is needed because EF Core doesn't provide a way to guarantee ordering for bulk inserts. + // In order to do that, we insert one by one because EF Core doesn't provide a way to guarantee ordering for bulk inserts. dbContext.ActivityLogs.Add(newEntry); dbContext.SaveChanges(); } From 2849d2b134691539ff990774073a8c03f2014918 Mon Sep 17 00:00:00 2001 From: aled Date: Wed, 13 May 2020 23:59:19 +0100 Subject: [PATCH 101/137] Fix compile warnings in Jellyfin.Naming.Tests --- .../Subtitles/SubtitleParserTests.cs | 6 ++-- .../TV/AbsoluteEpisodeNumberTests.cs | 2 +- .../TV/DailyEpisodeTests.cs | 12 ++++---- .../TV/EpisodeNumberWithoutSeasonTests.cs | 2 +- .../TV/EpisodeWithoutSeasonTests.cs | 6 ++-- .../TV/SeasonNumberTests.cs | 2 +- .../TV/SimpleEpisodeTests.cs | 6 ++-- .../Video/Format3DTests.cs | 4 +-- .../Video/MultiVersionTests.cs | 28 +++++++++---------- .../Jellyfin.Naming.Tests/Video/StubTests.cs | 4 +-- .../Video/VideoListResolverTests.cs | 2 +- .../Video/VideoResolverTests.cs | 22 +++++++-------- 12 files changed, 48 insertions(+), 48 deletions(-) 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..8e58b9243a 100644 --- a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs @@ -23,12 +23,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..e8348f6fe1 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs @@ -30,7 +30,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/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/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs index d2b3d6ff0d..69de96a47a 100644 --- a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -25,8 +25,8 @@ namespace Jellyfin.Naming.Tests.Video var result = 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] diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 03fe32b6e1..4b1ab6c883 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiEdition1() + private void TestMultiEdition1() { var files = new[] { @@ -37,7 +37,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiEdition2() + private void TestMultiEdition2() { var files = new[] { @@ -85,7 +85,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestLetterFolders() + private void TestLetterFolders() { var files = new[] { @@ -114,7 +114,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersionLimit() + private void TestMultiVersionLimit() { var files = new[] { @@ -144,7 +144,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersionLimit2() + private void TestMultiVersionLimit2() { var files = new[] { @@ -175,7 +175,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion3() + private void TestMultiVersion3() { var files = new[] { @@ -202,7 +202,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion4() + private void TestMultiVersion4() { // Test for false positive @@ -231,7 +231,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion5() + private void TestMultiVersion5() { var files = new[] { @@ -264,7 +264,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion6() + private void TestMultiVersion6() { var files = new[] { @@ -297,7 +297,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion7() + private void TestMultiVersion7() { var files = new[] { @@ -319,7 +319,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion8() + private void TestMultiVersion8() { // This is not actually supported yet @@ -353,7 +353,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion9() + private void TestMultiVersion9() { // Test for false positive @@ -382,7 +382,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion10() + private void TestMultiVersion10() { var files = new[] { @@ -406,7 +406,7 @@ namespace Jellyfin.Naming.Tests.Video // FIXME // [Fact] - public void TestMultiVersion11() + private void TestMultiVersion11() { // Currently not supported but we should probably handle this. diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs index e31d97e2e3..30ba941365 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs @@ -31,10 +31,10 @@ namespace Jellyfin.Naming.Tests.Video var result = 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 isStubResult = StubResolver.TryResolveFile(path, _namingOptions, out var stubTypeResult); diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs index 566dc9f7c1..4832d1593d 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Naming.Tests.Video 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[] diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index 114735ceed..a901c54702 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -184,17 +184,17 @@ namespace Jellyfin.Naming.Tests.Video var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path); 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); + 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); } } } From 428e1b04fc942b66dafaa04081d3b9dc5de62f1d Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 18:11:32 +0200 Subject: [PATCH 102/137] Add color transfer to ffprobe results --- MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 7 +++++++ .../Probing/ProbeResultNormalizer.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index 0b2f1d231e..fa51e61a26 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -278,5 +278,12 @@ 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; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b24d97f4ef..41daa22d6d 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -695,6 +695,11 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.RefFrames = streamInfo.Refs; } + + if (!string.IsNullOrEmpty(streamInfo.ColorTransfer)) + { + stream.ColorTransfer = streamInfo.ColorTransfer; + } } else { From 234292453f9b05f1fd6c2a00280a1a4b4254a4fa Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 18:44:51 +0200 Subject: [PATCH 103/137] Add HLG to the video range detection --- MediaBrowser.Model/Entities/MediaStream.cs | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index e7e8d7cecd..dd17623bde 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 From 2e18142bb32554cf162827ab1ca7a8040107baea Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Thu, 14 May 2020 18:52:42 +0200 Subject: [PATCH 104/137] Add color primaries to ffprobe output --- MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 7 +++++++ .../Probing/ProbeResultNormalizer.cs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index fa51e61a26..d7b0e0e644 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -285,5 +285,12 @@ namespace MediaBrowser.MediaEncoding.Probing /// The color transfer. [JsonPropertyName("color_transfer")] public string ColorTransfer { get; set; } + + /// + /// Gets or sets the color transfer. + /// + /// The color transfer. + [JsonPropertyName("color_primaries")] + public string ColorPrimaries { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 41daa22d6d..d3f8094b94 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -700,6 +700,11 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.ColorTransfer = streamInfo.ColorTransfer; } + + if (!string.IsNullOrEmpty(streamInfo.ColorPrimaries)) + { + stream.ColorPrimaries = streamInfo.ColorPrimaries; + } } else { From 3ff6e3ff65200094db881436be4deb9b7aee1763 Mon Sep 17 00:00:00 2001 From: aled Date: Thu, 14 May 2020 18:59:10 +0100 Subject: [PATCH 105/137] Add code analyzers to Jellyfin.Naming.Tests and fix resulting warnings --- .../Jellyfin.Naming.Tests.csproj | 12 ++++++++++- .../TV/DailyEpisodeTests.cs | 2 -- .../TV/EpisodeNumberWithoutSeasonTests.cs | 1 - .../TV/EpisodePathParserTest.cs | 3 +-- .../Video/MultiVersionTests.cs | 15 -------------- .../Jellyfin.Naming.Tests/Video/StackTests.cs | 10 +++++----- .../Video/VideoListResolverTests.cs | 20 +------------------ .../Video/VideoResolverTests.cs | 1 - 8 files changed, 18 insertions(+), 46 deletions(-) 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/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs index 8e58b9243a..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)] diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs index e8348f6fe1..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")] 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/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 4b1ab6c883..4198d69ff8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -28,7 +28,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -53,7 +52,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -76,7 +74,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -104,7 +101,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(7, result.Count); @@ -134,7 +130,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -165,7 +160,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(9, result.Count); @@ -192,7 +186,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -221,7 +214,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -251,7 +243,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -284,7 +275,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -311,7 +301,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -340,7 +329,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -372,7 +360,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -396,7 +383,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -422,7 +408,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs index 3630a07e46..8794d3ebe8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs @@ -368,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(); diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs index 4832d1593d..12c4a50fe3 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -9,6 +9,7 @@ namespace Jellyfin.Naming.Tests.Video public class VideoListResolverTests { private readonly NamingOptions _namingOptions = new NamingOptions(); + // FIXME // [Fact] private void TestStackAndExtras() @@ -45,7 +46,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -74,7 +74,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -95,7 +94,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -116,7 +114,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -138,7 +135,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -159,7 +155,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -184,7 +179,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(5, result.Count); @@ -205,7 +199,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = true, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -227,7 +220,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = true, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -249,7 +241,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -271,7 +262,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -294,7 +284,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -317,7 +306,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(2, result.Count); @@ -337,7 +325,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -357,7 +344,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -378,7 +364,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -399,7 +384,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); @@ -422,7 +406,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Equal(4, result.Count); @@ -443,7 +426,6 @@ namespace Jellyfin.Naming.Tests.Video { IsDirectory = false, FullName = i - }).ToList()).ToList(); Assert.Single(result); diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index a901c54702..99828b2eb7 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -176,7 +176,6 @@ namespace Jellyfin.Naming.Tests.Video }; } - [Theory] [MemberData(nameof(GetResolveFileTestData))] public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) From 4eb4ad3be7909f7a42aadcd442c0c7b77ce63c01 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 14 May 2020 17:03:53 -0400 Subject: [PATCH 106/137] Update Books Resolver File Types --- .../Library/Resolvers/Books/BookResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From b94afc597c4d51f67552c9ba2c25bdb8df6d8599 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 14 May 2020 17:13:45 -0400 Subject: [PATCH 107/137] Address review comments --- .../ApplicationHost.cs | 11 --- .../ServerConfigurationManager.cs | 6 -- .../Emby.Server.Implementations.csproj | 3 +- Jellyfin.Data/Entities/ActivityLog.cs | 83 ++++++++++--------- Jellyfin.Data/Jellyfin.Data.csproj | 6 +- .../Activity/ActivityManager.cs | 14 ++-- .../Jellyfin.Server.Implementations.csproj | 8 ++ Jellyfin.Server.Implementations/JellyfinDb.cs | 2 +- .../JellyfinDbProvider.cs | 2 +- ...20200514181226_AddActivityLog.Designer.cs} | 9 +- ...ma.cs => 20200514181226_AddActivityLog.cs} | 10 +-- .../Migrations/DesignTimeJellyfinDbFactory.cs | 7 +- .../Migrations/JellyfinDbModelSnapshot.cs | 4 +- Jellyfin.Server/CoreAppHost.cs | 14 ++++ Jellyfin.Server/Jellyfin.Server.csproj | 8 +- MediaBrowser.Api/System/ActivityLogService.cs | 13 ++- .../Activity/IActivityManager.cs | 3 +- .../Configuration/ServerConfiguration.cs | 2 - MediaBrowser.Model/Devices/DeviceOptions.cs | 9 ++ MediaBrowser.Model/Devices/DevicesOptions.cs | 23 ----- 20 files changed, 112 insertions(+), 125 deletions(-) rename Jellyfin.Server.Implementations/Migrations/{20200502231229_InitialSchema.Designer.cs => 20200514181226_AddActivityLog.Designer.cs} (92%) rename Jellyfin.Server.Implementations/Migrations/{20200502231229_InitialSchema.cs => 20200514181226_AddActivityLog.cs} (87%) create mode 100644 MediaBrowser.Model/Devices/DeviceOptions.cs delete mode 100644 MediaBrowser.Model/Devices/DevicesOptions.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 371b5a5b9b..8e5c3c9cf2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,8 +46,6 @@ using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; -using Jellyfin.Server.Implementations; -using Jellyfin.Server.Implementations.Activity; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -547,13 +545,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - // TODO: properly set up scoping and switch to AddDbContextPool - serviceCollection.AddDbContext( - options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), - ServiceLifetime.Transient); - - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton(); @@ -664,8 +655,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); 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/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index dccbe2a9a6..896e4310e7 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -9,7 +9,6 @@ - @@ -51,7 +50,7 @@ - netcoreapp3.1 + netstandard2.1 false true diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index df3026a770..8fbf6eaabf 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -5,34 +5,18 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Data.Entities { - public partial class ActivityLog + /// + /// An entity referencing an activity log entry. + /// + public partial class ActivityLog : ISavingChanges { - partial void Init(); - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. + /// Initializes a new instance of the class. + /// Public constructor with required data. /// - protected ActivityLog() - { - Init(); - } - - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static ActivityLog CreateActivityLogUnsafe() - { - return new ActivityLog(); - } - - /// - /// Public constructor with required data - /// - /// - /// - /// - /// - /// + /// The name. + /// The type. + /// The user id. public ActivityLog(string name, string type, Guid userId) { if (string.IsNullOrEmpty(name)) @@ -54,14 +38,21 @@ namespace Jellyfin.Data.Entities 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. public static ActivityLog Create(string name, string type, Guid userId) { return new ActivityLog(name, type, userId); @@ -72,7 +63,8 @@ namespace Jellyfin.Data.Entities *************************************************************************/ /// - /// Identity, Indexed, Required + /// Gets the identity of this instance. + /// This is the key in the backing database. /// [Key] [Required] @@ -80,7 +72,8 @@ namespace Jellyfin.Data.Entities public int Id { get; protected set; } /// - /// Required, Max length = 512 + /// Gets or sets the name. + /// Required, Max length = 512. /// [Required] [MaxLength(512)] @@ -88,21 +81,24 @@ namespace Jellyfin.Data.Entities public string Name { get; set; } /// - /// Max length = 512 + /// Gets or sets the overview. + /// Max length = 512. /// [MaxLength(512)] [StringLength(512)] public string Overview { get; set; } /// - /// Max length = 512 + /// Gets or sets the short overview. + /// Max length = 512. /// [MaxLength(512)] [StringLength(512)] public string ShortOverview { get; set; } /// - /// Required, Max length = 256 + /// Gets or sets the type. + /// Required, Max length = 256. /// [Required] [MaxLength(256)] @@ -110,41 +106,48 @@ namespace Jellyfin.Data.Entities public string Type { get; set; } /// - /// Required + /// Gets or sets the user id. + /// Required. /// [Required] public Guid UserId { get; set; } /// - /// Max length = 256 + /// Gets or sets the item id. + /// Max length = 256. /// [MaxLength(256)] [StringLength(256)] public string ItemId { get; set; } /// - /// Required + /// Gets or sets the date created. This should be in UTC. + /// Required. /// [Required] public DateTime DateCreated { get; set; } /// - /// Required + /// 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/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 8eae366bab..b2a3f7eb34 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -17,14 +17,10 @@ - + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 531b529dce..0b398b60cd 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -50,31 +50,31 @@ namespace Jellyfin.Server.Implementations.Activity /// public QueryResult GetPagedResult( - Func, IEnumerable> func, + Func, IQueryable> func, int? startIndex, int? limit) { using var dbContext = _provider.CreateContext(); - var result = func.Invoke(dbContext.ActivityLogs).AsQueryable(); + var query = func(dbContext.ActivityLogs).OrderByDescending(entry => entry.DateCreated).AsQueryable(); if (startIndex.HasValue) { - result = result.Where(entry => entry.Id >= startIndex.Value); + query = query.Skip(startIndex.Value); } if (limit.HasValue) { - result = result.OrderByDescending(entry => entry.DateCreated).Take(limit.Value); + 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 = result.Select(entry => ConvertToOldModel(entry)).ToList(); + var list = query.AsEnumerable().Select(ConvertToOldModel).ToList(); - return new QueryResult() + return new QueryResult { Items = list, - TotalRecordCount = list.Count + TotalRecordCount = dbContext.ActivityLogs.Count() }; } diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index a31f28f64a..149ca50209 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -25,6 +25,14 @@ + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 6fc8d251b8..23714b24a1 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -110,7 +110,7 @@ namespace Jellyfin.Server.Implementations foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) { var saveEntity = entity.Entity as ISavingChanges; - saveEntity.OnSavingChanges(); + saveEntity?.OnSavingChanges(); } return base.SaveChanges(); diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs index 8fdeab0887..eab531d386 100644 --- a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs +++ b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Implementations /// The newly created context. public JellyfinDb CreateContext() { - return _serviceProvider.GetService(); + return _serviceProvider.GetRequiredService(); } } } diff --git a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs similarity index 92% rename from Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs rename to Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs index e1ee9b34aa..98a83b7450 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs @@ -1,5 +1,4 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1601 +#pragma warning disable CS1591 // using System; @@ -12,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { [DbContext(typeof(JellyfinDb))] - [Migration("20200502231229_InitialSchema")] - partial class InitialSchema + [Migration("20200514181226_AddActivityLog")] + partial class AddActivityLog { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -65,7 +64,7 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.ToTable("ActivityLog"); + b.ToTable("ActivityLogs"); }); #pragma warning restore 612, 618 } diff --git a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs similarity index 87% rename from Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs rename to Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs index 42fac865ce..5e0b454d8b 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200502231229_InitialSchema.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 #pragma warning disable SA1601 using System; @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace Jellyfin.Server.Implementations.Migrations { - public partial class InitialSchema : Migration + public partial class AddActivityLog : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -14,7 +14,7 @@ namespace Jellyfin.Server.Implementations.Migrations name: "jellyfin"); migrationBuilder.CreateTable( - name: "ActivityLog", + name: "ActivityLogs", schema: "jellyfin", columns: table => new { @@ -32,14 +32,14 @@ namespace Jellyfin.Server.Implementations.Migrations }, constraints: table => { - table.PrimaryKey("PK_ActivityLog", x => x.Id); + table.PrimaryKey("PK_ActivityLogs", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "ActivityLog", + name: "ActivityLogs", schema: "jellyfin"); } } diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs index 23a0fdc784..a1b58eb5a9 100644 --- a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -1,6 +1,3 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1601 - using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; @@ -15,7 +12,9 @@ namespace Jellyfin.Server.Implementations.Migrations public JellyfinDb CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder(); - optionsBuilder.UseSqlite("Data Source=jellyfin.db"); + optionsBuilder.UseSqlite( + "Data Source=jellyfin.db", + opt => opt.MigrationsAssembly("Jellyfin.Migrations")); return new JellyfinDb(optionsBuilder.Options); } diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 27f5fe24b0..1e7ffd2359 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -1,9 +1,7 @@ // using System; -using Jellyfin.Server.Implementations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { @@ -60,7 +58,7 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.ToTable("ActivityLog"); + 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 4194070aa9..9eec6ed4eb 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -13,9 +13,6 @@ true true enable - - - True @@ -44,10 +41,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -67,6 +60,7 @@ + diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index f2c37d7117..a6bacad4fc 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -1,3 +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; @@ -47,7 +51,14 @@ namespace MediaBrowser.Api.System public object Get(GetActivityLogs request) { - var result = _activityManager.GetPagedResult(request.StartIndex, request.Limit); + DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ? + (DateTime?)null : + DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + + 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.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index 6742dc8fc4..9dab5e77b7 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -21,7 +20,7 @@ namespace MediaBrowser.Model.Activity QueryResult GetPagedResult(int? startIndex, int? limit); QueryResult GetPagedResult( - Func, IEnumerable> func, + Func, IQueryable> func, int? startIndex, int? limit); } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 22a42322a6..1f5981f101 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -79,8 +79,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableRemoteAccess { get; set; } - public bool CameraUploadUpgraded { get; set; } - public bool CollectionsUpgraded { 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; } - } -} From 953777f1ba4858f5186086e97910fcb88bfe3d61 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 14 May 2020 18:12:51 -0400 Subject: [PATCH 108/137] Removed unnecessary usings --- Emby.Server.Implementations/ApplicationHost.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 272a9355a8..e6410f8570 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -80,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; @@ -99,7 +98,6 @@ using MediaBrowser.Providers.Subtitles; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; From ce16651dbd43908770180054c4525e2188d95ed0 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 15 May 2020 01:55:00 +0300 Subject: [PATCH 109/137] Fix a check broken by https://github.com/jellyfin/jellyfin/pull/2105 --- Emby.Naming/Video/VideoListResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); From 3cb6fd8a2754837c787213c008ad84a973eb7cab Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Thu, 7 May 2020 20:36:50 -0700 Subject: [PATCH 110/137] Fix #3083: Set the Access-Control-Allow-Origin header to the request origin/host header if possible --- .../HttpServer/HttpListenerHost.cs | 35 ++++++++++++++++--- .../HttpServer/ResponseFilter.cs | 19 +++++++--- MediaBrowser.Controller/Net/IHttpServer.cs | 8 +++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 81e793f5c7..48cec87412 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -28,6 +28,7 @@ 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 @@ -454,9 +455,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(KeyValuePair header in GetCorsHeaders(httpReq)) + { + httpRes.Headers.Add(header.Key, header.Value); + } httpRes.ContentType = "text/plain"; await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); return; @@ -576,6 +578,31 @@ namespace Emby.Server.Implementations.HttpServer } } + /// + /// Get the default CORS headers + /// + /// + /// + public IDictionary GetCorsHeaders(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) { @@ -622,7 +649,7 @@ namespace Emby.Server.Implementations.HttpServer ResponseFilters = new Action[] { - new ResponseFilter(_logger).FilterResponse + new ResponseFilter(this, _logger).FilterResponse }; } diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 4089aa578e..2d4b31ef46 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(KeyValuePair header in _server.GetCorsHeaders(req)) + { + res.Headers.Add(header.Key, header.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/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index f1c4417613..eb2b4670af 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Model.Events; +using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net @@ -38,5 +39,12 @@ namespace MediaBrowser.Controller.Net /// /// Task RequestHandler(HttpContext context); + + /// + /// Get the default CORS headers + /// + /// + /// + IDictionary GetCorsHeaders(IRequest req); } } From c70c58923667a3c626b4112f783f755f91442d0b Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Wed, 13 May 2020 15:57:40 -0700 Subject: [PATCH 111/137] Update Emby.Server.Implementations/HttpServer/HttpListenerHost.cs from review Co-authored-by: Cody Robibero --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 48cec87412..958bb1e1d4 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -455,9 +455,9 @@ namespace Emby.Server.Implementations.HttpServer if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) { httpRes.StatusCode = 200; - foreach(KeyValuePair header in GetCorsHeaders(httpReq)) + foreach(var (key, value) in GetCorsHeaders(httpReq)) { - httpRes.Headers.Add(header.Key, header.Value); + httpRes.Headers.Add(key, value); } httpRes.ContentType = "text/plain"; await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false); From 6990af811ad65816a0534f75e889dc9c22632876 Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Thu, 14 May 2020 06:28:33 -0700 Subject: [PATCH 112/137] Use simpler dictionary iterator. --- Emby.Server.Implementations/HttpServer/ResponseFilter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 2d4b31ef46..c94e905afe 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -37,9 +37,9 @@ namespace Emby.Server.Implementations.HttpServer /// The dto. public void FilterResponse(IRequest req, HttpResponse res, object dto) { - foreach(KeyValuePair header in _server.GetCorsHeaders(req)) + foreach(var (key, value) in _server.GetCorsHeaders(req)) { - res.Headers.Add(header.Key, header.Value); + res.Headers.Add(key, value); } // Try to prevent compatibility view res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " + From 9ee10d22c8ccbeb9eb4112b1a9f520d5ed998013 Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Thu, 14 May 2020 16:03:45 -0700 Subject: [PATCH 113/137] Rename function --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 4 ++-- Emby.Server.Implementations/HttpServer/ResponseFilter.cs | 2 +- MediaBrowser.Controller/Net/IHttpServer.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 958bb1e1d4..794d55c049 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -455,7 +455,7 @@ namespace Emby.Server.Implementations.HttpServer if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) { httpRes.StatusCode = 200; - foreach(var (key, value) in GetCorsHeaders(httpReq)) + foreach(var (key, value) in GetDefaultCorsHeaders(httpReq)) { httpRes.Headers.Add(key, value); } @@ -583,7 +583,7 @@ namespace Emby.Server.Implementations.HttpServer /// /// /// - public IDictionary GetCorsHeaders(IRequest req) + public IDictionary GetDefaultCorsHeaders(IRequest req) { var origin = req.Headers["Origin"]; if (origin == StringValues.Empty) diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index c94e905afe..85c3db9b20 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer /// The dto. public void FilterResponse(IRequest req, HttpResponse res, object dto) { - foreach(var (key, value) in _server.GetCorsHeaders(req)) + foreach(var (key, value) in _server.GetDefaultCorsHeaders(req)) { res.Headers.Add(key, value); } diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index eb2b4670af..efb5f4ac3f 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -45,6 +45,6 @@ namespace MediaBrowser.Controller.Net /// /// /// - IDictionary GetCorsHeaders(IRequest req); + IDictionary GetDefaultCorsHeaders(IRequest req); } } From 7c571345353417db6990700b350fd22a2778ee4f Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 15 May 2020 02:30:28 +0300 Subject: [PATCH 114/137] Implement a cleanup migration --- Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Migrations/Routines/RemoveBuggedExtras.cs | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index ca17482820..7a881be0f6 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -17,7 +17,8 @@ namespace Jellyfin.Server.Migrations private static readonly Type[] _migrationTypes = { typeof(Routines.DisableTranscodingThrottling), - typeof(Routines.CreateUserLoggingConfigFile) + typeof(Routines.CreateUserLoggingConfigFile), + typeof(Routines.RemoveBuggedExtras) }; /// diff --git a/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs new file mode 100644 index 0000000000..7e45f99f94 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs @@ -0,0 +1,50 @@ +using System; +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 RemoveBuggedExtras : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _paths; + + public RemoveBuggedExtras(ILogger logger, IServerApplicationPaths paths) + { + _logger = logger; + _paths = paths; + } + + /// + public Guid Id => Guid.Parse("{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}"); + + /// + public string Name => "RemoveBuggedExtras"; + + /// + public void Perform() + { + var dataPath = _paths.DataPath; + using (var connection = SQLite3.Open( + Path.Combine(dataPath, DbFilename), + ConnectionFlags.ReadWrite, + null)) + { + 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()); + if (bads.Length != 0) + { + _logger.LogInformation("Removing found duplicated extras for the following items: {0}", 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')"); + } + } + } + } +} From dcaffd3812b3995e2158f4ea587599249016c03c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 14 May 2020 20:11:34 -0400 Subject: [PATCH 115/137] Fix regressions introduced by #3098 --- MediaBrowser.Api/BaseApiService.cs | 4 ++-- MediaBrowser.Api/Library/LibraryService.cs | 6 +++++- MediaBrowser.Api/Movies/MoviesService.cs | 2 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 2 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- MediaBrowser.Api/Playback/Progressive/AudioService.cs | 2 +- .../Progressive/BaseProgressiveStreamingService.cs | 2 +- MediaBrowser.Api/Playback/UniversalAudioService.cs | 9 ++++++--- MediaBrowser.Api/UserLibrary/ArtistsService.cs | 2 +- MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs | 2 +- 12 files changed, 22 insertions(+), 15 deletions(-) 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/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index a54640b2fd..2d1977d2e3 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, 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/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 4213193bac..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, 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/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, From ef533015ae8d2da48848cd97f847b4764bf13c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Fri, 15 May 2020 00:48:58 +0000 Subject: [PATCH 116/137] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emby.Server.Implementations/Localization/Core/nb.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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." } From 9ed8c6cb11d2221157db59d1f13f26ea00224877 Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 15 May 2020 07:59:46 -0400 Subject: [PATCH 117/137] Add opf mimetype --- MediaBrowser.Model/Net/MimeTypes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index fe2fbe7e4f..814151948e 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -67,6 +67,7 @@ namespace MediaBrowser.Model.Net { ".m3u8", "application/x-mpegURL" }, { ".map", "application/x-javascript" }, { ".mobi", "application/x-mobipocket-ebook" }, + { ".odf", "application/oebps-package+xml" }, { ".pdf", "application/pdf" }, { ".rar", "application/vnd.rar" }, { ".srt", "application/x-subrip" }, From eb3ce3fb8789ed697471f1fe14d2f85591527b56 Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 14 May 2020 12:42:22 -0400 Subject: [PATCH 118/137] Fix Progressive Stream 'P' capitalization --- MediaBrowser.Model/Entities/MediaStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index e7e8d7cecd..4a179776dc 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -199,7 +199,7 @@ namespace MediaBrowser.Model.Entities { return "1440I"; } - return "1440P"; + return "1440p"; } if (width >= 1900 || height >= 1000) { @@ -207,7 +207,7 @@ namespace MediaBrowser.Model.Entities { return "1080I"; } - return "1080P"; + return "1080p"; } if (width >= 1260 || height >= 700) { @@ -215,7 +215,7 @@ namespace MediaBrowser.Model.Entities { return "720I"; } - return "720P"; + return "720p"; } if (width >= 700 || height >= 440) { @@ -224,7 +224,7 @@ namespace MediaBrowser.Model.Entities { return "480I"; } - return "480P"; + return "480p"; } return "SD"; From d41cdb3b7a6ab9ec4b9b286ffe1a63f3e4e0996c Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 14 May 2020 13:27:23 -0400 Subject: [PATCH 119/137] Update Interlace --- MediaBrowser.Model/Entities/MediaStream.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 4a179776dc..784d659b6d 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -197,7 +197,7 @@ namespace MediaBrowser.Model.Entities { if (i.IsInterlaced) { - return "1440I"; + return "1440i"; } return "1440p"; } @@ -213,7 +213,7 @@ namespace MediaBrowser.Model.Entities { if (i.IsInterlaced) { - return "720I"; + return "720i"; } return "720p"; } @@ -222,7 +222,7 @@ namespace MediaBrowser.Model.Entities if (i.IsInterlaced) { - return "480I"; + return "480i"; } return "480p"; } From 527029af92585fbed5ff9bd619314ed0c31fe65b Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 15 May 2020 08:30:58 -0400 Subject: [PATCH 120/137] Update MediaStream.cs --- MediaBrowser.Model/Entities/MediaStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 784d659b6d..4a269c55b5 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -205,7 +205,7 @@ namespace MediaBrowser.Model.Entities { if (i.IsInterlaced) { - return "1080I"; + return "1080i"; } return "1080p"; } From a765272c179219bb8931cd4e90234a16d2114327 Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 15 May 2020 09:38:26 -0400 Subject: [PATCH 121/137] Update MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 814151948e..b491a015c0 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Model.Net { ".m3u8", "application/x-mpegURL" }, { ".map", "application/x-javascript" }, { ".mobi", "application/x-mobipocket-ebook" }, - { ".odf", "application/oebps-package+xml" }, + { ".opf", "application/oebps-package+xml" }, { ".pdf", "application/pdf" }, { ".rar", "application/vnd.rar" }, { ".srt", "application/x-subrip" }, From a5dee3680880d525a6c507deb7dd284b08b7ebdd Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 15 May 2020 12:51:18 -0400 Subject: [PATCH 122/137] Apply more review suggestions --- Jellyfin.Data/Entities/ActivityLog.cs | 3 ++- .../Activity/ActivityManager.cs | 7 +++---- Jellyfin.Server.Implementations/JellyfinDb.cs | 7 ++++--- .../Migrations/DesignTimeJellyfinDbFactory.cs | 4 +--- .../Migrations/Routines/MigrateActivityLogDb.cs | 5 ++--- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 8fbf6eaabf..522c206640 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -53,6 +53,7 @@ namespace Jellyfin.Data.Entities /// 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); @@ -63,7 +64,7 @@ namespace Jellyfin.Data.Entities *************************************************************************/ /// - /// Gets the identity of this instance. + /// Gets or sets the identity of this instance. /// This is the key in the backing database. /// [Key] diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 0b398b60cd..65ceee32bf 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -56,7 +55,7 @@ namespace Jellyfin.Server.Implementations.Activity { using var dbContext = _provider.CreateContext(); - var query = func(dbContext.ActivityLogs).OrderByDescending(entry => entry.DateCreated).AsQueryable(); + var query = func(dbContext.ActivityLogs.OrderByDescending(entry => entry.DateCreated)); if (startIndex.HasValue) { @@ -69,12 +68,12 @@ namespace Jellyfin.Server.Implementations.Activity } // This converts the objects from the new database model to the old for compatibility with the existing API. - var list = query.AsEnumerable().Select(ConvertToOldModel).ToList(); + var list = query.Select(ConvertToOldModel).ToList(); return new QueryResult { Items = list, - TotalRecordCount = dbContext.ActivityLogs.Count() + TotalRecordCount = func(dbContext.ActivityLogs).Count() }; } diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 23714b24a1..ec09a619f2 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -107,10 +107,11 @@ namespace Jellyfin.Server.Implementations public override int SaveChanges() { - foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + foreach (var saveEntity in ChangeTracker.Entries() + .Where(e => e.State == EntityState.Modified) + .OfType()) { - var saveEntity = entity.Entity as ISavingChanges; - saveEntity?.OnSavingChanges(); + saveEntity.OnSavingChanges(); } return base.SaveChanges(); diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs index a1b58eb5a9..72a4a8c3b6 100644 --- a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -12,9 +12,7 @@ namespace Jellyfin.Server.Implementations.Migrations public JellyfinDb CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder(); - optionsBuilder.UseSqlite( - "Data Source=jellyfin.db", - opt => opt.MigrationsAssembly("Jellyfin.Migrations")); + optionsBuilder.UseSqlite("Data Source=jellyfin.db"); return new JellyfinDb(optionsBuilder.Options); } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index 1d684804da..6f4fc4f281 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -106,11 +106,10 @@ namespace Jellyfin.Server.Migrations.Routines newEntry.ItemId = entry[5].ToString(); } - // Since code references the Id of the entries, this needs to be inserted in order. - // In order to do that, we insert one by one because EF Core doesn't provide a way to guarantee ordering for bulk inserts. dbContext.ActivityLogs.Add(newEntry); - dbContext.SaveChanges(); } + + dbContext.SaveChanges(); } try From a7c2e524a9571f4b6747908db94d0241d73ae5f3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 15 May 2020 14:08:46 -0400 Subject: [PATCH 123/137] Apply more review suggestions --- .../Routines/MigrateActivityLogDb.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index 6f4fc4f281..079b5b5dc4 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -1,6 +1,7 @@ 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; @@ -75,7 +76,7 @@ namespace Jellyfin.Server.Migrations.Routines dbContext.Database.ExecuteSqlRaw("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'ActivityLog';"); dbContext.SaveChanges(); - foreach (var entry in queryResult) + var newEntries = queryResult.Select(entry => { if (!logLevelDictionary.TryGetValue(entry[8].ToString(), out var severity)) { @@ -93,28 +94,35 @@ namespace Jellyfin.Server.Migrations.Routines if (entry[2].SQLiteType != SQLiteType.Null) { - newEntry.Overview = entry[2].ToString(); + newEntry.Overview = entry[2].ToString(); } if (entry[3].SQLiteType != SQLiteType.Null) { - newEntry.ShortOverview = entry[3].ToString(); + newEntry.ShortOverview = entry[3].ToString(); } if (entry[5].SQLiteType != SQLiteType.Null) { - newEntry.ItemId = entry[5].ToString(); + newEntry.ItemId = entry[5].ToString(); } - dbContext.ActivityLogs.Add(newEntry); - } + 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) { From 034fe97eebb709d87d7642151d6e0e5ec5f2a391 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 15 May 2020 21:32:56 +0300 Subject: [PATCH 124/137] Apply suggestions from code review Co-authored-by: Mark Monteiro --- Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs index 7e45f99f94..512bec0bf7 100644 --- a/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs +++ b/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs @@ -10,7 +10,7 @@ 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 RemoveBuggedExtras : IMigrationRoutine + internal class RemoveDuplicateExtras : IMigrationRoutine { private const string DbFilename = "library.db"; private readonly ILogger _logger; @@ -41,7 +41,7 @@ namespace Jellyfin.Server.Migrations.Routines var bads = string.Join(", ", queryResult.SelectScalarString()); if (bads.Length != 0) { - _logger.LogInformation("Removing found duplicated extras for the following items: {0}", bads); + _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')"); } } From 79dee27299bda60f67e98eda8c309b1f25e0893b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 15 May 2020 14:33:36 -0400 Subject: [PATCH 125/137] Fixed indentation --- Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index 079b5b5dc4..b3cc297082 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -94,17 +94,17 @@ namespace Jellyfin.Server.Migrations.Routines if (entry[2].SQLiteType != SQLiteType.Null) { - newEntry.Overview = entry[2].ToString(); + newEntry.Overview = entry[2].ToString(); } if (entry[3].SQLiteType != SQLiteType.Null) { - newEntry.ShortOverview = entry[3].ToString(); + newEntry.ShortOverview = entry[3].ToString(); } if (entry[5].SQLiteType != SQLiteType.Null) { - newEntry.ItemId = entry[5].ToString(); + newEntry.ItemId = entry[5].ToString(); } return newEntry; From 43dc604e8739614752a0c4e8a4ff00af5d74085d Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 15 May 2020 21:49:45 +0300 Subject: [PATCH 126/137] Fixed compilation, added backing db before removing extras --- Jellyfin.Server/Migrations/MigrationRunner.cs | 2 +- ...ggedExtras.cs => RemoveDuplicateExtras.cs} | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) rename Jellyfin.Server/Migrations/Routines/{RemoveBuggedExtras.cs => RemoveDuplicateExtras.cs} (61%) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index 7a881be0f6..3941700655 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -18,7 +18,7 @@ namespace Jellyfin.Server.Migrations { typeof(Routines.DisableTranscodingThrottling), typeof(Routines.CreateUserLoggingConfigFile), - typeof(Routines.RemoveBuggedExtras) + typeof(Routines.RemoveDuplicateExtras) }; /// diff --git a/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs similarity index 61% rename from Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs rename to Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs index 512bec0bf7..46de2d5c39 100644 --- a/Jellyfin.Server/Migrations/Routines/RemoveBuggedExtras.cs +++ b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.IO; using MediaBrowser.Controller; @@ -16,7 +17,7 @@ namespace Jellyfin.Server.Migrations.Routines private readonly ILogger _logger; private readonly IServerApplicationPaths _paths; - public RemoveBuggedExtras(ILogger logger, IServerApplicationPaths paths) + public RemoveDuplicateExtras(ILogger logger, IServerApplicationPaths paths) { _logger = logger; _paths = paths; @@ -26,14 +27,15 @@ namespace Jellyfin.Server.Migrations.Routines public Guid Id => Guid.Parse("{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}"); /// - public string Name => "RemoveBuggedExtras"; + public string Name => "RemoveDuplicateExtras"; /// public void Perform() { var dataPath = _paths.DataPath; + var dbPath = Path.Combine(dataPath, DbFilename); using (var connection = SQLite3.Open( - Path.Combine(dataPath, DbFilename), + dbPath, ConnectionFlags.ReadWrite, null)) { @@ -41,6 +43,25 @@ namespace Jellyfin.Server.Migrations.Routines var bads = string.Join(", ", queryResult.SelectScalarString()); if (bads.Length != 0) { + _logger.LogInformation("Found duplicate extras, making {Library} backup", DbFilename); + 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); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannot make a backup of {Library}", DbFilename); + throw; + } + } + } + _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')"); } From 6e68702799b2b3de9660babad6a66493d16fec72 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 15 May 2020 15:10:41 -0400 Subject: [PATCH 127/137] Do not run DELETE command if no extras are detected Also log a message if no extras were detected Also log the path used for the database backup Also add some comments to explain the migration --- .../Routines/RemoveDuplicateExtras.cs | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs index 46de2d5c39..e955363881 100644 --- a/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs +++ b/Jellyfin.Server/Migrations/Routines/RemoveDuplicateExtras.cs @@ -39,32 +39,40 @@ namespace Jellyfin.Server.Migrations.Routines 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()); - if (bads.Length != 0) + + // Do nothing if no duplicate extras were detected + if (bads.Length == 0) { - _logger.LogInformation("Found duplicate extras, making {Library} backup", DbFilename); - for (int i = 1; ; i++) + _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)) { - var bakPath = string.Format(CultureInfo.InvariantCulture, "{0}.bak{1}", dbPath, i); - if (!File.Exists(bakPath)) + try { - try - { - File.Copy(dbPath, bakPath); - break; - } - catch (Exception ex) - { - _logger.LogError(ex, "Cannot make a backup of {Library}", DbFilename); - throw; - } + 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; } } - - _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')"); } + + // 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')"); } } } From 9cad5980594fce9a765260cae72bbea4615c7529 Mon Sep 17 00:00:00 2001 From: Erik Rigtorp Date: Thu, 30 Apr 2020 17:15:38 -0700 Subject: [PATCH 128/137] Fix container image build by installing python --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 \ From 9314434bbf79250f1e545b459c545f57d5acc67c Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 16 May 2020 17:35:34 +0200 Subject: [PATCH 129/137] Fix suggestions --- MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 4 ++-- MediaBrowser.Model/Entities/MediaStream.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index d7b0e0e644..a2ea0766a8 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -287,9 +287,9 @@ namespace MediaBrowser.MediaEncoding.Probing public string ColorTransfer { get; set; } /// - /// Gets or sets the color transfer. + /// Gets or sets the color primaries. /// - /// The color transfer. + /// The color primaries. [JsonPropertyName("color_primaries")] public string ColorPrimaries { get; set; } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index dd17623bde..d340f9ef75 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Model.Entities var colorTransfer = ColorTransfer; if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) - || string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) + || string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) { return "HDR"; } From a33a589dba2e20790ebc2323a91645af73cbf6d3 Mon Sep 17 00:00:00 2001 From: Franco Castillo Date: Sat, 16 May 2020 18:15:27 +0000 Subject: [PATCH 130/137] Translated using Weblate (Spanish (Argentina)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_AR/ --- .../Localization/Core/es-AR.json | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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" } From e75b2cd33568b3cae2a344190226b1d83a12230f Mon Sep 17 00:00:00 2001 From: Samuel Gagnon Date: Sat, 16 May 2020 16:53:30 +0000 Subject: [PATCH 131/137] Translated using Weblate (French (Canada)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr_CA/ --- .../Localization/Core/fr-CA.json | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index c2349ba5bb..3dcfa68441 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -96,21 +96,22 @@ "TasksLibraryCategory": "Bibliothèque", "TasksMaintenanceCategory": "Entretien", "TaskDownloadMissingSubtitlesDescription": "Recherche l'internet pour des sous-titres manquants à base de métadonnées configurées.", - "TaskDownloadMissingSubtitles": "Télécharger des sous-titres manquants", - "TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines d'internet.", + "TaskDownloadMissingSubtitles": "Télécharger les sous-titres manquants", + "TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines internet.", "TaskRefreshChannels": "Rafraîchir des chaines", - "TaskCleanTranscodeDescription": "Retirer des fichiers de transcodage de plus qu'un jour.", - "TaskCleanTranscode": "Nettoyer le directoire de transcodage", - "TaskUpdatePluginsDescription": "Télécharger et installer des mises à jours des plugins qui sont configurés m.à.j. automisés.", - "TaskUpdatePlugins": "Mise à jour des plugins", - "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.", + "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": "Retire les données qui ont plus que {0} jours.", - "TaskCleanLogs": "Nettoyer les données de directoire", - "TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour des nouveaux fichiers et rafraîchit les métadonnées.", - "TaskRefreshChapterImages": "Extraire des images du chapitre", - "TaskRefreshChapterImagesDescription": "Créer des vignettes pour des vidéos qui ont des chapitres", - "TaskRefreshLibrary": "Analyser la bibliothèque de média", - "TaskCleanCache": "Nettoyer le cache de directoire", - "TasksApplicationCategory": "Application" + "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." } From 3bc07e7c56880de8b75cbd36c79deff6decf8e77 Mon Sep 17 00:00:00 2001 From: Nathan Kessler Date: Sun, 17 May 2020 10:48:30 -0400 Subject: [PATCH 132/137] Fix 500 error causing first-time setup wizard to hang --- Jellyfin.Api/Auth/CustomAuthenticationHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) 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)); From 3ed76d7e083940b53011c7a11a52cdb71d7aa715 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 17 May 2020 13:33:38 -0400 Subject: [PATCH 133/137] Update to .NET Core 3.1.4 --- .../Emby.Server.Implementations.csproj | 8 ++++---- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Data/Jellyfin.Data.csproj | 4 ++-- .../Jellyfin.Server.Implementations.csproj | 7 +++++-- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Common/MediaBrowser.Common.csproj | 4 ++-- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++-- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 4 ++-- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj | 2 +- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 896e4310e7..e95228b706 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -34,10 +34,10 @@ - - - - + + + + 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/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index b2a3f7eb34..9157c3ead9 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 149ca50209..8486fc2dfb 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -26,8 +26,11 @@ - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 9eec6ed4eb..c93aa837e7 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -41,8 +41,8 @@ - - + + 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/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.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 5c6e313e07..461f59672e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -21,9 +21,9 @@ - + - + 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/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/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj index f30e486900..60c392314d 100644 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj @@ -8,7 +8,7 @@ - + From 634bc73c9a641646b633fbc560a288207f5eac4b Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 17 May 2020 18:07:37 -0400 Subject: [PATCH 134/137] DO not use developer exception page when exception stack trace should be ignored --- .../HttpServer/HttpListenerHost.cs | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 794d55c049..718078ae13 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -210,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); @@ -504,15 +496,24 @@ namespace Emby.Server.Implementations.HttpServer { var requestInnerEx = GetActualException(requestEx); var statusCode = GetStatusCode(requestInnerEx); + 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 - // The framework-defined development exception page will be returned instead - if (statusCode == 500 && _hostEnvironment.IsDevelopment()) + // 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) { From 989ddbcafdfcbe32bdf16a30ebec9554d6ca548a Mon Sep 17 00:00:00 2001 From: erikasne6152 Date: Mon, 18 May 2020 11:31:19 +0000 Subject: [PATCH 135/137] Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- .../Localization/Core/lt-LT.json | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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" } From c70e38288c26be6a69ab5fdd334bca9bc30ab5ef Mon Sep 17 00:00:00 2001 From: Vasily Date: Mon, 18 May 2020 17:01:29 +0300 Subject: [PATCH 136/137] Apply suggestions from code review Co-authored-by: dkanada --- .../LiveTv/TunerHosts/SharedHttpStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index e41ced28b5..efec58fab6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -121,15 +121,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts await taskCompletionSource.Task.ConfigureAwait(false); if (taskCompletionSource.Task.Exception != null) { - // Error happened during opening the stream, re-raise the exception to inform the caller + // 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)); + throw new EndOfStreamException(String.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name)); } } @@ -162,6 +161,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogError(ex, "Error copying live stream {0} to {1}.", GetType().Name, TempFilePath); openTaskCompletionSource.TrySetException(ex); } + openTaskCompletionSource.TrySetResult(false); EnableStreamSharing = false; From 5eec3a13429d5fae6a944531d77602d3c198d023 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 18 May 2020 10:47:01 -0400 Subject: [PATCH 137/137] Remove extra whitespace Co-authored-by: dkanada --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 718078ae13..0438122900 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -508,7 +508,7 @@ namespace Emby.Server.Implementations.HttpServer // 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() ) + if (statusCode == 500 && !ignoreStackTrace && _hostEnvironment.IsDevelopment()) { throw; }