From 976459d3e8a8b889cebc2cf281e38b0fbc19c9b9 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 17 Dec 2019 23:15:02 +0100 Subject: [PATCH 001/411] 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 | 215 +++++++++--------- 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, 316 insertions(+), 962 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,46 +105,78 @@ 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]. + /// Sends a message asynchronously. /// - /// The bytes. - private void OnReceiveInternal(byte[] bytes) + /// + /// The message. + /// The cancellation token. + /// Task. + /// message + public Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken) { - LastActivityDate = DateTime.UtcNow; - - if (OnReceive == null) + if (message == null) { - return; + throw new ArgumentNullException(nameof(message)); } - var charset = CharsetDetector.DetectFromBytes(bytes).Detected?.EncodingName; - if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase)) + var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions); + return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken); + } + + /// + public async Task ProcessAsync(CancellationToken cancellationToken = default) + { + var pipe = new Pipe(); + var writer = pipe.Writer; + + ValueWebSocketReceiveResult receiveresult; + do { - OnReceiveInternal(Encoding.UTF8.GetString(bytes, 0, bytes.Length)); - } - else + // 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) { - OnReceiveInternal(Encoding.ASCII.GetString(bytes, 0, bytes.Length)); + await _socket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + string.Empty, // REVIEW: human readable explanation as to why the connection is closed. + cancellationToken).ConfigureAwait(false); } + + Closed?.Invoke(this, EventArgs.Empty); + + _socket.Dispose(); } - private void OnReceiveInternal(string message) + private async Task ProcessInternal(PipeReader reader) { 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; @@ -165,74 +184,47 @@ namespace Emby.Server.Implementations.HttpServer try { - var stub = (WebSocketMessage)_jsonSerializer.DeserializeFromString(message, typeof(WebSocketMessage)); + 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(), + Data = stub.Data.ToString(), Connection = this }; - OnReceive(info); + await OnReceive(info).ConfigureAwait(false); } - catch (Exception ex) + catch (JsonException ex) { _logger.LogError(ex, "Error processing web socket message"); } } - /// - /// Sends a message asynchronously. - /// - /// - /// The message. - /// The cancellation token. - /// Task. - /// message - public Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - var json = _jsonSerializer.SerializeToString(message); - - return SendAsync(json, cancellationToken); - } - - /// - /// Sends a message asynchronously. - /// - /// The buffer. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] buffer, CancellationToken cancellationToken) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return _socket.SendAsync(buffer, true, cancellationToken); - } - - /// - public Task SendAsync(string text, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(text)) - { - throw new ArgumentNullException(nameof(text)); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return _socket.SendAsync(text, true, cancellationToken); - } - /// public void Dispose() { @@ -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/411] 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/411] Fix websocket handling --- .../HttpServer/WebSocketConnection.cs | 73 ++++++++----------- .../Session/WebSocketController.cs | 2 - .../Net/IWebSocketConnection.cs | 2 +- 3 files changed, 30 insertions(+), 47 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"); + return; } - } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + // Tell the PipeReader how much of the buffer we have consumed + reader.AdvanceTo(buffer.End); - /// - /// 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) + _logger.LogDebug("WS received message: {@Message}", stub); + + var info = new WebSocketMessageInfo { - return; - } + MessageType = stub.MessageType, + Data = stub.Data?.ToString(), // Data can be null + Connection = this + }; + + _logger.LogDebug("WS message info: {@MessageInfo}", info); - if (dispose) + await OnReceive(info).ConfigureAwait(false); + + // Stop reading if there's no more data coming + if (result.IsCompleted) { - _socket.Dispose(); + return; } - - _disposed = true; } } } 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/411] 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/411] 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/411] 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/411] 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/411] 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/411] 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 974a04c12939068b23b62ee6ebb1e7fc2e830eec Mon Sep 17 00:00:00 2001 From: dkanada Date: Wed, 26 Feb 2020 01:58:39 +0900 Subject: [PATCH 010/411] update plugin classes for nightly builds --- .../Activity/ActivityLogEntryPoint.cs | 8 +- .../ApplicationHost.cs | 10 +- .../Updates/InstallationManager.cs | 68 +++++------ MediaBrowser.Api/PackageService.cs | 36 +----- .../Extensions/BaseExtensions.cs | 1 - MediaBrowser.Common/IApplicationHost.cs | 4 +- MediaBrowser.Common/Plugins/BasePlugin.cs | 8 +- .../Updates/IInstallationManager.cs | 22 ++-- .../Updates/InstallationEventArgs.cs | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Model/System/SystemInfo.cs | 2 +- .../Updates/CheckForUpdateResult.cs | 4 +- .../Updates/InstallationInfo.cs | 2 +- MediaBrowser.Model/Updates/PackageInfo.cs | 106 +----------------- .../Updates/PackageTargetSystem.cs | 23 ---- .../Updates/PackageVersionInfo.cs | 97 ---------------- ...ckageVersionClass.cs => ReleaseChannel.cs} | 15 +-- MediaBrowser.Model/Updates/VersionInfo.cs | 73 ++++++++++++ 18 files changed, 150 insertions(+), 333 deletions(-) delete mode 100644 MediaBrowser.Model/Updates/PackageTargetSystem.cs delete mode 100644 MediaBrowser.Model/Updates/PackageVersionInfo.cs rename MediaBrowser.Model/Updates/{PackageVersionClass.cs => ReleaseChannel.cs} (51%) create mode 100644 MediaBrowser.Model/Updates/VersionInfo.cs diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index ac8af66a20..0f0b8b97b1 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -421,7 +421,7 @@ namespace Emby.Server.Implementations.Activity }); } - private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, PackageVersionInfo)> e) + private void OnPluginUpdated(object sender, GenericEventArgs<(IPlugin, VersionInfo)> e) { CreateLogEntry(new ActivityLogEntry { @@ -433,7 +433,7 @@ namespace Emby.Server.Implementations.Activity ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), - e.Argument.Item2.versionStr), + e.Argument.Item2.versionString), Overview = e.Argument.Item2.description }); } @@ -450,7 +450,7 @@ namespace Emby.Server.Implementations.Activity }); } - private void OnPluginInstalled(object sender, GenericEventArgs e) + private void OnPluginInstalled(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { @@ -462,7 +462,7 @@ namespace Emby.Server.Implementations.Activity ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), - e.Argument.versionStr) + e.Argument.versionString) }); } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index dee0edd26e..ad77ab8b48 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -212,14 +212,14 @@ namespace Emby.Server.Implementations public IFileSystem FileSystemManager { get; set; } /// - public PackageVersionClass SystemUpdateLevel + public ReleaseChannel SystemUpdateLevel { get { -#if BETA - return PackageVersionClass.Beta; +#if NIGHTLY + return PackageChannel.Nightly; #else - return PackageVersionClass.Release; + return ReleaseChannel.Stable; #endif } } @@ -1003,7 +1003,7 @@ namespace Emby.Server.Implementations AuthenticatedAttribute.AuthService = AuthService; } - private async void PluginInstalled(object sender, GenericEventArgs args) + private async void PluginInstalled(object sender, GenericEventArgs args) { string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name); var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories) diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index c897036eb8..1450c74d2d 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -23,12 +23,12 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Updates { /// - /// Manages all install, uninstall and update operations (both plugins and system). + /// Manages all install, uninstall, and update operations for the system and individual plugins. /// public class InstallationManager : IInstallationManager { /// - /// The _logger. + /// The logger. /// private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -101,10 +101,10 @@ namespace Emby.Server.Implementations.Updates public event EventHandler> PluginUninstalled; /// - public event EventHandler> PluginUpdated; + public event EventHandler> PluginUpdated; /// - public event EventHandler> PluginInstalled; + public event EventHandler> PluginInstalled; /// public IEnumerable CompletedInstallations => _completedInstallationsInternal; @@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.Updates using (var response = await _httpClient.SendAsync( new HttpRequestOptions { - Url = "https://repo.jellyfin.org/releases/plugin/manifest.json", + Url = "https://repo.jellyfin.org/releases/plugin/manifest-water.json", CancellationToken = cancellationToken, CacheMode = CacheMode.Unconditional, CacheLength = TimeSpan.FromMinutes(3) @@ -148,48 +148,48 @@ namespace Emby.Server.Implementations.Updates } /// - public IEnumerable GetCompatibleVersions( - IEnumerable availableVersions, + public IEnumerable GetCompatibleVersions( + IEnumerable availableVersions, Version minVersion = null, - PackageVersionClass classification = PackageVersionClass.Release) + ReleaseChannel releaseChannel = ReleaseChannel.Stable) { var appVer = _applicationHost.ApplicationVersion; availableVersions = availableVersions - .Where(x => x.classification == classification - && Version.Parse(x.requiredVersionStr) <= appVer); + .Where(x => x.channel == releaseChannel + && Version.Parse(x.minimumServerVersion) <= appVer); if (minVersion != null) { - availableVersions = availableVersions.Where(x => x.Version >= minVersion); + availableVersions = availableVersions.Where(x => x.versionCode >= minVersion); } - return availableVersions.OrderByDescending(x => x.Version); + return availableVersions.OrderByDescending(x => x.versionCode); } /// - public IEnumerable GetCompatibleVersions( + public IEnumerable GetCompatibleVersions( IEnumerable availablePackages, string name = null, Guid guid = default, Version minVersion = null, - PackageVersionClass classification = PackageVersionClass.Release) + ReleaseChannel releaseChannel = ReleaseChannel.Stable) { var package = FilterPackages(availablePackages, name, guid).FirstOrDefault(); - // Package not found. + // Package not found in repository if (package == null) { - return Enumerable.Empty(); + return Enumerable.Empty(); } return GetCompatibleVersions( package.versions, minVersion, - classification); + releaseChannel); } /// - public async IAsyncEnumerable GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default) + public async IAsyncEnumerable GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default) { var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); @@ -198,8 +198,8 @@ namespace Emby.Server.Implementations.Updates // Figure out what needs to be installed foreach (var plugin in _applicationHost.Plugins) { - var compatibleversions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version, systemUpdateLevel); - var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version); + var compatibleVersions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version, systemUpdateLevel); + var version = compatibleVersions.FirstOrDefault(y => y.versionCode > plugin.Version); if (version != null && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase))) { @@ -209,7 +209,7 @@ namespace Emby.Server.Implementations.Updates } /// - public async Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken) + public async Task InstallPackage(VersionInfo package, CancellationToken cancellationToken) { if (package == null) { @@ -221,8 +221,8 @@ namespace Emby.Server.Implementations.Updates Id = Guid.NewGuid(), Name = package.name, AssemblyGuid = package.guid, - UpdateClass = package.classification, - Version = package.versionStr + UpdateClass = package.channel, + Version = package.versionString }; var innerCancellationTokenSource = new CancellationTokenSource(); @@ -240,7 +240,7 @@ namespace Emby.Server.Implementations.Updates var installationEventArgs = new InstallationEventArgs { InstallationInfo = installationInfo, - PackageVersionInfo = package + VersionInfo = package }; PackageInstalling?.Invoke(this, installationEventArgs); @@ -265,7 +265,7 @@ namespace Emby.Server.Implementations.Updates _currentInstallations.Remove(tuple); } - _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionStr); + _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionString); PackageInstallationCancelled?.Invoke(this, installationEventArgs); @@ -301,7 +301,7 @@ namespace Emby.Server.Implementations.Updates /// The package. /// The cancellation token. /// . - private async Task InstallPackageInternal(PackageVersionInfo package, CancellationToken cancellationToken) + private async Task InstallPackageInternal(VersionInfo package, CancellationToken cancellationToken) { // Set last update time if we were installed before IPlugin plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase)) @@ -313,26 +313,26 @@ namespace Emby.Server.Implementations.Updates // Do plugin-specific processing if (plugin == null) { - _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); + _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionString ?? string.Empty, package.channel); - PluginInstalled?.Invoke(this, new GenericEventArgs(package)); + PluginInstalled?.Invoke(this, new GenericEventArgs(package)); } else { - _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification); + _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionString ?? string.Empty, package.channel); - PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, PackageVersionInfo)>((plugin, package))); + PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package))); } _applicationHost.NotifyPendingRestart(); } - private async Task PerformPackageInstallation(PackageVersionInfo package, CancellationToken cancellationToken) + private async Task PerformPackageInstallation(VersionInfo package, CancellationToken cancellationToken) { - var extension = Path.GetExtension(package.targetFilename); + var extension = Path.GetExtension(package.filename); if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase)) { - _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.targetFilename); + _logger.LogError("Only zip packages are supported. {Filename} is not a zip archive.", package.filename); return; } @@ -379,7 +379,7 @@ namespace Emby.Server.Implementations.Updates } /// - /// Uninstalls a plugin + /// Uninstalls a plugin. /// /// The plugin. public void UninstallPlugin(IPlugin plugin) diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index afc3e026a8..ccc978295c 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -42,23 +42,6 @@ namespace MediaBrowser.Api [Authenticated] public class GetPackages : IReturn { - /// - /// Gets or sets the name. - /// - /// The name. - [ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string PackageType { get; set; } - - [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string TargetSystems { get; set; } - - [ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? IsPremium { get; set; } - - [ApiMember(Name = "IsAdult", Description = "Optional. Filter by package that contain adult content.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? IsAdult { get; set; } - - public bool? IsAppStoreEnabled { get; set; } } /// @@ -94,7 +77,7 @@ namespace MediaBrowser.Api /// /// The update class. [ApiMember(Name = "UpdateClass", Description = "Optional update class (Dev, Beta, Release). Defaults to Release.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public PackageVersionClass UpdateClass { get; set; } + public ReleaseChannel UpdateClass { get; set; } } /// @@ -154,23 +137,6 @@ namespace MediaBrowser.Api { IEnumerable packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); - if (!string.IsNullOrEmpty(request.TargetSystems)) - { - var apps = request.TargetSystems.Split(',').Select(i => (PackageTargetSystem)Enum.Parse(typeof(PackageTargetSystem), i, true)); - - packages = packages.Where(p => apps.Contains(p.targetSystem)); - } - - if (request.IsAdult.HasValue) - { - packages = packages.Where(p => p.adult == request.IsAdult.Value); - } - - if (request.IsAppStoreEnabled.HasValue) - { - packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value); - } - return ToOptimizedResult(packages.ToArray()); } diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 08964420e7..bc002e5233 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -35,7 +35,6 @@ namespace MediaBrowser.Common.Extensions { return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str))); } - #pragma warning restore CA5351 } } diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 68a24aabaa..c88eac27a1 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -50,8 +50,8 @@ namespace MediaBrowser.Common /// /// Gets the version class of the system. /// - /// or . - PackageVersionClass SystemUpdateLevel { get; } + /// or . + ReleaseChannel SystemUpdateLevel { get; } /// /// Gets the application version. diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index b24d10ff10..9e4a360c38 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Common.Plugins } /// - /// Called when just before the plugin is uninstalled from the server. + /// Called just before the plugin is uninstalled from the server. /// public virtual void OnUninstalling() { @@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Plugins private readonly object _configurationSyncLock = new object(); /// - /// The save lock. + /// The configuration save lock. /// private readonly object _configurationSaveLock = new object(); @@ -148,7 +148,7 @@ namespace MediaBrowser.Common.Plugins protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath); /// - /// Gets or sets the plugin's configuration. + /// Gets or sets the plugin configuration. /// /// The configuration. public TConfigurationType Configuration @@ -186,7 +186,7 @@ namespace MediaBrowser.Common.Plugins public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); /// - /// Gets the plugin's configuration. + /// Gets the plugin configuration. /// /// The configuration. BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index a09c1916c5..284e418d9d 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -29,12 +29,12 @@ namespace MediaBrowser.Common.Updates /// /// Occurs when a plugin is updated. /// - event EventHandler> PluginUpdated; + event EventHandler> PluginUpdated; /// /// Occurs when a plugin is installed. /// - event EventHandler> PluginInstalled; + event EventHandler> PluginInstalled; /// /// Gets the completed installations. @@ -65,12 +65,12 @@ namespace MediaBrowser.Common.Updates /// /// The available version of the plugin. /// The minimum required version of the plugin. - /// The classification of updates. + /// The classification of updates. /// All compatible versions ordered from newest to oldest. - IEnumerable GetCompatibleVersions( - IEnumerable availableVersions, + IEnumerable GetCompatibleVersions( + IEnumerable availableVersions, Version minVersion = null, - PackageVersionClass classification = PackageVersionClass.Release); + ReleaseChannel releaseChannel = ReleaseChannel.Stable); /// /// Returns all compatible versions ordered from newest to oldest. @@ -79,21 +79,21 @@ namespace MediaBrowser.Common.Updates /// The name. /// The guid of the plugin. /// The minimum required version of the plugin. - /// The classification. + /// The classification. /// All compatible versions ordered from newest to oldest. - IEnumerable GetCompatibleVersions( + IEnumerable GetCompatibleVersions( IEnumerable availablePackages, string name = null, Guid guid = default, Version minVersion = null, - PackageVersionClass classification = PackageVersionClass.Release); + ReleaseChannel releaseChannel = ReleaseChannel.Stable); /// /// Returns the available plugin updates. /// /// The cancellation token. /// The available plugin updates. - IAsyncEnumerable GetAvailablePluginUpdates(CancellationToken cancellationToken = default); + IAsyncEnumerable GetAvailablePluginUpdates(CancellationToken cancellationToken = default); /// /// Installs the package. @@ -101,7 +101,7 @@ namespace MediaBrowser.Common.Updates /// The package. /// The cancellation token. /// . - Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default); + Task InstallPackage(VersionInfo package, CancellationToken cancellationToken = default); /// /// Uninstalls a plugin. diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs index 8bbb231ce1..f459fd8256 100644 --- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs @@ -9,6 +9,6 @@ namespace MediaBrowser.Common.Updates { public InstallationInfo InstallationInfo { get; set; } - public PackageVersionInfo PackageVersionInfo { get; set; } + public VersionInfo VersionInfo { get; set; } } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 6576657662..41644ad334 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -11,7 +11,7 @@ netstandard2.1 false true - true + true diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 190411c9ba..da39ee208a 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Model.System /// public class SystemInfo : PublicSystemInfo { - public PackageVersionClass SystemUpdateLevel { get; set; } + public ReleaseChannel SystemUpdateLevel { get; set; } /// /// Gets or sets the display name of the operating system. diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs index be1b082238..883fc636b4 100644 --- a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs +++ b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs @@ -17,13 +17,13 @@ namespace MediaBrowser.Model.Updates /// The available version. public string AvailableVersion { - get => Package != null ? Package.versionStr : "0.0.0.1"; + get => Package != null ? Package.versionString : "0.0.0.1"; set { } // need this for the serializer } /// /// Get or sets package information for an available update /// - public PackageVersionInfo Package { get; set; } + public VersionInfo Package { get; set; } } } diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs index 42c2105f54..870bf8c0be 100644 --- a/MediaBrowser.Model/Updates/InstallationInfo.cs +++ b/MediaBrowser.Model/Updates/InstallationInfo.cs @@ -35,6 +35,6 @@ namespace MediaBrowser.Model.Updates /// Gets or sets the update class. /// /// The update class. - public PackageVersionClass UpdateClass { get; set; } + public ReleaseChannel UpdateClass { get; set; } } } diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs index abbe91eff6..d06ffe1e6c 100644 --- a/MediaBrowser.Model/Updates/PackageInfo.cs +++ b/MediaBrowser.Model/Updates/PackageInfo.cs @@ -8,12 +8,6 @@ namespace MediaBrowser.Model.Updates /// public class PackageInfo { - /// - /// The internal id of this package. - /// - /// The id. - public string id { get; set; } - /// /// Gets or sets the name. /// @@ -32,24 +26,6 @@ namespace MediaBrowser.Model.Updates /// The overview. public string overview { get; set; } - /// - /// Gets or sets a value indicating whether this instance is premium. - /// - /// true if this instance is premium; otherwise, false. - public bool isPremium { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is adult only content. - /// - /// true if this instance is adult; otherwise, false. - public bool adult { get; set; } - - /// - /// Gets or sets the rich desc URL. - /// - /// The rich desc URL. - public string richDescUrl { get; set; } - /// /// Gets or sets the thumb image. /// @@ -63,16 +39,10 @@ namespace MediaBrowser.Model.Updates public string previewImage { get; set; } /// - /// Gets or sets the type. - /// - /// The type. - public string type { get; set; } - - /// - /// Gets or sets the target filename. + /// Gets or sets the target filename for the downloaded binary. /// /// The target filename. - public string targetFilename { get; set; } + public string filename { get; set; } /// /// Gets or sets the owner. @@ -87,90 +57,24 @@ namespace MediaBrowser.Model.Updates public string category { get; set; } /// - /// Gets or sets the catalog tile color. - /// - /// The owner. - public string tileColor { get; set; } - - /// - /// Gets or sets the feature id of this package (if premium). - /// - /// The feature id. - public string featureId { get; set; } - - /// - /// Gets or sets the registration info for this package (if premium). - /// - /// The registration info. - public string regInfo { get; set; } - - /// - /// Gets or sets the price for this package (if premium). - /// - /// The price. - public float price { get; set; } - - /// - /// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic). - /// - /// The target system. - public PackageTargetSystem targetSystem { get; set; } - - /// - /// The guid of the assembly associated with this package (if a plug-in). + /// The guid of the assembly associated with this plugin. /// This is used to identify the proper item for automatic updates. /// /// The name. public string guid { get; set; } - /// - /// Gets or sets the total number of ratings for this package. - /// - /// The total ratings. - public int? totalRatings { get; set; } - - /// - /// Gets or sets the average rating for this package . - /// - /// The rating. - public float avgRating { get; set; } - - /// - /// Gets or sets whether or not this package is registered. - /// - /// True if registered. - public bool isRegistered { get; set; } - - /// - /// Gets or sets the expiration date for this package. - /// - /// Expiration Date. - public DateTime expDate { get; set; } - /// /// Gets or sets the versions. /// /// The versions. - public IReadOnlyList versions { get; set; } - - /// - /// Gets or sets a value indicating whether [enable in application store]. - /// - /// true if [enable in application store]; otherwise, false. - public bool enableInAppStore { get; set; } - - /// - /// Gets or sets the installs. - /// - /// The installs. - public int installs { get; set; } + public IReadOnlyList versions { get; set; } /// /// Initializes a new instance of the class. /// public PackageInfo() { - versions = Array.Empty(); + versions = Array.Empty(); } } } diff --git a/MediaBrowser.Model/Updates/PackageTargetSystem.cs b/MediaBrowser.Model/Updates/PackageTargetSystem.cs deleted file mode 100644 index 11af7f02dd..0000000000 --- a/MediaBrowser.Model/Updates/PackageTargetSystem.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MediaBrowser.Model.Updates -{ - /// - /// Enum PackageType. - /// - public enum PackageTargetSystem - { - /// - /// Server. - /// - Server, - - /// - /// MB Theater. - /// - MBTheater, - - /// - /// MB Classic. - /// - MBClassic - } -} diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs deleted file mode 100644 index 85d8fde860..0000000000 --- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs +++ /dev/null @@ -1,97 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -using System; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Model.Updates -{ - /// - /// Class PackageVersionInfo. - /// - public class PackageVersionInfo - { - /// - /// Gets or sets the name. - /// - /// The name. - public string name { get; set; } - - /// - /// Gets or sets the guid. - /// - /// The guid. - public string guid { get; set; } - - /// - /// Gets or sets the version STR. - /// - /// The version STR. - public string versionStr { get; set; } - - /// - /// The _version - /// - private Version _version; - - /// - /// Gets or sets the version. - /// Had to make this an interpreted property since Protobuf can't handle Version - /// - /// The version. - [JsonIgnore] - public Version Version - { - get - { - if (_version == null) - { - var ver = versionStr; - _version = new Version(string.IsNullOrEmpty(ver) ? "0.0.0.1" : ver); - } - - return _version; - } - } - - /// - /// Gets or sets the classification. - /// - /// The classification. - public PackageVersionClass classification { get; set; } - - /// - /// Gets or sets the description. - /// - /// The description. - public string description { get; set; } - - /// - /// Gets or sets the required version STR. - /// - /// The required version STR. - public string requiredVersionStr { get; set; } - - /// - /// Gets or sets the source URL. - /// - /// The source URL. - public string sourceUrl { get; set; } - - /// - /// Gets or sets the source URL. - /// - /// The source URL. - public string checksum { get; set; } - - /// - /// Gets or sets the target filename. - /// - /// The target filename. - public string targetFilename { get; set; } - - public string infoUrl { get; set; } - - public string runtimes { get; set; } - } -} diff --git a/MediaBrowser.Model/Updates/PackageVersionClass.cs b/MediaBrowser.Model/Updates/ReleaseChannel.cs similarity index 51% rename from MediaBrowser.Model/Updates/PackageVersionClass.cs rename to MediaBrowser.Model/Updates/ReleaseChannel.cs index f813f2c974..ed4a774a72 100644 --- a/MediaBrowser.Model/Updates/PackageVersionClass.cs +++ b/MediaBrowser.Model/Updates/ReleaseChannel.cs @@ -3,21 +3,16 @@ namespace MediaBrowser.Model.Updates /// /// Enum PackageVersionClass. /// - public enum PackageVersionClass + public enum ReleaseChannel { /// - /// The release. + /// The stable. /// - Release = 0, + Stable = 0, /// - /// The beta. + /// The nightly. /// - Beta = 1, - - /// - /// The dev. - /// - Dev = 2 + Nightly = 1 } } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs new file mode 100644 index 0000000000..ad893db2e2 --- /dev/null +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -0,0 +1,73 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + +using System; + +namespace MediaBrowser.Model.Updates +{ + /// + /// Class PackageVersionInfo. + /// + public class VersionInfo + { + /// + /// Gets or sets the name. + /// + /// The name. + public string name { get; set; } + + /// + /// Gets or sets the guid. + /// + /// The guid. + public string guid { get; set; } + + /// + /// Gets or sets the version string. + /// + /// The version string. + public string versionString { get; set; } + + /// + /// Gets or sets the version. + /// + /// The version. + public Version versionCode { get; set; } + + /// + /// Gets or sets the release channel. + /// + /// The release channel for a given package version. + public ReleaseChannel channel { get; set; } + + /// + /// Gets or sets the description. + /// + /// The description. + public string description { get; set; } + + /// + /// Gets or sets the minimum required version for the server. + /// + /// The minimum required version. + public string minimumServerVersion { get; set; } + + /// + /// Gets or sets the source URL. + /// + /// The source URL. + public string sourceUrl { get; set; } + + /// + /// Gets or sets a checksum for the binary. + /// + /// The checksum. + public string checksum { get; set; } + + /// + /// Gets or sets the target filename for the downloaded binary. + /// + /// The target filename. + public string filename { get; set; } + } +} From 5d760b7ee806d3fb00ac5aa7d0981362526f1d11 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Sun, 1 Mar 2020 21:38:34 +0100 Subject: [PATCH 011/411] 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 012/411] 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 013/411] 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 | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 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 (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) - { - users = users.Where(i => i.Policy.EnableRemoteAccess); - } + if (!string.IsNullOrWhiteSpace(deviceId)) + { + result = result.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); + } - var result = users - .OrderBy(u => u.Name) - .Select(i => _userManager.GetPublicUserDto(i, Request.RemoteIp)) - .ToArray(); + if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + result = result.Where(i => i.Policy.EnableRemoteAccess); + } + } - 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 014/411] 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 85da15685f7a761af3a34f1c591bf129aa5deb5f Mon Sep 17 00:00:00 2001 From: Andreas B <6439218+YouKnowBlom@users.noreply.github.com> Date: Sun, 15 Mar 2020 15:06:38 +0100 Subject: [PATCH 015/411] Refactor DynamicHlsService.AppendPlaylist to use StringBuilder --- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 262f517869..8787eb2a3f 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -724,7 +724,10 @@ namespace MediaBrowser.Api.Playback.Hls private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup) { - var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture); + builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") + .Append(bitrate.ToString(CultureInfo.InvariantCulture)) + .Append(",AVERAGE-BANDWIDTH=") + .Append(bitrate.ToString(CultureInfo.InvariantCulture)); // tvos wants resolution, codecs, framerate //if (state.TargetFramerate.HasValue) @@ -734,10 +737,12 @@ namespace MediaBrowser.Api.Playback.Hls if (!string.IsNullOrWhiteSpace(subtitleGroup)) { - header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup); + builder.Append(",SUBTITLES=\"") + .Append(subtitleGroup) + .Append('"'); } - builder.AppendLine(header); + builder.Append(Environment.NewLine); builder.AppendLine(url); } From 681dd8d32fbb4fdb67a4b82125179bd40d03edbd Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 17 Mar 2020 14:21:00 +0100 Subject: [PATCH 016/411] Add recommended extensions to VS Code configuration --- .vscode/extensions.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..59d9452fed --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-dotnettools.csharp", + "editorconfig.editorconfig" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + + ] +} From 751dff09dc6f57be14f071346a79a23f33fa48c5 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 17 Mar 2020 14:21:24 +0100 Subject: [PATCH 017/411] Add development instructions to README with details on running from source --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/README.md b/README.md index ea54b8c8b0..74042d8d83 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,92 @@ Most of the translations can be found in the web client but we have several othe Detailed Translation Status + +## Development + +These instructions will help you get set up with a local development environment in order to contribute to this repository. Before you start, please be sure to completely read our [guidelines on development contributions](https://jellyfin.org/docs/general/contributing/development.html). Note that this project is supported on all major operating systems (Windows, Mac and Linux). + +### Prerequisites + +The following software prerequisites are required to be installed locally before the project can be built and executed. + +* [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) +* [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least version 2017) or [Visual Studio Code](https://code.visualstudio.com/Download) + +### Cloning the Repository + +After dependencies are installed you will need to clone a local copy of this repository. If you just want to run the server from source you can clone this repository directly, but if you are intending to contribute code changes to the project, you should [set up your own fork](https://jellyfin.org/docs/general/contributing/development.html#set-up-your-copy-of-the-repo) of the repository. The following example shows how you can clone the repository directly over HTTPS. + +```bash +git clone https://github.com/jellyfin/jellyfin.git +``` + +### Installing the Web Client + +By default, the server is configured to host the static files required for the [web client](https://github.com/jellyfin/jellyfin-web) in addition to serving the backend API. before you can run the server, you will need to get a copy of the web client files since they are not included in this repository directly. + +Note that it is also possible to [host the web client separately](#hosting-the-web-client-separately) from the web server with some additional configuration, in which case you can skip this step. + +There are two options to get the files for the web client: + +1. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web) +2. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located here: `C:\Program Files\Jellyfin\Server\jellyfin-web` + +Once you have a copy of the built web client files, you need to copy them into the build output directory of the web server project. For example: `\Jellyfin.Server\bin\Debug\netcoreapp3.1\jellyfin-web` + +### Running The Server + +The following instructions will help you get the project up and running via the command line, or your preferred IDE. + +#### Running With Visual Studio + +To run the project with Visual Studio you can open the Solution (`.sln`) file and then press `F5` to run the server. + +#### Running With Visual Studio Code + +To run the project with Visual Studio Code you will first need to open the repository directory with Visual Studio Code using the `Open Folder...` option. + +Second, you need to [install the recommended extensions for the workspace](https://code.visualstudio.com/docs/editor/extension-gallery#_recommended-extensions). Note that extension recommendations are classified as either "Workspace Recommendations" or "Other Recommendations", but only the "Workspace Recommendations" are required. + +After the required extensions are installed, you can can run the server by pressing `F5`. + +#### Running From The Command Line + +To run the server from the command line you can use the `dotnet run` command. The example below shows how to do this if you have cloned the repository into a directory named `jellyfin` (the default directory name) and should work on all operating systems. + +```bash +cd jellyfin # Move into the repository directory +cd Jellyfin.Server # Move into the server startup project directory +dotnet run # Run the server startup project +``` + +A second option is to build the project and then run the resulting executable file directly. When running the executable directly you can easily add command line options. Add the `--help` flag to list details on all the supported command line options. + +1. Build the project + + ```bash + dotnet build # Build the project + cd bin/Debug/netcoreapp3.1 # Change into the build output directory + ``` + +2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. + +### Running The Tests + +This repository also includes several unit test projects that are used to validate functionality as part of a CI process. These are several ways to run these tests: + +1. Run tests from the command line using `dotnet test` +2. Run tests in Visual Studio using the [Test Explorer](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer) +3. Run individual tests in Visual Studio Code using the associated [CodeLens annotation](https://github.com/OmniSharp/omnisharp-vscode/wiki/How-to-run-and-debug-unit-tests) + +### Advanced Configuration + +The following sections describe some more advanced scenarios for running the server from source that build upon the standard instructions above. + +#### Hosting The Web Client Separately + +It is not necessary to host the frontend web client as part of the backend server. Hosting these two components separately may be useful for front-end developers who would prefer to host the client in a separate webpack development server for a tighter development loop (see the [jellyfin-web](https://github.com/jellyfin/jellyfin-web#getting-started) repo for instructions on how to do this). + +To instruct the server not to host the web content, there is a `nowebcontent` configuration flag that must be set. This can specified using the command line switch `--nowebcontent` or the environment variable `JELLYFIN_NOWEBCONTENT=true`. + +Since this is a common scenario, there is also a separate launch profile defined for Visual Studio called `Jellyfin.Server (nowebcontent)` that can be selected from the 'Start Debugging' dropdown in the main toolbar. From f2858878d166df214aee20f2dc0710b766285c91 Mon Sep 17 00:00:00 2001 From: Andreas B <6439218+YouKnowBlom@users.noreply.github.com> Date: Wed, 11 Mar 2020 18:14:36 +0100 Subject: [PATCH 018/411] Add CODECS field to HLS master playlist --- .../Playback/Hls/DynamicHlsService.cs | 110 +++++++++++++++ .../Playback/Hls/HlsCodecStringFactory.cs | 126 ++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 8787eb2a3f..e6c9213912 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -722,6 +722,114 @@ namespace MediaBrowser.Api.Playback.Hls //return state.VideoRequest.VideoBitRate.HasValue; } + /// + /// Gets a formatted string of the output audio codec, for use in the CODECS field. + /// + /// + /// + /// StreamState of the current stream. + /// Formatted audio codec string. + private string GetPlaylistAudioCodecs(StreamState state) + { + + if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase)) + { + string profile = state.GetRequestedProfiles("aac").FirstOrDefault(); + + return HlsCodecStringFactory.GetAACString(profile); + } + else if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringFactory.GetMP3String(); + } + else if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringFactory.GetAC3String(); + } + else if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) + { + return HlsCodecStringFactory.GetEAC3String(); + } + + return string.Empty; + } + + /// + /// Gets a formatted string of the output video codec, for use in the CODECS field. + /// + /// + /// + /// StreamState of the current stream. + /// Formatted video codec string. + private string GetPlaylistVideoCodecs(StreamState state) + { + int level = Convert.ToInt32(state.GetRequestedLevel(state.ActualOutputVideoCodec)); + + if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + string profile = state.GetRequestedProfiles("h264").FirstOrDefault(); + + return HlsCodecStringFactory.GetH264String(profile, level); + } + else if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + { + string profile = state.GetRequestedProfiles("h265").FirstOrDefault(); + + return HlsCodecStringFactory.GetH265String(profile, level); + } + + return string.Empty; + } + + /// + /// Appends a CODECS field containing formatted strings of + /// the active streams output video and audio codecs. + /// + /// + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state) + { + // Video + string videoCodecs = string.Empty; + if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec)) + { + videoCodecs = GetPlaylistVideoCodecs(state); + } + + // Audio + string audioCodecs = string.Empty; + if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec)) + { + audioCodecs = GetPlaylistAudioCodecs(state); + } + + if (!string.IsNullOrEmpty(videoCodecs) || !string.IsNullOrEmpty(audioCodecs)) + { + builder.Append(",CODECS=\""); + + if (!string.IsNullOrEmpty(videoCodecs) && !string.IsNullOrEmpty(audioCodecs)) + { + builder.Append(videoCodecs) + .Append(',') + .Append(audioCodecs); + } + else if (!string.IsNullOrEmpty(videoCodecs)) + { + builder.Append(videoCodecs); + } + else if (!string.IsNullOrEmpty(audioCodecs)) + { + builder.Append(audioCodecs); + } + + builder.Append('"'); + } + } + private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup) { builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") @@ -735,6 +843,8 @@ namespace MediaBrowser.Api.Playback.Hls // header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture)); //} + AppendPlaylistCodecsField(builder, state); + if (!string.IsNullOrWhiteSpace(subtitleGroup)) { builder.Append(",SUBTITLES=\"") diff --git a/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs b/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs new file mode 100644 index 0000000000..3bbb77a65e --- /dev/null +++ b/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs @@ -0,0 +1,126 @@ +using System; +using System.Text; + + +namespace MediaBrowser.Api.Playback +{ + /// + /// Get various codec strings for use in HLS playlists. + /// + static class HlsCodecStringFactory + { + + /// + /// Gets a MP3 codec string. + /// + /// MP3 codec string. + public static string GetMP3String() + { + return "mp4a.40.34"; + } + + /// + /// Gets an AAC codec string. + /// + /// AAC profile. + /// AAC codec string. + public static string GetAACString(string profile) + { + StringBuilder result = new StringBuilder("mp4a", 9); + + if (string.Equals(profile, "HE", StringComparison.OrdinalIgnoreCase)) + { + result.Append(".40.5"); + } + else + { + // Default to LC if profile is invalid + result.Append(".40.2"); + } + + return result.ToString(); + } + + /// + /// Gets a H.264 codec string. + /// + /// H.264 profile. + /// H.264 level. + /// H.264 string. + public static string GetH264String(string profile, int level) + { + StringBuilder result = new StringBuilder("avc1", 11); + + if (string.Equals(profile, "high", StringComparison.OrdinalIgnoreCase)) + { + result.Append(".6400"); + } + else if (string.Equals(profile, "main", StringComparison.OrdinalIgnoreCase)) + { + result.Append(".4D40"); + } + else if (string.Equals(profile, "baseline", StringComparison.OrdinalIgnoreCase)) + { + result.Append(".42E0"); + } + else + { + // Default to constrained baseline if profile is invalid + result.Append(".4240"); + } + + string levelHex = level.ToString("X2"); + result.Append(levelHex); + + return result.ToString(); + } + + /// + /// Gets a H.265 codec string. + /// + /// H.265 profile. + /// H.265 level. + /// H.265 string. + public static string GetH265String(string profile, int level) + { + // The h265 syntax is a bit of a mystery at the time this comment was written. + // This is what I've found through various sources: + // FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN] + StringBuilder result = new StringBuilder("hev1", 16); + + if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase)) + { + result.Append(".2.6"); + } + else + { + // Default to main if profile is invalid + result.Append(".1.6"); + } + + result.Append(".L") + .Append(level * 3) + .Append(".B0"); + + return result.ToString(); + } + + /// + /// Gets an AC-3 codec string. + /// + /// AC-3 codec string. + public static string GetAC3String() + { + return "mp4a.a5"; + } + + /// + /// Gets an E-AC-3 codec string. + /// + /// E-AC-3 codec string. + public static string GetEAC3String() + { + return "mp4a.a6"; + } + } +} From 8a990d1d95aa22840bae5c3494cb5371bcf2b4d8 Mon Sep 17 00:00:00 2001 From: Andreas B <6439218+YouKnowBlom@users.noreply.github.com> Date: Wed, 11 Mar 2020 18:16:57 +0100 Subject: [PATCH 019/411] Add FRAME-RATE field to HLS master playlist --- .../Playback/Hls/DynamicHlsService.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index e6c9213912..d56b5cbff4 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -830,6 +830,32 @@ namespace MediaBrowser.Api.Playback.Hls } } + /// + /// Appends a FRAME-RATE field containing the framerate of the output stream. + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state) + { + double? framerate = null; + if (state.TargetFramerate.HasValue) + { + framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3); + } + else if (state.VideoStream.RealFrameRate.HasValue) + { + framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3); + } + + if (framerate.HasValue) + { + builder.Append(",FRAME-RATE=\"") + .Append(framerate.Value) + .Append('"'); + } + } + private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup) { builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") @@ -845,6 +871,8 @@ namespace MediaBrowser.Api.Playback.Hls AppendPlaylistCodecsField(builder, state); + AppendPlaylistFramerateField(builder, state); + if (!string.IsNullOrWhiteSpace(subtitleGroup)) { builder.Append(",SUBTITLES=\"") From 0a2d24aff3d2e78c97b4b2294a418e2cd4a16be1 Mon Sep 17 00:00:00 2001 From: Andreas B <6439218+YouKnowBlom@users.noreply.github.com> Date: Sun, 15 Mar 2020 18:13:19 +0100 Subject: [PATCH 020/411] Add RESOLUTION field to HLS master playlist --- .../Playback/Hls/DynamicHlsService.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index d56b5cbff4..ce25676ff5 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -856,6 +856,24 @@ namespace MediaBrowser.Api.Playback.Hls } } + /// + /// Appends a RESOLUTION field containing the resolution of the output stream. + /// + /// + /// StringBuilder to append the field to. + /// StreamState of the current stream. + private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state) + { + if (state.OutputWidth.HasValue && state.OutputHeight.HasValue) + { + builder.Append(",RESOLUTION=\"") + .Append(state.OutputWidth.GetValueOrDefault()) + .Append('x') + .Append(state.OutputHeight.GetValueOrDefault()) + .Append('"'); + } + } + private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup) { builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=") @@ -863,14 +881,10 @@ namespace MediaBrowser.Api.Playback.Hls .Append(",AVERAGE-BANDWIDTH=") .Append(bitrate.ToString(CultureInfo.InvariantCulture)); - // tvos wants resolution, codecs, framerate - //if (state.TargetFramerate.HasValue) - //{ - // header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture)); - //} - AppendPlaylistCodecsField(builder, state); + AppendPlaylistResolutionField(builder, state); + AppendPlaylistFramerateField(builder, state); if (!string.IsNullOrWhiteSpace(subtitleGroup)) From 48f33f9a9669d0a237c85278a0cac3d3240a7c49 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 20 Mar 2020 12:21:20 +0100 Subject: [PATCH 021/411] Reword prerequisite section so that IDEs are listed as optional --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 74042d8d83..d7f2da7900 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,9 @@ These instructions will help you get set up with a local development environment ### Prerequisites -The following software prerequisites are required to be installed locally before the project can be built and executed. +Before the the project can be built, you must first install the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) on your system. -* [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) -* [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least version 2017) or [Visual Studio Code](https://code.visualstudio.com/Download) +Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but explicit instructions for [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download) are included here. ### Cloning the Repository From cd34115e9981185e6a4f286d81f10160990e3f7c Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 20 Mar 2020 12:35:01 +0100 Subject: [PATCH 022/411] Remove duplicate text Co-Authored-By: artiume --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d7f2da7900..95659e8a8c 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ These instructions will help you get set up with a local development environment ### Prerequisites -Before the the project can be built, you must first install the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) on your system. +Before the project can be built, you must first install the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) on your system. Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but explicit instructions for [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download) are included here. From 3fd245ba8789e862a67f41d5c481ccab6a49fb33 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 21 Mar 2020 23:03:00 +0100 Subject: [PATCH 023/411] Add instructions for serving content over HTTPS --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 95659e8a8c..7b08723d6a 100644 --- a/README.md +++ b/README.md @@ -151,3 +151,9 @@ It is not necessary to host the frontend web client as part of the backend serve To instruct the server not to host the web content, there is a `nowebcontent` configuration flag that must be set. This can specified using the command line switch `--nowebcontent` or the environment variable `JELLYFIN_NOWEBCONTENT=true`. Since this is a common scenario, there is also a separate launch profile defined for Visual Studio called `Jellyfin.Server (nowebcontent)` that can be selected from the 'Start Debugging' dropdown in the main toolbar. + +#### Serving Over HTTPS + +The .NET Core SDK includes a certificate that can be used to serve content over HTTPS while developing. When running from Visual Studio, VS Code, or using `dotnet run`, this behavior is automatically enabled by setting the environment variable `ASPNETCORE_ENVIRONMENT=Development` and you can access the HTTPS version of the site at https://localhost:8920. + +By default, the development certificate is not trusted so you will see a security warning when you browse to the site over HTTPS. On most browsers you can easily bypass this warning and continue to the site. However, if you want to get rid of the warning, you can configure your machine to trust the development certificate by following the instructions in the [ASP.NET Core documentation](https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl#trust-the-aspnet-core-https-development-certificate-on-windows-and-macos). From 2afbbba3f874884e7f249bacb9de458244242380 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:00:20 -0400 Subject: [PATCH 024/411] Remove old build script --- build | 197 ---------------------------------------------------------- 1 file changed, 197 deletions(-) delete mode 100755 build diff --git a/build b/build deleted file mode 100755 index 95d5d5c495..0000000000 --- a/build +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env bash - -# build - build Jellyfin binaries or packages - -set -o errexit -set -o pipefail - -# The list of possible package actions (except 'clean') -declare -a actions=( 'build' 'package' 'sign' 'publish' ) - -# The list of possible platforms, based on directories under 'deployment/' -declare -a platforms=( $( - find deployment/ -maxdepth 1 -mindepth 1 -type d -exec basename {} \; | sort -) ) - -# The list of standard dependencies required by all build scripts; individual -# action scripts may specify their own dependencies -declare -a dependencies=( 'tar' 'zip' ) - -usage() { - echo -e "build - build Jellyfin binaries or packages" - echo -e "" - echo -e "Usage:" - echo -e " $ build --list-platforms" - echo -e " $ build --list-actions " - echo -e " $ build [-k/--keep-artifacts] [-b/--web-branch ] " - echo -e "" - echo -e "The 'keep-artifacts' option preserves build artifacts, e.g. Docker images for system package builds." - echo -e "The web_branch defaults to the same branch name as the current main branch or can be 'local' to not touch the submodule branching." - echo -e "To build all platforms, use 'all'." - echo -e "To perform all build actions, use 'all'." - echo -e "Build output files are collected at '../bin/'." -} - -# Show usage on stderr with exit 1 on argless -if [[ -z $1 ]]; then - usage >&2 - exit 1 -fi -# Show usage if -h or --help are specified in the args -if [[ $@ =~ '-h' || $@ =~ '--help' ]]; then - usage - exit 0 -fi - -# List all available platforms then exit -if [[ $1 == '--list-platforms' ]]; then - echo -e "Available platforms:" - for platform in ${platforms[@]}; do - echo -e " ${platform}" - done - exit 0 -fi - -# List all available actions for a given platform then exit -if [[ $1 == '--list-actions' ]]; then - platform="$2" - if [[ ! " ${platforms[@]} " =~ " ${platform} " ]]; then - echo "ERROR: Platform ${platform} does not exist." - exit 1 - fi - echo -e "Available actions for platform ${platform}:" - for action in ${actions[@]}; do - if [[ -f deployment/${platform}/${action}.sh ]]; then - echo -e " ${action}" - fi - done - exit 0 -fi - -# Parse keep-artifacts option -if [[ $1 == '-k' || $1 == '--keep-artifacts' ]]; then - keep_artifacts="y" - shift 1 -else - keep_artifacts="n" -fi - -# Parse branch option -if [[ $1 == '-b' || $1 == '--web-branch' ]]; then - web_branch="$2" - shift 2 -else - web_branch="$( git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' )" -fi - -# Parse platform option -if [[ -n $1 ]]; then - cli_platform="$1" - shift -else - echo "ERROR: A platform must be specified. Use 'all' to specify all platforms." - exit 1 -fi -if [[ ${cli_platform} == 'all' ]]; then - declare -a platform=( ${platforms[@]} ) -else - if [[ ! " ${platforms[@]} " =~ " ${cli_platform} " ]]; then - echo "ERROR: Platform ${cli_platform} is invalid. Use the '--list-platforms' option to list available platforms." - exit 1 - else - declare -a platform=( "${cli_platform}" ) - fi -fi - -# Parse action option -if [[ -n $1 ]]; then - cli_action="$1" - shift -else - echo "ERROR: An action must be specified. Use 'all' to specify all actions." - exit 1 -fi -if [[ ${cli_action} == 'all' ]]; then - declare -a action=( ${actions[@]} ) -else - if [[ ! " ${actions[@]} " =~ " ${cli_action} " ]]; then - echo "ERROR: Action ${cli_action} is invalid. Use the '--list-actions ' option to list available actions." - exit 1 - else - declare -a action=( "${cli_action}" ) - fi -fi - -# Verify required utilities are installed -missing_deps=() -for utility in ${dependencies[@]}; do - if ! which ${utility} &>/dev/null; then - missing_deps+=( ${utility} ) - fi -done - -# Error if we're missing anything -if [[ ${#missing_deps[@]} -gt 0 ]]; then - echo -e "ERROR: This script requires the following missing utilities:" - for utility in ${missing_deps[@]}; do - echo -e " ${utility}" - done - exit 1 -fi - -# Parse platform-specific dependencies -for target_platform in ${platform[@]}; do - # Read platform-specific dependencies - if [[ -f deployment/${target_platform}/dependencies.txt ]]; then - platform_dependencies="$( grep -v '^#' deployment/${target_platform}/dependencies.txt )" - - # Verify required utilities are installed - missing_deps=() - for utility in ${platform_dependencies[@]}; do - if ! which ${utility} &>/dev/null; then - missing_deps+=( ${utility} ) - fi - done - - # Error if we're missing anything - if [[ ${#missing_deps[@]} -gt 0 ]]; then - echo -e "ERROR: The ${target_platform} platform requires the following utilities:" - for utility in ${missing_deps[@]}; do - echo -e " ${utility}" - done - exit 1 - fi - fi -done - -# Execute each platform and action in order, if said action is enabled -pushd deployment/ -for target_platform in ${platform[@]}; do - echo -e "> Processing platform ${target_platform}" - date_start=$( date +%s ) - pushd ${target_platform} - cleanup() { - echo -e ">> Processing action clean" - if [[ -f clean.sh && -x clean.sh ]]; then - ./clean.sh ${keep_artifacts} - fi - } - trap cleanup EXIT INT - for target_action in ${action[@]}; do - echo -e ">> Processing action ${target_action}" - if [[ -f ${target_action}.sh && -x ${target_action}.sh ]]; then - ./${target_action}.sh web_branch=${web_branch} - fi - done - if [[ -d pkg-dist/ ]]; then - echo -e ">> Collecting build artifacts" - target_dir="../../../bin/${target_platform}" - mkdir -p ${target_dir} - mv pkg-dist/* ${target_dir}/ - fi - cleanup - date_end=$( date +%s ) - echo -e "> Completed platform ${target_platform} in $( expr ${date_end} - ${date_start} ) seconds." - popd -done -popd From 28f7df652015013ff5cedb10971fb69c8e41d2b1 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:00:52 -0400 Subject: [PATCH 025/411] Move all old deployment stuff to a new folder --- .../fedora-package-x64/pkg-src/restart.sh | 36 ------------------- deployment/{ => old}/README.md | 0 .../{ => old}/centos-package-x64/Dockerfile | 0 .../{ => old}/centos-package-x64/clean.sh | 0 .../centos-package-x64/dependencies.txt | 0 .../centos-package-x64/docker-build.sh | 0 .../{ => old}/centos-package-x64/package.sh | 0 .../{ => old}/centos-package-x64/pkg-src | 0 .../debian-package-arm64/Dockerfile.amd64 | 0 .../debian-package-arm64/Dockerfile.arm64 | 0 .../{ => old}/debian-package-arm64/clean.sh | 0 .../debian-package-arm64/dependencies.txt | 0 .../debian-package-arm64/docker-build.sh | 0 .../{ => old}/debian-package-arm64/package.sh | 0 .../{ => old}/debian-package-arm64/pkg-src | 0 .../debian-package-armhf/Dockerfile.amd64 | 0 .../debian-package-armhf/Dockerfile.armhf | 0 .../{ => old}/debian-package-armhf/clean.sh | 0 .../debian-package-armhf/dependencies.txt | 0 .../debian-package-armhf/docker-build.sh | 0 .../{ => old}/debian-package-armhf/package.sh | 0 .../{ => old}/debian-package-armhf/pkg-src | 0 .../{ => old}/debian-package-x64/Dockerfile | 0 .../{ => old}/debian-package-x64/clean.sh | 0 .../debian-package-x64/dependencies.txt | 0 .../debian-package-x64/docker-build.sh | 0 .../{ => old}/debian-package-x64/package.sh | 0 .../debian-package-x64/pkg-src/changelog | 0 .../debian-package-x64/pkg-src/compat | 0 .../debian-package-x64/pkg-src/conf/jellyfin | 0 .../pkg-src/conf/jellyfin-sudoers | 0 .../pkg-src/conf/jellyfin.service.conf | 0 .../pkg-src/conf/logging.json | 0 .../debian-package-x64/pkg-src/control | 0 .../debian-package-x64/pkg-src/copyright | 0 .../debian-package-x64/pkg-src/gbp.conf | 0 .../debian-package-x64/pkg-src/install | 0 .../debian-package-x64/pkg-src/jellyfin.init | 0 .../pkg-src/jellyfin.service | 0 .../pkg-src/jellyfin.upstart | 0 .../debian-package-x64/pkg-src/po/POTFILES.in | 0 .../pkg-src/po/templates.pot | 0 .../debian-package-x64/pkg-src/postinst | 0 .../debian-package-x64/pkg-src/postrm | 0 .../debian-package-x64/pkg-src/preinst | 0 .../debian-package-x64/pkg-src/prerm | 0 .../debian-package-x64/pkg-src/rules | 0 .../pkg-src/source.lintian-overrides | 0 .../debian-package-x64/pkg-src/source/format | 0 .../debian-package-x64/pkg-src/source/options | 0 .../{ => old}/fedora-package-x64/Dockerfile | 0 .../{ => old}/fedora-package-x64/clean.sh | 0 .../fedora-package-x64/dependencies.txt | 0 .../fedora-package-x64/docker-build.sh | 0 .../{ => old}/fedora-package-x64/package.sh | 0 .../fedora-package-x64/pkg-src/.gitignore | 0 .../fedora-package-x64/pkg-src/README.md | 0 .../pkg-src/jellyfin-firewalld.xml | 0 .../fedora-package-x64/pkg-src/jellyfin.env | 0 .../pkg-src/jellyfin.override.conf | 0 .../pkg-src/jellyfin.service | 0 .../fedora-package-x64/pkg-src/jellyfin.spec | 0 .../pkg-src/jellyfin.sudoers | 0 .../fedora-package-x64/pkg-src}/restart.sh | 0 deployment/{ => old}/linux-x64/Dockerfile | 0 deployment/{ => old}/linux-x64/clean.sh | 0 .../{ => old}/linux-x64/dependencies.txt | 0 .../{ => old}/linux-x64/docker-build.sh | 0 deployment/{ => old}/linux-x64/package.sh | 0 deployment/{ => old}/macos/Dockerfile | 0 deployment/{ => old}/macos/clean.sh | 0 deployment/{ => old}/macos/dependencies.txt | 0 deployment/{ => old}/macos/docker-build.sh | 0 deployment/{ => old}/macos/package.sh | 0 deployment/{ => old}/portable/Dockerfile | 0 deployment/{ => old}/portable/clean.sh | 0 .../{ => old}/portable/dependencies.txt | 0 deployment/{ => old}/portable/docker-build.sh | 0 deployment/{ => old}/portable/package.sh | 0 .../ubuntu-package-arm64/Dockerfile.amd64 | 0 .../ubuntu-package-arm64/Dockerfile.arm64 | 0 .../{ => old}/ubuntu-package-arm64/clean.sh | 0 .../ubuntu-package-arm64/dependencies.txt | 0 .../ubuntu-package-arm64/docker-build.sh | 0 .../{ => old}/ubuntu-package-arm64/package.sh | 0 .../{ => old}/ubuntu-package-arm64/pkg-src | 0 .../ubuntu-package-armhf/Dockerfile.amd64 | 0 .../ubuntu-package-armhf/Dockerfile.armhf | 0 .../{ => old}/ubuntu-package-armhf/clean.sh | 0 .../ubuntu-package-armhf/dependencies.txt | 0 .../ubuntu-package-armhf/docker-build.sh | 0 .../{ => old}/ubuntu-package-armhf/package.sh | 0 .../{ => old}/ubuntu-package-armhf/pkg-src | 0 .../{ => old}/ubuntu-package-x64/Dockerfile | 0 .../{ => old}/ubuntu-package-x64/clean.sh | 0 .../ubuntu-package-x64/dependencies.txt | 0 .../ubuntu-package-x64/docker-build.sh | 0 .../{ => old}/ubuntu-package-x64/package.sh | 0 .../{ => old}/ubuntu-package-x64/pkg-src | 0 .../unraid/docker-templates/README.md | 0 .../unraid/docker-templates/jellyfin.xml | 0 deployment/{ => old}/win-x64/Dockerfile | 0 deployment/{ => old}/win-x64/clean.sh | 0 deployment/{ => old}/win-x64/dependencies.txt | 0 deployment/{ => old}/win-x64/docker-build.sh | 0 deployment/{ => old}/win-x64/package.sh | 0 deployment/{ => old}/win-x86/Dockerfile | 0 deployment/{ => old}/win-x86/clean.sh | 0 deployment/{ => old}/win-x86/dependencies.txt | 0 deployment/{ => old}/win-x86/docker-build.sh | 0 deployment/{ => old}/win-x86/package.sh | 0 .../{ => old}/windows/build-jellyfin.ps1 | 0 deployment/{ => old}/windows/dependencies.txt | 0 .../windows/dialogs/confirmation.nsddef | 0 .../windows/dialogs/confirmation.nsdinc | 0 .../windows/dialogs/service-config.nsddef | 0 .../windows/dialogs/service-config.nsdinc | 0 .../windows/dialogs/setuptype.nsddef | 0 .../windows/dialogs/setuptype.nsdinc | 0 .../{ => old}/windows/helpers/ShowError.nsh | 0 .../{ => old}/windows/helpers/StrSlash.nsh | 0 deployment/{ => old}/windows/jellyfin.nsi | 0 .../windows/legacy/install-jellyfin.ps1 | 0 .../{ => old}/windows/legacy/install.bat | 0 124 files changed, 36 deletions(-) delete mode 100755 deployment/fedora-package-x64/pkg-src/restart.sh rename deployment/{ => old}/README.md (100%) rename deployment/{ => old}/centos-package-x64/Dockerfile (100%) rename deployment/{ => old}/centos-package-x64/clean.sh (100%) rename deployment/{ => old}/centos-package-x64/dependencies.txt (100%) rename deployment/{ => old}/centos-package-x64/docker-build.sh (100%) rename deployment/{ => old}/centos-package-x64/package.sh (100%) rename deployment/{ => old}/centos-package-x64/pkg-src (100%) rename deployment/{ => old}/debian-package-arm64/Dockerfile.amd64 (100%) rename deployment/{ => old}/debian-package-arm64/Dockerfile.arm64 (100%) rename deployment/{ => old}/debian-package-arm64/clean.sh (100%) rename deployment/{ => old}/debian-package-arm64/dependencies.txt (100%) rename deployment/{ => old}/debian-package-arm64/docker-build.sh (100%) rename deployment/{ => old}/debian-package-arm64/package.sh (100%) rename deployment/{ => old}/debian-package-arm64/pkg-src (100%) rename deployment/{ => old}/debian-package-armhf/Dockerfile.amd64 (100%) rename deployment/{ => old}/debian-package-armhf/Dockerfile.armhf (100%) rename deployment/{ => old}/debian-package-armhf/clean.sh (100%) rename deployment/{ => old}/debian-package-armhf/dependencies.txt (100%) rename deployment/{ => old}/debian-package-armhf/docker-build.sh (100%) rename deployment/{ => old}/debian-package-armhf/package.sh (100%) rename deployment/{ => old}/debian-package-armhf/pkg-src (100%) rename deployment/{ => old}/debian-package-x64/Dockerfile (100%) rename deployment/{ => old}/debian-package-x64/clean.sh (100%) rename deployment/{ => old}/debian-package-x64/dependencies.txt (100%) rename deployment/{ => old}/debian-package-x64/docker-build.sh (100%) rename deployment/{ => old}/debian-package-x64/package.sh (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/changelog (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/compat (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/conf/jellyfin (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/conf/jellyfin-sudoers (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/conf/jellyfin.service.conf (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/conf/logging.json (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/control (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/copyright (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/gbp.conf (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/install (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/jellyfin.init (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/jellyfin.service (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/jellyfin.upstart (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/po/POTFILES.in (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/po/templates.pot (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/postinst (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/postrm (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/preinst (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/prerm (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/rules (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/source.lintian-overrides (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/source/format (100%) rename deployment/{ => old}/debian-package-x64/pkg-src/source/options (100%) rename deployment/{ => old}/fedora-package-x64/Dockerfile (100%) rename deployment/{ => old}/fedora-package-x64/clean.sh (100%) rename deployment/{ => old}/fedora-package-x64/dependencies.txt (100%) rename deployment/{ => old}/fedora-package-x64/docker-build.sh (100%) rename deployment/{ => old}/fedora-package-x64/package.sh (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/.gitignore (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/README.md (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin-firewalld.xml (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin.env (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin.override.conf (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin.service (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin.spec (100%) rename deployment/{ => old}/fedora-package-x64/pkg-src/jellyfin.sudoers (100%) rename deployment/{debian-package-x64/pkg-src/bin => old/fedora-package-x64/pkg-src}/restart.sh (100%) rename deployment/{ => old}/linux-x64/Dockerfile (100%) rename deployment/{ => old}/linux-x64/clean.sh (100%) rename deployment/{ => old}/linux-x64/dependencies.txt (100%) rename deployment/{ => old}/linux-x64/docker-build.sh (100%) rename deployment/{ => old}/linux-x64/package.sh (100%) rename deployment/{ => old}/macos/Dockerfile (100%) rename deployment/{ => old}/macos/clean.sh (100%) rename deployment/{ => old}/macos/dependencies.txt (100%) rename deployment/{ => old}/macos/docker-build.sh (100%) rename deployment/{ => old}/macos/package.sh (100%) rename deployment/{ => old}/portable/Dockerfile (100%) rename deployment/{ => old}/portable/clean.sh (100%) rename deployment/{ => old}/portable/dependencies.txt (100%) rename deployment/{ => old}/portable/docker-build.sh (100%) rename deployment/{ => old}/portable/package.sh (100%) rename deployment/{ => old}/ubuntu-package-arm64/Dockerfile.amd64 (100%) rename deployment/{ => old}/ubuntu-package-arm64/Dockerfile.arm64 (100%) rename deployment/{ => old}/ubuntu-package-arm64/clean.sh (100%) rename deployment/{ => old}/ubuntu-package-arm64/dependencies.txt (100%) rename deployment/{ => old}/ubuntu-package-arm64/docker-build.sh (100%) rename deployment/{ => old}/ubuntu-package-arm64/package.sh (100%) rename deployment/{ => old}/ubuntu-package-arm64/pkg-src (100%) rename deployment/{ => old}/ubuntu-package-armhf/Dockerfile.amd64 (100%) rename deployment/{ => old}/ubuntu-package-armhf/Dockerfile.armhf (100%) rename deployment/{ => old}/ubuntu-package-armhf/clean.sh (100%) rename deployment/{ => old}/ubuntu-package-armhf/dependencies.txt (100%) rename deployment/{ => old}/ubuntu-package-armhf/docker-build.sh (100%) rename deployment/{ => old}/ubuntu-package-armhf/package.sh (100%) rename deployment/{ => old}/ubuntu-package-armhf/pkg-src (100%) rename deployment/{ => old}/ubuntu-package-x64/Dockerfile (100%) rename deployment/{ => old}/ubuntu-package-x64/clean.sh (100%) rename deployment/{ => old}/ubuntu-package-x64/dependencies.txt (100%) rename deployment/{ => old}/ubuntu-package-x64/docker-build.sh (100%) rename deployment/{ => old}/ubuntu-package-x64/package.sh (100%) rename deployment/{ => old}/ubuntu-package-x64/pkg-src (100%) rename deployment/{ => old}/unraid/docker-templates/README.md (100%) rename deployment/{ => old}/unraid/docker-templates/jellyfin.xml (100%) rename deployment/{ => old}/win-x64/Dockerfile (100%) rename deployment/{ => old}/win-x64/clean.sh (100%) rename deployment/{ => old}/win-x64/dependencies.txt (100%) rename deployment/{ => old}/win-x64/docker-build.sh (100%) rename deployment/{ => old}/win-x64/package.sh (100%) rename deployment/{ => old}/win-x86/Dockerfile (100%) rename deployment/{ => old}/win-x86/clean.sh (100%) rename deployment/{ => old}/win-x86/dependencies.txt (100%) rename deployment/{ => old}/win-x86/docker-build.sh (100%) rename deployment/{ => old}/win-x86/package.sh (100%) rename deployment/{ => old}/windows/build-jellyfin.ps1 (100%) rename deployment/{ => old}/windows/dependencies.txt (100%) rename deployment/{ => old}/windows/dialogs/confirmation.nsddef (100%) rename deployment/{ => old}/windows/dialogs/confirmation.nsdinc (100%) rename deployment/{ => old}/windows/dialogs/service-config.nsddef (100%) rename deployment/{ => old}/windows/dialogs/service-config.nsdinc (100%) rename deployment/{ => old}/windows/dialogs/setuptype.nsddef (100%) rename deployment/{ => old}/windows/dialogs/setuptype.nsdinc (100%) rename deployment/{ => old}/windows/helpers/ShowError.nsh (100%) rename deployment/{ => old}/windows/helpers/StrSlash.nsh (100%) rename deployment/{ => old}/windows/jellyfin.nsi (100%) rename deployment/{ => old}/windows/legacy/install-jellyfin.ps1 (100%) rename deployment/{ => old}/windows/legacy/install.bat (100%) diff --git a/deployment/fedora-package-x64/pkg-src/restart.sh b/deployment/fedora-package-x64/pkg-src/restart.sh deleted file mode 100755 index 9b64b6d728..0000000000 --- a/deployment/fedora-package-x64/pkg-src/restart.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# restart.sh - Jellyfin server restart script -# Part of the Jellyfin project (https://github.com/jellyfin) -# -# This script restarts the Jellyfin daemon on Linux when using -# the Restart button on the admin dashboard. It supports the -# systemctl, service, and traditional /etc/init.d (sysv) restart -# methods, chosen automatically by which one is found first (in -# that order). -# -# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. - -get_service_command() { - for command in systemctl service; do - if which $command &>/dev/null; then - echo $command && return - fi - done - echo "sysv" -} - -cmd="$( get_service_command )" -echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." -case $cmd in - 'systemctl') - echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now - ;; - 'service') - echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now - ;; - 'sysv') - echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now - ;; -esac -exit 0 diff --git a/deployment/README.md b/deployment/old/README.md similarity index 100% rename from deployment/README.md rename to deployment/old/README.md diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/old/centos-package-x64/Dockerfile similarity index 100% rename from deployment/centos-package-x64/Dockerfile rename to deployment/old/centos-package-x64/Dockerfile diff --git a/deployment/centos-package-x64/clean.sh b/deployment/old/centos-package-x64/clean.sh similarity index 100% rename from deployment/centos-package-x64/clean.sh rename to deployment/old/centos-package-x64/clean.sh diff --git a/deployment/centos-package-x64/dependencies.txt b/deployment/old/centos-package-x64/dependencies.txt similarity index 100% rename from deployment/centos-package-x64/dependencies.txt rename to deployment/old/centos-package-x64/dependencies.txt diff --git a/deployment/centos-package-x64/docker-build.sh b/deployment/old/centos-package-x64/docker-build.sh similarity index 100% rename from deployment/centos-package-x64/docker-build.sh rename to deployment/old/centos-package-x64/docker-build.sh diff --git a/deployment/centos-package-x64/package.sh b/deployment/old/centos-package-x64/package.sh similarity index 100% rename from deployment/centos-package-x64/package.sh rename to deployment/old/centos-package-x64/package.sh diff --git a/deployment/centos-package-x64/pkg-src b/deployment/old/centos-package-x64/pkg-src similarity index 100% rename from deployment/centos-package-x64/pkg-src rename to deployment/old/centos-package-x64/pkg-src diff --git a/deployment/debian-package-arm64/Dockerfile.amd64 b/deployment/old/debian-package-arm64/Dockerfile.amd64 similarity index 100% rename from deployment/debian-package-arm64/Dockerfile.amd64 rename to deployment/old/debian-package-arm64/Dockerfile.amd64 diff --git a/deployment/debian-package-arm64/Dockerfile.arm64 b/deployment/old/debian-package-arm64/Dockerfile.arm64 similarity index 100% rename from deployment/debian-package-arm64/Dockerfile.arm64 rename to deployment/old/debian-package-arm64/Dockerfile.arm64 diff --git a/deployment/debian-package-arm64/clean.sh b/deployment/old/debian-package-arm64/clean.sh similarity index 100% rename from deployment/debian-package-arm64/clean.sh rename to deployment/old/debian-package-arm64/clean.sh diff --git a/deployment/debian-package-arm64/dependencies.txt b/deployment/old/debian-package-arm64/dependencies.txt similarity index 100% rename from deployment/debian-package-arm64/dependencies.txt rename to deployment/old/debian-package-arm64/dependencies.txt diff --git a/deployment/debian-package-arm64/docker-build.sh b/deployment/old/debian-package-arm64/docker-build.sh similarity index 100% rename from deployment/debian-package-arm64/docker-build.sh rename to deployment/old/debian-package-arm64/docker-build.sh diff --git a/deployment/debian-package-arm64/package.sh b/deployment/old/debian-package-arm64/package.sh similarity index 100% rename from deployment/debian-package-arm64/package.sh rename to deployment/old/debian-package-arm64/package.sh diff --git a/deployment/debian-package-arm64/pkg-src b/deployment/old/debian-package-arm64/pkg-src similarity index 100% rename from deployment/debian-package-arm64/pkg-src rename to deployment/old/debian-package-arm64/pkg-src diff --git a/deployment/debian-package-armhf/Dockerfile.amd64 b/deployment/old/debian-package-armhf/Dockerfile.amd64 similarity index 100% rename from deployment/debian-package-armhf/Dockerfile.amd64 rename to deployment/old/debian-package-armhf/Dockerfile.amd64 diff --git a/deployment/debian-package-armhf/Dockerfile.armhf b/deployment/old/debian-package-armhf/Dockerfile.armhf similarity index 100% rename from deployment/debian-package-armhf/Dockerfile.armhf rename to deployment/old/debian-package-armhf/Dockerfile.armhf diff --git a/deployment/debian-package-armhf/clean.sh b/deployment/old/debian-package-armhf/clean.sh similarity index 100% rename from deployment/debian-package-armhf/clean.sh rename to deployment/old/debian-package-armhf/clean.sh diff --git a/deployment/debian-package-armhf/dependencies.txt b/deployment/old/debian-package-armhf/dependencies.txt similarity index 100% rename from deployment/debian-package-armhf/dependencies.txt rename to deployment/old/debian-package-armhf/dependencies.txt diff --git a/deployment/debian-package-armhf/docker-build.sh b/deployment/old/debian-package-armhf/docker-build.sh similarity index 100% rename from deployment/debian-package-armhf/docker-build.sh rename to deployment/old/debian-package-armhf/docker-build.sh diff --git a/deployment/debian-package-armhf/package.sh b/deployment/old/debian-package-armhf/package.sh similarity index 100% rename from deployment/debian-package-armhf/package.sh rename to deployment/old/debian-package-armhf/package.sh diff --git a/deployment/debian-package-armhf/pkg-src b/deployment/old/debian-package-armhf/pkg-src similarity index 100% rename from deployment/debian-package-armhf/pkg-src rename to deployment/old/debian-package-armhf/pkg-src diff --git a/deployment/debian-package-x64/Dockerfile b/deployment/old/debian-package-x64/Dockerfile similarity index 100% rename from deployment/debian-package-x64/Dockerfile rename to deployment/old/debian-package-x64/Dockerfile diff --git a/deployment/debian-package-x64/clean.sh b/deployment/old/debian-package-x64/clean.sh similarity index 100% rename from deployment/debian-package-x64/clean.sh rename to deployment/old/debian-package-x64/clean.sh diff --git a/deployment/debian-package-x64/dependencies.txt b/deployment/old/debian-package-x64/dependencies.txt similarity index 100% rename from deployment/debian-package-x64/dependencies.txt rename to deployment/old/debian-package-x64/dependencies.txt diff --git a/deployment/debian-package-x64/docker-build.sh b/deployment/old/debian-package-x64/docker-build.sh similarity index 100% rename from deployment/debian-package-x64/docker-build.sh rename to deployment/old/debian-package-x64/docker-build.sh diff --git a/deployment/debian-package-x64/package.sh b/deployment/old/debian-package-x64/package.sh similarity index 100% rename from deployment/debian-package-x64/package.sh rename to deployment/old/debian-package-x64/package.sh diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/old/debian-package-x64/pkg-src/changelog similarity index 100% rename from deployment/debian-package-x64/pkg-src/changelog rename to deployment/old/debian-package-x64/pkg-src/changelog diff --git a/deployment/debian-package-x64/pkg-src/compat b/deployment/old/debian-package-x64/pkg-src/compat similarity index 100% rename from deployment/debian-package-x64/pkg-src/compat rename to deployment/old/debian-package-x64/pkg-src/compat diff --git a/deployment/debian-package-x64/pkg-src/conf/jellyfin b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin similarity index 100% rename from deployment/debian-package-x64/pkg-src/conf/jellyfin rename to deployment/old/debian-package-x64/pkg-src/conf/jellyfin diff --git a/deployment/debian-package-x64/pkg-src/conf/jellyfin-sudoers b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers similarity index 100% rename from deployment/debian-package-x64/pkg-src/conf/jellyfin-sudoers rename to deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers diff --git a/deployment/debian-package-x64/pkg-src/conf/jellyfin.service.conf b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf similarity index 100% rename from deployment/debian-package-x64/pkg-src/conf/jellyfin.service.conf rename to deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf diff --git a/deployment/debian-package-x64/pkg-src/conf/logging.json b/deployment/old/debian-package-x64/pkg-src/conf/logging.json similarity index 100% rename from deployment/debian-package-x64/pkg-src/conf/logging.json rename to deployment/old/debian-package-x64/pkg-src/conf/logging.json diff --git a/deployment/debian-package-x64/pkg-src/control b/deployment/old/debian-package-x64/pkg-src/control similarity index 100% rename from deployment/debian-package-x64/pkg-src/control rename to deployment/old/debian-package-x64/pkg-src/control diff --git a/deployment/debian-package-x64/pkg-src/copyright b/deployment/old/debian-package-x64/pkg-src/copyright similarity index 100% rename from deployment/debian-package-x64/pkg-src/copyright rename to deployment/old/debian-package-x64/pkg-src/copyright diff --git a/deployment/debian-package-x64/pkg-src/gbp.conf b/deployment/old/debian-package-x64/pkg-src/gbp.conf similarity index 100% rename from deployment/debian-package-x64/pkg-src/gbp.conf rename to deployment/old/debian-package-x64/pkg-src/gbp.conf diff --git a/deployment/debian-package-x64/pkg-src/install b/deployment/old/debian-package-x64/pkg-src/install similarity index 100% rename from deployment/debian-package-x64/pkg-src/install rename to deployment/old/debian-package-x64/pkg-src/install diff --git a/deployment/debian-package-x64/pkg-src/jellyfin.init b/deployment/old/debian-package-x64/pkg-src/jellyfin.init similarity index 100% rename from deployment/debian-package-x64/pkg-src/jellyfin.init rename to deployment/old/debian-package-x64/pkg-src/jellyfin.init diff --git a/deployment/debian-package-x64/pkg-src/jellyfin.service b/deployment/old/debian-package-x64/pkg-src/jellyfin.service similarity index 100% rename from deployment/debian-package-x64/pkg-src/jellyfin.service rename to deployment/old/debian-package-x64/pkg-src/jellyfin.service diff --git a/deployment/debian-package-x64/pkg-src/jellyfin.upstart b/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart similarity index 100% rename from deployment/debian-package-x64/pkg-src/jellyfin.upstart rename to deployment/old/debian-package-x64/pkg-src/jellyfin.upstart diff --git a/deployment/debian-package-x64/pkg-src/po/POTFILES.in b/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in similarity index 100% rename from deployment/debian-package-x64/pkg-src/po/POTFILES.in rename to deployment/old/debian-package-x64/pkg-src/po/POTFILES.in diff --git a/deployment/debian-package-x64/pkg-src/po/templates.pot b/deployment/old/debian-package-x64/pkg-src/po/templates.pot similarity index 100% rename from deployment/debian-package-x64/pkg-src/po/templates.pot rename to deployment/old/debian-package-x64/pkg-src/po/templates.pot diff --git a/deployment/debian-package-x64/pkg-src/postinst b/deployment/old/debian-package-x64/pkg-src/postinst similarity index 100% rename from deployment/debian-package-x64/pkg-src/postinst rename to deployment/old/debian-package-x64/pkg-src/postinst diff --git a/deployment/debian-package-x64/pkg-src/postrm b/deployment/old/debian-package-x64/pkg-src/postrm similarity index 100% rename from deployment/debian-package-x64/pkg-src/postrm rename to deployment/old/debian-package-x64/pkg-src/postrm diff --git a/deployment/debian-package-x64/pkg-src/preinst b/deployment/old/debian-package-x64/pkg-src/preinst similarity index 100% rename from deployment/debian-package-x64/pkg-src/preinst rename to deployment/old/debian-package-x64/pkg-src/preinst diff --git a/deployment/debian-package-x64/pkg-src/prerm b/deployment/old/debian-package-x64/pkg-src/prerm similarity index 100% rename from deployment/debian-package-x64/pkg-src/prerm rename to deployment/old/debian-package-x64/pkg-src/prerm diff --git a/deployment/debian-package-x64/pkg-src/rules b/deployment/old/debian-package-x64/pkg-src/rules similarity index 100% rename from deployment/debian-package-x64/pkg-src/rules rename to deployment/old/debian-package-x64/pkg-src/rules diff --git a/deployment/debian-package-x64/pkg-src/source.lintian-overrides b/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides similarity index 100% rename from deployment/debian-package-x64/pkg-src/source.lintian-overrides rename to deployment/old/debian-package-x64/pkg-src/source.lintian-overrides diff --git a/deployment/debian-package-x64/pkg-src/source/format b/deployment/old/debian-package-x64/pkg-src/source/format similarity index 100% rename from deployment/debian-package-x64/pkg-src/source/format rename to deployment/old/debian-package-x64/pkg-src/source/format diff --git a/deployment/debian-package-x64/pkg-src/source/options b/deployment/old/debian-package-x64/pkg-src/source/options similarity index 100% rename from deployment/debian-package-x64/pkg-src/source/options rename to deployment/old/debian-package-x64/pkg-src/source/options diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/old/fedora-package-x64/Dockerfile similarity index 100% rename from deployment/fedora-package-x64/Dockerfile rename to deployment/old/fedora-package-x64/Dockerfile diff --git a/deployment/fedora-package-x64/clean.sh b/deployment/old/fedora-package-x64/clean.sh similarity index 100% rename from deployment/fedora-package-x64/clean.sh rename to deployment/old/fedora-package-x64/clean.sh diff --git a/deployment/fedora-package-x64/dependencies.txt b/deployment/old/fedora-package-x64/dependencies.txt similarity index 100% rename from deployment/fedora-package-x64/dependencies.txt rename to deployment/old/fedora-package-x64/dependencies.txt diff --git a/deployment/fedora-package-x64/docker-build.sh b/deployment/old/fedora-package-x64/docker-build.sh similarity index 100% rename from deployment/fedora-package-x64/docker-build.sh rename to deployment/old/fedora-package-x64/docker-build.sh diff --git a/deployment/fedora-package-x64/package.sh b/deployment/old/fedora-package-x64/package.sh similarity index 100% rename from deployment/fedora-package-x64/package.sh rename to deployment/old/fedora-package-x64/package.sh diff --git a/deployment/fedora-package-x64/pkg-src/.gitignore b/deployment/old/fedora-package-x64/pkg-src/.gitignore similarity index 100% rename from deployment/fedora-package-x64/pkg-src/.gitignore rename to deployment/old/fedora-package-x64/pkg-src/.gitignore diff --git a/deployment/fedora-package-x64/pkg-src/README.md b/deployment/old/fedora-package-x64/pkg-src/README.md similarity index 100% rename from deployment/fedora-package-x64/pkg-src/README.md rename to deployment/old/fedora-package-x64/pkg-src/README.md diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin-firewalld.xml b/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin-firewalld.xml rename to deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.env b/deployment/old/fedora-package-x64/pkg-src/jellyfin.env similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin.env rename to deployment/old/fedora-package-x64/pkg-src/jellyfin.env diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.override.conf b/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin.override.conf rename to deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.service b/deployment/old/fedora-package-x64/pkg-src/jellyfin.service similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin.service rename to deployment/old/fedora-package-x64/pkg-src/jellyfin.service diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin.spec rename to deployment/old/fedora-package-x64/pkg-src/jellyfin.spec diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.sudoers b/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers similarity index 100% rename from deployment/fedora-package-x64/pkg-src/jellyfin.sudoers rename to deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers diff --git a/deployment/debian-package-x64/pkg-src/bin/restart.sh b/deployment/old/fedora-package-x64/pkg-src/restart.sh similarity index 100% rename from deployment/debian-package-x64/pkg-src/bin/restart.sh rename to deployment/old/fedora-package-x64/pkg-src/restart.sh diff --git a/deployment/linux-x64/Dockerfile b/deployment/old/linux-x64/Dockerfile similarity index 100% rename from deployment/linux-x64/Dockerfile rename to deployment/old/linux-x64/Dockerfile diff --git a/deployment/linux-x64/clean.sh b/deployment/old/linux-x64/clean.sh similarity index 100% rename from deployment/linux-x64/clean.sh rename to deployment/old/linux-x64/clean.sh diff --git a/deployment/linux-x64/dependencies.txt b/deployment/old/linux-x64/dependencies.txt similarity index 100% rename from deployment/linux-x64/dependencies.txt rename to deployment/old/linux-x64/dependencies.txt diff --git a/deployment/linux-x64/docker-build.sh b/deployment/old/linux-x64/docker-build.sh similarity index 100% rename from deployment/linux-x64/docker-build.sh rename to deployment/old/linux-x64/docker-build.sh diff --git a/deployment/linux-x64/package.sh b/deployment/old/linux-x64/package.sh similarity index 100% rename from deployment/linux-x64/package.sh rename to deployment/old/linux-x64/package.sh diff --git a/deployment/macos/Dockerfile b/deployment/old/macos/Dockerfile similarity index 100% rename from deployment/macos/Dockerfile rename to deployment/old/macos/Dockerfile diff --git a/deployment/macos/clean.sh b/deployment/old/macos/clean.sh similarity index 100% rename from deployment/macos/clean.sh rename to deployment/old/macos/clean.sh diff --git a/deployment/macos/dependencies.txt b/deployment/old/macos/dependencies.txt similarity index 100% rename from deployment/macos/dependencies.txt rename to deployment/old/macos/dependencies.txt diff --git a/deployment/macos/docker-build.sh b/deployment/old/macos/docker-build.sh similarity index 100% rename from deployment/macos/docker-build.sh rename to deployment/old/macos/docker-build.sh diff --git a/deployment/macos/package.sh b/deployment/old/macos/package.sh similarity index 100% rename from deployment/macos/package.sh rename to deployment/old/macos/package.sh diff --git a/deployment/portable/Dockerfile b/deployment/old/portable/Dockerfile similarity index 100% rename from deployment/portable/Dockerfile rename to deployment/old/portable/Dockerfile diff --git a/deployment/portable/clean.sh b/deployment/old/portable/clean.sh similarity index 100% rename from deployment/portable/clean.sh rename to deployment/old/portable/clean.sh diff --git a/deployment/portable/dependencies.txt b/deployment/old/portable/dependencies.txt similarity index 100% rename from deployment/portable/dependencies.txt rename to deployment/old/portable/dependencies.txt diff --git a/deployment/portable/docker-build.sh b/deployment/old/portable/docker-build.sh similarity index 100% rename from deployment/portable/docker-build.sh rename to deployment/old/portable/docker-build.sh diff --git a/deployment/portable/package.sh b/deployment/old/portable/package.sh similarity index 100% rename from deployment/portable/package.sh rename to deployment/old/portable/package.sh diff --git a/deployment/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 similarity index 100% rename from deployment/ubuntu-package-arm64/Dockerfile.amd64 rename to deployment/old/ubuntu-package-arm64/Dockerfile.amd64 diff --git a/deployment/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 similarity index 100% rename from deployment/ubuntu-package-arm64/Dockerfile.arm64 rename to deployment/old/ubuntu-package-arm64/Dockerfile.arm64 diff --git a/deployment/ubuntu-package-arm64/clean.sh b/deployment/old/ubuntu-package-arm64/clean.sh similarity index 100% rename from deployment/ubuntu-package-arm64/clean.sh rename to deployment/old/ubuntu-package-arm64/clean.sh diff --git a/deployment/ubuntu-package-arm64/dependencies.txt b/deployment/old/ubuntu-package-arm64/dependencies.txt similarity index 100% rename from deployment/ubuntu-package-arm64/dependencies.txt rename to deployment/old/ubuntu-package-arm64/dependencies.txt diff --git a/deployment/ubuntu-package-arm64/docker-build.sh b/deployment/old/ubuntu-package-arm64/docker-build.sh similarity index 100% rename from deployment/ubuntu-package-arm64/docker-build.sh rename to deployment/old/ubuntu-package-arm64/docker-build.sh diff --git a/deployment/ubuntu-package-arm64/package.sh b/deployment/old/ubuntu-package-arm64/package.sh similarity index 100% rename from deployment/ubuntu-package-arm64/package.sh rename to deployment/old/ubuntu-package-arm64/package.sh diff --git a/deployment/ubuntu-package-arm64/pkg-src b/deployment/old/ubuntu-package-arm64/pkg-src similarity index 100% rename from deployment/ubuntu-package-arm64/pkg-src rename to deployment/old/ubuntu-package-arm64/pkg-src diff --git a/deployment/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 similarity index 100% rename from deployment/ubuntu-package-armhf/Dockerfile.amd64 rename to deployment/old/ubuntu-package-armhf/Dockerfile.amd64 diff --git a/deployment/ubuntu-package-armhf/Dockerfile.armhf b/deployment/old/ubuntu-package-armhf/Dockerfile.armhf similarity index 100% rename from deployment/ubuntu-package-armhf/Dockerfile.armhf rename to deployment/old/ubuntu-package-armhf/Dockerfile.armhf diff --git a/deployment/ubuntu-package-armhf/clean.sh b/deployment/old/ubuntu-package-armhf/clean.sh similarity index 100% rename from deployment/ubuntu-package-armhf/clean.sh rename to deployment/old/ubuntu-package-armhf/clean.sh diff --git a/deployment/ubuntu-package-armhf/dependencies.txt b/deployment/old/ubuntu-package-armhf/dependencies.txt similarity index 100% rename from deployment/ubuntu-package-armhf/dependencies.txt rename to deployment/old/ubuntu-package-armhf/dependencies.txt diff --git a/deployment/ubuntu-package-armhf/docker-build.sh b/deployment/old/ubuntu-package-armhf/docker-build.sh similarity index 100% rename from deployment/ubuntu-package-armhf/docker-build.sh rename to deployment/old/ubuntu-package-armhf/docker-build.sh diff --git a/deployment/ubuntu-package-armhf/package.sh b/deployment/old/ubuntu-package-armhf/package.sh similarity index 100% rename from deployment/ubuntu-package-armhf/package.sh rename to deployment/old/ubuntu-package-armhf/package.sh diff --git a/deployment/ubuntu-package-armhf/pkg-src b/deployment/old/ubuntu-package-armhf/pkg-src similarity index 100% rename from deployment/ubuntu-package-armhf/pkg-src rename to deployment/old/ubuntu-package-armhf/pkg-src diff --git a/deployment/ubuntu-package-x64/Dockerfile b/deployment/old/ubuntu-package-x64/Dockerfile similarity index 100% rename from deployment/ubuntu-package-x64/Dockerfile rename to deployment/old/ubuntu-package-x64/Dockerfile diff --git a/deployment/ubuntu-package-x64/clean.sh b/deployment/old/ubuntu-package-x64/clean.sh similarity index 100% rename from deployment/ubuntu-package-x64/clean.sh rename to deployment/old/ubuntu-package-x64/clean.sh diff --git a/deployment/ubuntu-package-x64/dependencies.txt b/deployment/old/ubuntu-package-x64/dependencies.txt similarity index 100% rename from deployment/ubuntu-package-x64/dependencies.txt rename to deployment/old/ubuntu-package-x64/dependencies.txt diff --git a/deployment/ubuntu-package-x64/docker-build.sh b/deployment/old/ubuntu-package-x64/docker-build.sh similarity index 100% rename from deployment/ubuntu-package-x64/docker-build.sh rename to deployment/old/ubuntu-package-x64/docker-build.sh diff --git a/deployment/ubuntu-package-x64/package.sh b/deployment/old/ubuntu-package-x64/package.sh similarity index 100% rename from deployment/ubuntu-package-x64/package.sh rename to deployment/old/ubuntu-package-x64/package.sh diff --git a/deployment/ubuntu-package-x64/pkg-src b/deployment/old/ubuntu-package-x64/pkg-src similarity index 100% rename from deployment/ubuntu-package-x64/pkg-src rename to deployment/old/ubuntu-package-x64/pkg-src diff --git a/deployment/unraid/docker-templates/README.md b/deployment/old/unraid/docker-templates/README.md similarity index 100% rename from deployment/unraid/docker-templates/README.md rename to deployment/old/unraid/docker-templates/README.md diff --git a/deployment/unraid/docker-templates/jellyfin.xml b/deployment/old/unraid/docker-templates/jellyfin.xml similarity index 100% rename from deployment/unraid/docker-templates/jellyfin.xml rename to deployment/old/unraid/docker-templates/jellyfin.xml diff --git a/deployment/win-x64/Dockerfile b/deployment/old/win-x64/Dockerfile similarity index 100% rename from deployment/win-x64/Dockerfile rename to deployment/old/win-x64/Dockerfile diff --git a/deployment/win-x64/clean.sh b/deployment/old/win-x64/clean.sh similarity index 100% rename from deployment/win-x64/clean.sh rename to deployment/old/win-x64/clean.sh diff --git a/deployment/win-x64/dependencies.txt b/deployment/old/win-x64/dependencies.txt similarity index 100% rename from deployment/win-x64/dependencies.txt rename to deployment/old/win-x64/dependencies.txt diff --git a/deployment/win-x64/docker-build.sh b/deployment/old/win-x64/docker-build.sh similarity index 100% rename from deployment/win-x64/docker-build.sh rename to deployment/old/win-x64/docker-build.sh diff --git a/deployment/win-x64/package.sh b/deployment/old/win-x64/package.sh similarity index 100% rename from deployment/win-x64/package.sh rename to deployment/old/win-x64/package.sh diff --git a/deployment/win-x86/Dockerfile b/deployment/old/win-x86/Dockerfile similarity index 100% rename from deployment/win-x86/Dockerfile rename to deployment/old/win-x86/Dockerfile diff --git a/deployment/win-x86/clean.sh b/deployment/old/win-x86/clean.sh similarity index 100% rename from deployment/win-x86/clean.sh rename to deployment/old/win-x86/clean.sh diff --git a/deployment/win-x86/dependencies.txt b/deployment/old/win-x86/dependencies.txt similarity index 100% rename from deployment/win-x86/dependencies.txt rename to deployment/old/win-x86/dependencies.txt diff --git a/deployment/win-x86/docker-build.sh b/deployment/old/win-x86/docker-build.sh similarity index 100% rename from deployment/win-x86/docker-build.sh rename to deployment/old/win-x86/docker-build.sh diff --git a/deployment/win-x86/package.sh b/deployment/old/win-x86/package.sh similarity index 100% rename from deployment/win-x86/package.sh rename to deployment/old/win-x86/package.sh diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/old/windows/build-jellyfin.ps1 similarity index 100% rename from deployment/windows/build-jellyfin.ps1 rename to deployment/old/windows/build-jellyfin.ps1 diff --git a/deployment/windows/dependencies.txt b/deployment/old/windows/dependencies.txt similarity index 100% rename from deployment/windows/dependencies.txt rename to deployment/old/windows/dependencies.txt diff --git a/deployment/windows/dialogs/confirmation.nsddef b/deployment/old/windows/dialogs/confirmation.nsddef similarity index 100% rename from deployment/windows/dialogs/confirmation.nsddef rename to deployment/old/windows/dialogs/confirmation.nsddef diff --git a/deployment/windows/dialogs/confirmation.nsdinc b/deployment/old/windows/dialogs/confirmation.nsdinc similarity index 100% rename from deployment/windows/dialogs/confirmation.nsdinc rename to deployment/old/windows/dialogs/confirmation.nsdinc diff --git a/deployment/windows/dialogs/service-config.nsddef b/deployment/old/windows/dialogs/service-config.nsddef similarity index 100% rename from deployment/windows/dialogs/service-config.nsddef rename to deployment/old/windows/dialogs/service-config.nsddef diff --git a/deployment/windows/dialogs/service-config.nsdinc b/deployment/old/windows/dialogs/service-config.nsdinc similarity index 100% rename from deployment/windows/dialogs/service-config.nsdinc rename to deployment/old/windows/dialogs/service-config.nsdinc diff --git a/deployment/windows/dialogs/setuptype.nsddef b/deployment/old/windows/dialogs/setuptype.nsddef similarity index 100% rename from deployment/windows/dialogs/setuptype.nsddef rename to deployment/old/windows/dialogs/setuptype.nsddef diff --git a/deployment/windows/dialogs/setuptype.nsdinc b/deployment/old/windows/dialogs/setuptype.nsdinc similarity index 100% rename from deployment/windows/dialogs/setuptype.nsdinc rename to deployment/old/windows/dialogs/setuptype.nsdinc diff --git a/deployment/windows/helpers/ShowError.nsh b/deployment/old/windows/helpers/ShowError.nsh similarity index 100% rename from deployment/windows/helpers/ShowError.nsh rename to deployment/old/windows/helpers/ShowError.nsh diff --git a/deployment/windows/helpers/StrSlash.nsh b/deployment/old/windows/helpers/StrSlash.nsh similarity index 100% rename from deployment/windows/helpers/StrSlash.nsh rename to deployment/old/windows/helpers/StrSlash.nsh diff --git a/deployment/windows/jellyfin.nsi b/deployment/old/windows/jellyfin.nsi similarity index 100% rename from deployment/windows/jellyfin.nsi rename to deployment/old/windows/jellyfin.nsi diff --git a/deployment/windows/legacy/install-jellyfin.ps1 b/deployment/old/windows/legacy/install-jellyfin.ps1 similarity index 100% rename from deployment/windows/legacy/install-jellyfin.ps1 rename to deployment/old/windows/legacy/install-jellyfin.ps1 diff --git a/deployment/windows/legacy/install.bat b/deployment/old/windows/legacy/install.bat similarity index 100% rename from deployment/windows/legacy/install.bat rename to deployment/old/windows/legacy/install.bat From 8b620ed26addec0f42e2797e3e4d45fbd68b0f23 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:01:33 -0400 Subject: [PATCH 026/411] Move Debian folder to root of repo --- debian/changelog | 59 ++++++++++++++++++++ debian/compat | 1 + debian/conf/jellyfin | 40 ++++++++++++++ debian/conf/jellyfin-sudoers | 37 +++++++++++++ debian/conf/jellyfin.service.conf | 7 +++ debian/conf/logging.json | 30 ++++++++++ debian/control | 31 +++++++++++ debian/copyright | 29 ++++++++++ debian/gbp.conf | 6 ++ debian/install | 6 ++ debian/jellyfin.init | 61 ++++++++++++++++++++ debian/jellyfin.service | 14 +++++ debian/jellyfin.upstart | 20 +++++++ debian/po/POTFILES.in | 1 + debian/po/templates.pot | 57 +++++++++++++++++++ debian/postinst | 92 +++++++++++++++++++++++++++++++ debian/postrm | 81 +++++++++++++++++++++++++++ debian/preinst | 78 ++++++++++++++++++++++++++ debian/prerm | 61 ++++++++++++++++++++ debian/rules | 66 ++++++++++++++++++++++ debian/source.lintian-overrides | 3 + debian/source/format | 1 + debian/source/options | 11 ++++ 23 files changed, 792 insertions(+) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/conf/jellyfin create mode 100644 debian/conf/jellyfin-sudoers create mode 100644 debian/conf/jellyfin.service.conf create mode 100644 debian/conf/logging.json create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/gbp.conf create mode 100644 debian/install create mode 100644 debian/jellyfin.init create mode 100644 debian/jellyfin.service create mode 100644 debian/jellyfin.upstart create mode 100644 debian/po/POTFILES.in create mode 100644 debian/po/templates.pot create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100644 debian/preinst create mode 100644 debian/prerm create mode 100755 debian/rules create mode 100644 debian/source.lintian-overrides create mode 100644 debian/source/format create mode 100644 debian/source/options diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..51c4822370 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,59 @@ +jellyfin (10.5.0-1) unstable; urgency=medium + + * New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 + + -- Jellyfin Packaging Team Fri, 11 Oct 2019 20:12:38 -0400 + +jellyfin (10.4.0-1) unstable; urgency=medium + + * New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 + + -- Jellyfin Packaging Team Sat, 31 Aug 2019 21:38:56 -0400 + +jellyfin (10.3.7-1) unstable; urgency=medium + + * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 + + -- Jellyfin Packaging Team Wed, 24 Jul 2019 10:48:28 -0400 + +jellyfin (10.3.6-1) unstable; urgency=medium + + * New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 + + -- Jellyfin Packaging Team Sat, 06 Jul 2019 13:34:19 -0400 + +jellyfin (10.3.5-1) unstable; urgency=medium + + * New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 + + -- Jellyfin Packaging Team Sun, 09 Jun 2019 21:47:35 -0400 + +jellyfin (10.3.4-1) unstable; urgency=medium + + * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 + + -- Jellyfin Packaging Team Thu, 06 Jun 2019 22:45:31 -0400 + +jellyfin (10.3.3-1) unstable; urgency=medium + + * New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 + + -- Jellyfin Packaging Team Fri, 17 May 2019 23:12:08 -0400 + +jellyfin (10.3.2-1) unstable; urgency=medium + + * New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 + + -- Jellyfin Packaging Team Tue, 30 Apr 2019 20:18:44 -0400 + +jellyfin (10.3.1-1) unstable; urgency=medium + + * New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 + + -- Jellyfin Packaging Team Sat, 20 Apr 2019 14:24:07 -0400 + +jellyfin (10.3.0-1) unstable; urgency=medium + + * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 + + -- Jellyfin Packaging Team Fri, 19 Apr 2019 14:24:29 -0400 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000000..45a4fb75db --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/conf/jellyfin b/debian/conf/jellyfin new file mode 100644 index 0000000000..c6e595f15a --- /dev/null +++ b/debian/conf/jellyfin @@ -0,0 +1,40 @@ +# Jellyfin default configuration options +# This is a POSIX shell fragment + +# Use this file to override the default configurations; add additional +# options with JELLYFIN_ADD_OPTS. + +# Under systemd, use +# /etc/systemd/system/jellyfin.service.d/jellyfin.service.conf +# to override the user or this config file's location. + +# +# General options +# + +# Program directories +JELLYFIN_DATA_DIR="/var/lib/jellyfin" +JELLYFIN_CONFIG_DIR="/etc/jellyfin" +JELLYFIN_LOG_DIR="/var/log/jellyfin" +JELLYFIN_CACHE_DIR="/var/cache/jellyfin" + +# Restart script for in-app server control +JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh" + +# ffmpeg binary paths, overriding the system values +JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" + +# [OPTIONAL] run Jellyfin as a headless service +#JELLYFIN_SERVICE_OPT="--service" + +# [OPTIONAL] run Jellyfin without the web app +#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" + +# +# SysV init/Upstart options +# + +# Application username +JELLYFIN_USER="jellyfin" +# Full application command +JELLYFIN_ARGS="$JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" diff --git a/debian/conf/jellyfin-sudoers b/debian/conf/jellyfin-sudoers new file mode 100644 index 0000000000..b481ba4ad4 --- /dev/null +++ b/debian/conf/jellyfin-sudoers @@ -0,0 +1,37 @@ +#Allow jellyfin group to start, stop and restart itself +Cmnd_Alias RESTARTSERVER_SYSV = /sbin/service jellyfin restart, /usr/sbin/service jellyfin restart +Cmnd_Alias STARTSERVER_SYSV = /sbin/service jellyfin start, /usr/sbin/service jellyfin start +Cmnd_Alias STOPSERVER_SYSV = /sbin/service jellyfin stop, /usr/sbin/service jellyfin stop +Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin +Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin +Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin +Cmnd_Alias RESTARTSERVER_INITD = /etc/init.d/jellyfin restart +Cmnd_Alias STARTSERVER_INITD = /etc/init.d/jellyfin start +Cmnd_Alias STOPSERVER_INITD = /etc/init.d/jellyfin stop + + +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_INITD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_INITD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_INITD + +Defaults!RESTARTSERVER_SYSV !requiretty +Defaults!STARTSERVER_SYSV !requiretty +Defaults!STOPSERVER_SYSV !requiretty +Defaults!RESTARTSERVER_SYSTEMD !requiretty +Defaults!STARTSERVER_SYSTEMD !requiretty +Defaults!STOPSERVER_SYSTEMD !requiretty +Defaults!RESTARTSERVER_INITD !requiretty +Defaults!STARTSERVER_INITD !requiretty +Defaults!STOPSERVER_INITD !requiretty + +#Allow the server to mount iso images +jellyfin ALL=(ALL) NOPASSWD: /bin/mount +jellyfin ALL=(ALL) NOPASSWD: /bin/umount + +Defaults:jellyfin !requiretty diff --git a/debian/conf/jellyfin.service.conf b/debian/conf/jellyfin.service.conf new file mode 100644 index 0000000000..1b69dd74ef --- /dev/null +++ b/debian/conf/jellyfin.service.conf @@ -0,0 +1,7 @@ +# Jellyfin systemd configuration options + +# Use this file to override the user or environment file location. + +[Service] +#User = jellyfin +#EnvironmentFile = /etc/default/jellyfin diff --git a/debian/conf/logging.json b/debian/conf/logging.json new file mode 100644 index 0000000000..f32b2089eb --- /dev/null +++ b/debian/conf/logging.json @@ -0,0 +1,30 @@ +{ + "Serilog": { + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "File", + "Args": { + "path": "%JELLYFIN_LOG_DIR%//jellyfin.log", + "fileSizeLimitBytes": 10485700, + "rollOnFileSizeLimit": true, + "retainedFileCountLimit": 10, + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" + } + } + ] + } + } + ] + } +} diff --git a/debian/control b/debian/control new file mode 100644 index 0000000000..13fd3ecabb --- /dev/null +++ b/debian/control @@ -0,0 +1,31 @@ +Source: jellyfin +Section: misc +Priority: optional +Maintainer: Jellyfin Team +Build-Depends: debhelper (>= 9), + dotnet-sdk-3.1, + libc6-dev, + libcurl4-openssl-dev, + libfontconfig1-dev, + libfreetype6-dev, + libssl-dev, + wget, + npm | nodejs +Standards-Version: 3.9.4 +Homepage: https://jellyfin.media/ +Vcs-Git: https://github.org/jellyfin/jellyfin.git +Vcs-Browser: https://github.org/jellyfin/jellyfin + +Package: jellyfin +Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Architecture: any +Depends: at, + libsqlite3-0, + jellyfin-ffmpeg, + libfontconfig1, + libfreetype6, + libssl1.1 +Description: Jellyfin is a home media server. + It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000000..0d7a2a6007 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: jellyfin +Source: https://github.com/jellyfin/jellyfin + +Files: * +Copyright: 2018 Jellyfin Team +License: GPL-2.0+ + +Files: debian/* +Copyright: 2018 Joshua Boniface +Copyright: 2014 Carlos Hernandez +License: GPL-2.0+ + +License: GPL-2.0+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000000..60b3d28723 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,6 @@ +[DEFAULT] +pristine-tar = False +cleaner = fakeroot debian/rules clean + +[import-orig] +filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/debian/install b/debian/install new file mode 100644 index 0000000000..994322d141 --- /dev/null +++ b/debian/install @@ -0,0 +1,6 @@ +usr/lib/jellyfin usr/lib/ +debian/conf/jellyfin etc/default/ +debian/conf/logging.json etc/jellyfin/ +debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/ +debian/conf/jellyfin-sudoers etc/sudoers.d/ +debian/bin/restart.sh usr/lib/jellyfin/ diff --git a/debian/jellyfin.init b/debian/jellyfin.init new file mode 100644 index 0000000000..7f5642bac1 --- /dev/null +++ b/debian/jellyfin.init @@ -0,0 +1,61 @@ +### BEGIN INIT INFO +# Provides: Jellyfin Media Server +# Required-Start: $local_fs $network +# Required-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Jellyfin Media Server +# Description: Runs Jellyfin Server +### END INIT INFO + +set -e + +# Carry out specific functions when asked to by the system + +if test -f /etc/default/jellyfin; then + . /etc/default/jellyfin +fi + +. /lib/lsb/init-functions + +PIDFILE="/run/jellyfin.pid" + +case "$1" in + start) + log_daemon_msg "Starting Jellyfin Media Server" "jellyfin" || true + + if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + stop) + log_daemon_msg "Stopping Jellyfin Media Server" "jellyfin" || true + if start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + restart) + log_daemon_msg "Restarting Jellyfin Media Server" "jellyfin" || true + start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE --remove-pidfile + if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + status) + status_of_proc -p $PIDFILE /usr/bin/jellyfin jellyfin && exit 0 || exit $? + ;; + + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac diff --git a/debian/jellyfin.service b/debian/jellyfin.service new file mode 100644 index 0000000000..1305e238b0 --- /dev/null +++ b/debian/jellyfin.service @@ -0,0 +1,14 @@ +[Unit] +Description = Jellyfin Media Server +After = network.target + +[Service] +Type = simple +EnvironmentFile = /etc/default/jellyfin +User = jellyfin +ExecStart = /usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +Restart = on-failure +TimeoutSec = 15 + +[Install] +WantedBy = multi-user.target diff --git a/debian/jellyfin.upstart b/debian/jellyfin.upstart new file mode 100644 index 0000000000..ef5bc9bcaf --- /dev/null +++ b/debian/jellyfin.upstart @@ -0,0 +1,20 @@ +description "jellyfin daemon" + +start on (local-filesystems and net-device-up IFACE!=lo) +stop on runlevel [!2345] + +console log +respawn +respawn limit 10 5 + +kill timeout 20 + +script + set -x + echo "Starting $UPSTART_JOB" + + # Log file + logger -t "$0" "DEBUG: `set`" + . /etc/default/jellyfin + exec su -u $JELLYFIN_USER -c /usr/bin/jellyfin $JELLYFIN_ARGS +end script diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in new file mode 100644 index 0000000000..cef83a3407 --- /dev/null +++ b/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] templates diff --git a/debian/po/templates.pot b/debian/po/templates.pot new file mode 100644 index 0000000000..2cdcae4173 --- /dev/null +++ b/debian/po/templates.pot @@ -0,0 +1,57 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: jellyfin-server\n" +"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n" +"POT-Creation-Date: 2015-06-12 20:51-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "Jellyfin permission info:" +msgstr "" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "" +"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the " +"user jellyfin has read and write access to any folders you wish to add to your " +"library. Otherwise please run jellyfin under a different user." +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "Username to run Jellyfin as:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "The user that jellyfin will run as." +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin still running" +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin is currently running. Please close it and try again." +msgstr "" diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000000..860222e051 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,92 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +case "$1" in + configure) + # create jellyfin group if it does not exist + if [[ -z "$(getent group jellyfin)" ]]; then + addgroup --quiet --system jellyfin > /dev/null 2>&1 + fi + # create jellyfin user if it does not exist + if [[ -z "$(getent passwd jellyfin)" ]]; then + adduser --system --ingroup jellyfin --shell /bin/false jellyfin --no-create-home --home ${PROGRAMDATA} \ + --gecos "Jellyfin default user" > /dev/null 2>&1 + fi + # ensure $PROGRAMDATA exists + if [[ ! -d $PROGRAMDATA ]]; then + mkdir $PROGRAMDATA + fi + # ensure $CONFIGDATA exists + if [[ ! -d $CONFIGDATA ]]; then + mkdir $CONFIGDATA + fi + # ensure $LOGDATA exists + if [[ ! -d $LOGDATA ]]; then + mkdir $LOGDATA + fi + # ensure $CACHEDATA exists + if [[ ! -d $CACHEDATA ]]; then + mkdir $CACHEDATA + fi + # Ensure permissions are correct on all config directories + chown -R jellyfin $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + + chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true + + # Install jellyfin symlink into /usr/bin + ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin + + ;; + abort-upgrade|abort-remove|abort-deconfigure) + ;; + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER + +if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + # Manual init script handling + deb-systemd-helper unmask jellyfin.service >/dev/null || true + # was-enabled defaults to true, so new installations run enable. + if deb-systemd-helper --quiet was-enabled jellyfin.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable jellyfin.service >/dev/null || true + else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state jellyfin.service >/dev/null || true + fi +fi + +# End automatically added section +# Automatically added by dh_installinit +if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then + if [[ -d "/run/systemd/systemd" ]]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start jellyfin >/dev/null || true + elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then + update-rc.d jellyfin defaults >/dev/null + invoke-rc.d jellyfin start || exit $? + fi +fi +exit 0 diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000000..1d00a984ec --- /dev/null +++ b/debian/postrm @@ -0,0 +1,81 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [[ -d /run/systemd/system ]] ; then + systemctl --system daemon-reload >/dev/null || true +fi + +case "$1" in + purge) + echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true + + if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.connf" ]]; then + update-rc.d jellyfin remove >/dev/null 2>&1 || true + fi + + if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + deb-systemd-helper purge jellyfin.service >/dev/null + deb-systemd-helper unmask jellyfin.service >/dev/null + fi + + # Remove user and group + userdel jellyfin > /dev/null 2>&1 || true + delgroup --quiet jellyfin > /dev/null 2>&1 || true + # Remove config dir + if [[ -d $CONFIGDATA ]]; then + rm -rf $CONFIGDATA + fi + # Remove log dir + if [[ -d $LOGDATA ]]; then + rm -rf $LOGDATA + fi + # Remove cache dir + if [[ -d $CACHEDATA ]]; then + rm -rf $CACHEDATA + fi + # Remove program data dir + if [[ -d $PROGRAMDATA ]]; then + rm -rf $PROGRAMDATA + fi + # Remove binary symlink + [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin + # Remove sudoers config + [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers + # Remove anything at the default locations; catches situations where the user moved the defaults + [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin + [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin + [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin + [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin + ;; + remove) + if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + deb-systemd-helper mask jellyfin.service >/dev/null + fi + ;; + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 0000000000..2713fb9b80 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,78 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [[ -d /run/systemd/system ]] ; then + systemctl --system daemon-reload >/dev/null || true +fi + +case "$1" in + install|upgrade) + # try graceful termination; + if [[ -d /run/systemd/system ]]; then + deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true + elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then + invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true + fi + # try and figure out if jellyfin is running + PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) + [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) + # if its running, let's stop it + if [[ -n "$JELLYFIN_PID" ]]; then + echo "Stopping Jellyfin!" + # if jellyfin is still running, kill it + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + CPIDS=$(pgrep -P $JELLYFIN_PID) + sleep 2 && kill -KILL $CPIDS + kill -TERM $CPIDS > /dev/null 2>&1 + fi + sleep 1 + # if it's still running, show error + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + echo "Could not successfully stop JellyfinServer, please do so before uninstalling." + exit 1 + else + [[ -f $PIDFILE ]] && rm $PIDFILE + fi + fi + + # Clean up old Emby cruft that can break the user's system + [[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby + + # If we have existing config, log, or cache dirs in /var/lib/jellyfin, move them into the right place + if [[ -d $PROGRAMDATA/config ]]; then + mv $PROGRAMDATA/config $CONFIGDATA + fi + if [[ -d $PROGRAMDATA/logs ]]; then + mv $PROGRAMDATA/logs $LOGDATA + fi + if [[ -d $PROGRAMDATA/logs ]]; then + mv $PROGRAMDATA/cache $CACHEDATA + fi + + ;; + abort-upgrade) + ;; + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac +#DEBHELPER# + +exit 0 diff --git a/debian/prerm b/debian/prerm new file mode 100644 index 0000000000..e965cb7d71 --- /dev/null +++ b/debian/prerm @@ -0,0 +1,61 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +case "$1" in + remove|upgrade|deconfigure) + echo "Stopping Jellyfin!" + # try graceful termination; + if [[ -d /run/systemd/system ]]; then + deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true + elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then + invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true + fi + # Ensure that it is shutdown + PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) + [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) + # if its running, let's stop it + if [[ -n "$JELLYFIN_PID" ]]; then + # if jellyfin is still running, kill it + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + CPIDS=$(pgrep -P $JELLYFIN_PID) + sleep 2 && kill -KILL $CPIDS + kill -TERM $CPIDS > /dev/null 2>&1 + fi + sleep 1 + # if it's still running, show error + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + echo "Could not successfully stop Jellyfin, please do so before uninstalling." + exit 1 + else + [[ -f $PIDFILE ]] && rm $PIDFILE + fi + fi + if [[ -f /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so ]]; then + rm /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so + fi + ;; + failed-upgrade) + ;; + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000000..c2d57dfb22 --- /dev/null +++ b/debian/rules @@ -0,0 +1,66 @@ +#! /usr/bin/make -f +CONFIG := Release +TERM := xterm +SHELL := /bin/bash +WEB_TARGET := $(CURDIR)/MediaBrowser.WebDashboard/jellyfin-web +WEB_VERSION := $(shell sed -n -e 's/^version: "\(.*\)"/\1/p' $(CURDIR)/build.yaml) + +HOST_ARCH := $(shell arch) +BUILD_ARCH := ${DEB_HOST_MULTIARCH} +ifeq ($(HOST_ARCH),x86_64) + # Building AMD64 + DOTNETRUNTIME := debian-x64 + ifeq ($(BUILD_ARCH),arm-linux-gnueabihf) + # Cross-building ARM on AMD64 + DOTNETRUNTIME := debian-arm + endif + ifeq ($(BUILD_ARCH),aarch64-linux-gnu) + # Cross-building ARM on AMD64 + DOTNETRUNTIME := debian-arm64 + endif +endif +ifeq ($(HOST_ARCH),armv7l) + # Building ARM + DOTNETRUNTIME := debian-arm +endif +ifeq ($(HOST_ARCH),arm64) + # Building ARM + DOTNETRUNTIME := debian-arm64 +endif + +export DH_VERBOSE=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 + +%: + dh $@ + +# disable "make check" +override_dh_auto_test: + +# disable stripping debugging symbols +override_dh_clistrip: + +override_dh_auto_build: + echo $(WEB_VERSION) + # Clone down and build Web frontend + mkdir -p $(WEB_TARGET) + wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/v$(WEB_VERSION).tar.gz || wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz + mkdir -p $(CURDIR)/web + tar -xzf web-src.tgz -C $(CURDIR)/web/ --strip 1 + cd $(CURDIR)/web/ && npm install yarn + cd $(CURDIR)/web/ && node_modules/yarn/bin/yarn install + mv $(CURDIR)/web/dist/* $(WEB_TARGET)/ + # Build the application + dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ + "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server + +override_dh_auto_clean: + dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true + rm -f '$(CURDIR)/web-src.tgz' + rm -rf '$(CURDIR)/usr' + rm -rf '$(CURDIR)/web' + rm -rf '$(WEB_TARGET)' + +# Force the service name to jellyfin even if we're building jellyfin-nightly +override_dh_installinit: + dh_installinit --name=jellyfin diff --git a/debian/source.lintian-overrides b/debian/source.lintian-overrides new file mode 100644 index 0000000000..aeb332f13a --- /dev/null +++ b/debian/source.lintian-overrides @@ -0,0 +1,3 @@ +# This is an override for the following lintian errors: +jellyfin source: license-problem-md5sum-non-free-file Emby.Drawing/ImageMagick/fonts/webdings.ttf* +jellyfin source: source-is-missing diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000000..d3827e75a5 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +1.0 diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000000..17b5373d5e --- /dev/null +++ b/debian/source/options @@ -0,0 +1,11 @@ +tar-ignore='.git*' +tar-ignore='**/.git' +tar-ignore='**/.hg' +tar-ignore='**/.vs' +tar-ignore='**/.vscode' +tar-ignore='deployment' +tar-ignore='**/bin' +tar-ignore='**/obj' +tar-ignore='**/.nuget' +tar-ignore='*.deb' +tar-ignore='ThirdParty' From f9cecfc0fb0cbd7a75b8aace84f094a46824b705 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:01:47 -0400 Subject: [PATCH 027/411] Add new build.sh script and symlink --- build | 1 + build.sh | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 120000 build create mode 100755 build.sh diff --git a/build b/build new file mode 120000 index 0000000000..c07a74de4f --- /dev/null +++ b/build @@ -0,0 +1 @@ +build.sh \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..b61126e11f --- /dev/null +++ b/build.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# build.sh - Build Jellyfin binary packages +# Part of the Jellyfin Project + +set -o errexit +set -o pipefail + +usage() { + echo -e "build.sh - Build Jellyfin binary packages" + echo -e "Usage:" + echo -e " $0 -t/--type -p/--platform [-k/--keep-artifacts] [-l/--list-platforms]" + echo -e "Notes:" + echo -e " * BUILD_TYPE can be one of: [native, docker] and must be specified" + echo -e " * native: Build using the build script in the host OS" + echo -e " * docker: Build using the build script in a standardized Docker container" + echo -e " * PLATFORM can be any platform shown by -l/--list-platforms and must be specified" + echo -e " * If -k/--keep-artifacts is specified, transient artifacts (e.g. Docker containers) will be" + echo -e " retained after the build is finished" + echo -e " * If -l/--list-platforms is specified, all other arguments are ignored; the script will print" + echo -e " the list of supported platforms and exit" +} + +list_platforms() { + declare -a platforms + platforms=( + $( find deployment -maxdepth 1 -mindepth 1 -type f -name "build.*" | awk -F'.' '{ $1=""; print $2 "." $3 }' ) + ) + echo -e "Valid platforms:" + echo + for platform in ${platforms[@]}; do + echo -e "* ${platform} : $( grep '^#=' deployment/build.${platform} | sed 's/^#= //' )" + done +} + +do_build_native() { + export IS_DOCKER=NO + deployment/build.${PLATFORM} +} + +do_build_docker() { + if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then + echo "Missing Dockerfile for platform ${PLATFORM}" + exit 1 + fi + if [[ ${KEEP_ARTIFACTS} == YES ]]; then + docker_args="" + else + docker_args="--rm" + fi + + docker build . -t "jellyfin-builder.${PLATFORM}" -f deployment/Dockerfile.${PLATFORM} + mkdir -p ${ARTIFACT_DIR} + docker run $docker_args -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}" +} + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -t|--type) + BUILD_TYPE="$2" + shift # past argument + shift # past value + ;; + -p|--platform) + PLATFORM="$2" + shift # past argument + shift # past value + ;; + -k|--keep-artifacts) + KEEP_ARTIFACTS=YES + shift # past argument + ;; + -l|--list-platforms) + list_platforms + exit 0 + ;; + -h|--help) + usage + exit 0 + ;; + *) # unknown option + echo "Unknown option $1" + usage + exit 1 + ;; + esac +done + +if [[ -z ${BUILD_TYPE} || -z ${PLATFORM} ]]; then + usage + exit 1 +fi + +export SOURCE_DIR="$( pwd )" +export ARTIFACT_DIR="${SOURCE_DIR}/../bin/${PLATFORM}" + +# Determine build type +case ${BUILD_TYPE} in + native) + do_build_native + ;; + docker) + do_build_docker + ;; +esac From 3571afece104e39c676cfdb53df9392c225380d0 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:02:35 -0400 Subject: [PATCH 028/411] Ignore web artifacts --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 42243f01a8..17cf4a6277 100644 --- a/.gitignore +++ b/.gitignore @@ -271,3 +271,8 @@ dist # BenchmarkDotNet artifacts BenchmarkDotNet.Artifacts + +# Ignore web artifacts from native builds +web/ +web-src.* +MediaBrowser.WebDashboard/jellyfin-web/ From ba55ee4986fa871390e211c56fdec1b024ff617e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 22 Mar 2020 16:03:14 -0400 Subject: [PATCH 029/411] Add first proof-of-concept deployment setup --- deployment/Dockerfile.debian.amd64 | 34 ++++++++++++++++++++++++++++++ deployment/build.debian.amd64 | 26 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 deployment/Dockerfile.debian.amd64 create mode 100755 deployment/build.debian.amd64 diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 new file mode 100644 index 0000000000..eb29402194 --- /dev/null +++ b/deployment/Dockerfile.debian.amd64 @@ -0,0 +1,34 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.amd64 /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 new file mode 100755 index 0000000000..89f8445d80 --- /dev/null +++ b/deployment/build.debian.amd64 @@ -0,0 +1,26 @@ +#!/bin/bash + +#= Debian 9+ amd64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +dpkg-buildpackage -us -uc --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd From 93d1256a4c80c071b0c14e066c13bb9720b63dc9 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 14:46:16 -0400 Subject: [PATCH 030/411] Remove web building, rename, bump version --- debian/changelog | 6 ++++++ debian/control | 9 ++++----- debian/rules | 15 --------------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/debian/changelog b/debian/changelog index 51c4822370..35fb659571 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +jellyfin-server (10.6.0-1) unstable; urgency=medium + + * Forthcoming stable release + + -- Jellyfin Packaging Team Mon, 23 Mar 2020 14:46:05 -0400 + jellyfin (10.5.0-1) unstable; urgency=medium * New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 diff --git a/debian/control b/debian/control index 13fd3ecabb..f473dc41d2 100644 --- a/debian/control +++ b/debian/control @@ -1,4 +1,4 @@ -Source: jellyfin +Source: jellyfin-server Section: misc Priority: optional Maintainer: Jellyfin Team @@ -8,15 +8,13 @@ Build-Depends: debhelper (>= 9), libcurl4-openssl-dev, libfontconfig1-dev, libfreetype6-dev, - libssl-dev, - wget, - npm | nodejs + libssl-dev Standards-Version: 3.9.4 Homepage: https://jellyfin.media/ Vcs-Git: https://github.org/jellyfin/jellyfin.git Vcs-Browser: https://github.org/jellyfin/jellyfin -Package: jellyfin +Package: jellyfin-server Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server @@ -27,5 +25,6 @@ Depends: at, libfontconfig1, libfreetype6, libssl1.1 +Recommends: jellyfin-web Description: Jellyfin is a home media server. It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. diff --git a/debian/rules b/debian/rules index c2d57dfb22..2a5d41a696 100755 --- a/debian/rules +++ b/debian/rules @@ -2,8 +2,6 @@ CONFIG := Release TERM := xterm SHELL := /bin/bash -WEB_TARGET := $(CURDIR)/MediaBrowser.WebDashboard/jellyfin-web -WEB_VERSION := $(shell sed -n -e 's/^version: "\(.*\)"/\1/p' $(CURDIR)/build.yaml) HOST_ARCH := $(shell arch) BUILD_ARCH := ${DEB_HOST_MULTIARCH} @@ -41,25 +39,12 @@ override_dh_auto_test: override_dh_clistrip: override_dh_auto_build: - echo $(WEB_VERSION) - # Clone down and build Web frontend - mkdir -p $(WEB_TARGET) - wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/v$(WEB_VERSION).tar.gz || wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz - mkdir -p $(CURDIR)/web - tar -xzf web-src.tgz -C $(CURDIR)/web/ --strip 1 - cd $(CURDIR)/web/ && npm install yarn - cd $(CURDIR)/web/ && node_modules/yarn/bin/yarn install - mv $(CURDIR)/web/dist/* $(WEB_TARGET)/ - # Build the application dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server override_dh_auto_clean: dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true - rm -f '$(CURDIR)/web-src.tgz' rm -rf '$(CURDIR)/usr' - rm -rf '$(CURDIR)/web' - rm -rf '$(WEB_TARGET)' # Force the service name to jellyfin even if we're building jellyfin-nightly override_dh_installinit: From c61e95d11757656f9a71ec4b6d410c4c5936f516 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 14:49:55 -0400 Subject: [PATCH 031/411] Only support Docker builds on amd64 --- build.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.sh b/build.sh index b61126e11f..f318a0b1f6 100755 --- a/build.sh +++ b/build.sh @@ -39,6 +39,10 @@ do_build_native() { } do_build_docker() { + if ! dpkg --print-architecture | grep -q 'amd64'; then + echo "Docker-based builds only support amd64-based cross-building; use a native build instead" + exit 1 + fi if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then echo "Missing Dockerfile for platform ${PLATFORM}" exit 1 From 163cf223aa1b0b89c159b4d23c9ee9d888b96416 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:00:41 -0400 Subject: [PATCH 032/411] Only support cross-building with Docker --- build.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index f318a0b1f6..238871c168 100755 --- a/build.sh +++ b/build.sh @@ -34,13 +34,17 @@ list_platforms() { } do_build_native() { + if [[ $( dpkg --print-architecture | head -1 ) != "${PLATFORM##*.}" ]]; then + echo "Cross-building is not supported for native builds, use 'docker' builds on amd64 for cross-building." + exit 1 + fi export IS_DOCKER=NO deployment/build.${PLATFORM} } do_build_docker() { if ! dpkg --print-architecture | grep -q 'amd64'; then - echo "Docker-based builds only support amd64-based cross-building; use a native build instead" + echo "Docker-based builds only support amd64-based cross-building; use a 'native' build instead." exit 1 fi if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then From 9c378866e4bcc26315c3618cd7d91c96f90630d5 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:02:54 -0400 Subject: [PATCH 033/411] Add arm64 and armhf builds --- deployment/Dockerfile.debian.arm64 | 42 ++++++++++++++++++++++++++++++ deployment/Dockerfile.debian.armhf | 42 ++++++++++++++++++++++++++++++ deployment/build.debian.arm64 | 27 +++++++++++++++++++ deployment/build.debian.armhf | 27 +++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 deployment/Dockerfile.debian.arm64 create mode 100644 deployment/Dockerfile.debian.armhf create mode 100755 deployment/build.debian.arm64 create mode 100755 deployment/build.debian.armhf diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 new file mode 100644 index 0000000000..6e7e80d701 --- /dev/null +++ b/deployment/Dockerfile.debian.arm64 @@ -0,0 +1,42 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN dpkg --add-architecture arm64 \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 8 \ + && cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \ + && apt-get install -y gcc-8-source libstdc++-8-dev-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64 + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf new file mode 100644 index 0000000000..6e2e3a40cb --- /dev/null +++ b/deployment/Dockerfile.debian.armhf @@ -0,0 +1,42 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN dpkg --add-architecture armhf \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 8 \ + && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \ + && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 new file mode 100755 index 0000000000..3525ae471c --- /dev/null +++ b/deployment/build.debian.arm64 @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Debian 9+ arm64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf new file mode 100755 index 0000000000..45730eebef --- /dev/null +++ b/deployment/build.debian.armhf @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Debian 9+ arm64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd From 0365adb8233352c3261d669aeb83b8503100796b Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:24:13 -0400 Subject: [PATCH 034/411] Fix deps for armhf --- deployment/Dockerfile.debian.armhf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 6e2e3a40cb..8ebe1830ab 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -27,7 +27,7 @@ RUN dpkg --add-architecture armhf \ && apt-get install -y cross-gcc-dev \ && TARGET_LIST="armhf" cross-gcc-gensource 8 \ && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \ - && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf + && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip binutils-arm-linux-gnueabihf libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh From c4a29e537cf7f74d592a4b230627876f0bf0de37 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:28:57 -0400 Subject: [PATCH 035/411] Remove NPM install from Dockerfiles --- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index eb29402194..47c13fa712 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index 6e7e80d701..7b792d7e16 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 8ebe1830ab..d633d316a2 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current From f72c5b7a1d4db9b16f3b15cebe12bbca110bf7ef Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:40:19 -0400 Subject: [PATCH 036/411] Fix version output --- deployment/build.debian.amd64 | 2 +- deployment/build.debian.arm64 | 2 +- deployment/build.debian.armhf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 89f8445d80..c43585161d 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 9+ amd64 .deb +#= Debian 10+ amd64 .deb set -o errexit set -o xtrace diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 3525ae471c..4225c2f9df 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 9+ arm64 .deb +#= Debian 10+ arm64 .deb set -o errexit set -o xtrace diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index 45730eebef..f71a960410 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 9+ arm64 .deb +#= Debian 10+ arm64 .deb set -o errexit set -o xtrace From 9ce2af2a6c20c061bb1317728262bad305d05f27 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:40:51 -0400 Subject: [PATCH 037/411] Don't limit to files (allow symlinks) --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 238871c168..f54ce04ce7 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ usage() { list_platforms() { declare -a platforms platforms=( - $( find deployment -maxdepth 1 -mindepth 1 -type f -name "build.*" | awk -F'.' '{ $1=""; print $2 "." $3 }' ) + $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; print $2 "." $3 }' | sort ) ) echo -e "Valid platforms:" echo From 3e7a106a95a183ba4c7d1bf00d87e149463f0e23 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:40:59 -0400 Subject: [PATCH 038/411] Add Ubuntu configurations --- deployment/Dockerfile.ubuntu.amd64 | 34 +++++++++++++++++++ deployment/Dockerfile.ubuntu.arm64 | 53 ++++++++++++++++++++++++++++++ deployment/Dockerfile.ubuntu.armhf | 53 ++++++++++++++++++++++++++++++ deployment/build.ubuntu.amd64 | 26 +++++++++++++++ deployment/build.ubuntu.arm64 | 27 +++++++++++++++ deployment/build.ubuntu.armhf | 27 +++++++++++++++ 6 files changed, 220 insertions(+) create mode 100644 deployment/Dockerfile.ubuntu.amd64 create mode 100644 deployment/Dockerfile.ubuntu.arm64 create mode 100644 deployment/Dockerfile.ubuntu.armhf create mode 100755 deployment/build.ubuntu.amd64 create mode 100755 deployment/build.ubuntu.arm64 create mode 100755 deployment/build.ubuntu.armhf diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 new file mode 100644 index 0000000000..e1b0c3975d --- /dev/null +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -0,0 +1,34 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.amd64 /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 new file mode 100644 index 0000000000..98dfbf7dd6 --- /dev/null +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -0,0 +1,53 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=arm64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN rm /etc/apt/sources.list \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && dpkg --add-architecture arm64 \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 6 \ + && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64 + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.arm64 /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf new file mode 100644 index 0000000000..30cd861359 --- /dev/null +++ b/deployment/Dockerfile.ubuntu.armhf @@ -0,0 +1,53 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=armhf +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN rm /etc/apt/sources.list \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && dpkg --add-architecture armhf \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 6 \ + && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf + +# Link to build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh + +# Create the source dir +RUN mkdir -p ${SOURCE_DIR} + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 new file mode 100755 index 0000000000..e74db90c43 --- /dev/null +++ b/deployment/build.ubuntu.amd64 @@ -0,0 +1,26 @@ +#!/bin/bash + +#= Ubuntu 18.04+ amd64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +dpkg-buildpackage -us -uc --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 new file mode 100755 index 0000000000..1d91b303ad --- /dev/null +++ b/deployment/build.ubuntu.arm64 @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Ubuntu 18.04+ arm64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf new file mode 100755 index 0000000000..efdc2b65b1 --- /dev/null +++ b/deployment/build.ubuntu.armhf @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Ubuntu 18.04+ arm64 .deb + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +if [[ ${IS_DOCKER} == YES ]]; then + # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + sed -i '/dotnet-sdk-3.1,/d' debian/control +fi + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean + +mkdir -p ${ARTIFACT_DIR}/ +mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd From 8b1a76a32e5c2d8677fc6bba62682cfc1af748e6 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 15:44:23 -0400 Subject: [PATCH 039/411] Mount the source volume rather than copy it Now that the build script cleans up both before and after building, this is a viable option and will significant reduce build times by promoting container reuse (with `-k`). --- build.sh | 4 ++-- deployment/Dockerfile.debian.amd64 | 5 +---- deployment/Dockerfile.debian.arm64 | 5 +---- deployment/Dockerfile.debian.armhf | 5 +---- deployment/Dockerfile.ubuntu.amd64 | 5 +---- deployment/Dockerfile.ubuntu.arm64 | 5 +---- deployment/Dockerfile.ubuntu.armhf | 5 +---- 7 files changed, 8 insertions(+), 26 deletions(-) diff --git a/build.sh b/build.sh index f54ce04ce7..5d3f8ec713 100755 --- a/build.sh +++ b/build.sh @@ -16,7 +16,7 @@ usage() { echo -e " * docker: Build using the build script in a standardized Docker container" echo -e " * PLATFORM can be any platform shown by -l/--list-platforms and must be specified" echo -e " * If -k/--keep-artifacts is specified, transient artifacts (e.g. Docker containers) will be" - echo -e " retained after the build is finished" + echo -e " retained after the build is finished; the source directory will still be cleaned" echo -e " * If -l/--list-platforms is specified, all other arguments are ignored; the script will print" echo -e " the list of supported platforms and exit" } @@ -59,7 +59,7 @@ do_build_docker() { docker build . -t "jellyfin-builder.${PLATFORM}" -f deployment/Dockerfile.${PLATFORM} mkdir -p ${ARTIFACT_DIR} - docker run $docker_args -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}" + docker run $docker_args -v "${SOURCE_DIR}:/jellyfin" -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}" } while [[ $# -gt 0 ]]; do diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index 47c13fa712..b5a0380489 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -24,11 +24,8 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4 # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.amd64 /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index 7b792d7e16..cfe562df33 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -32,11 +32,8 @@ RUN dpkg --add-architecture arm64 \ # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index d633d316a2..ea8c8c8e62 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -32,11 +32,8 @@ RUN dpkg --add-architecture armhf \ # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index e1b0c3975d..e61be4efcc 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -24,11 +24,8 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4 # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.amd64 /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 98dfbf7dd6..e34ef7edd1 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -43,11 +43,8 @@ RUN rm /etc/apt/sources.list \ # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.arm64 /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 30cd861359..6f92c81ab1 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -43,11 +43,8 @@ RUN rm /etc/apt/sources.list \ # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh -# Create the source dir -RUN mkdir -p ${SOURCE_DIR} +VOLUME ${SOURCE_DIR}/ VOLUME ${ARTIFACT_DIR}/ -COPY . ${SOURCE_DIR}/ - ENTRYPOINT ["/build.sh"] From eb632e4a0dd5d83a1aea4c710caf1ea0f3ad6b0e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 16:01:25 -0400 Subject: [PATCH 040/411] Back up and restore control file --- deployment/build.debian.amd64 | 2 ++ deployment/build.debian.arm64 | 2 ++ deployment/build.debian.armhf | 2 ++ deployment/build.ubuntu.amd64 | 2 ++ deployment/build.ubuntu.arm64 | 2 ++ deployment/build.ubuntu.armhf | 2 ++ 6 files changed, 12 insertions(+) diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index c43585161d..0eb9ee5c83 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -20,6 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 4225c2f9df..d1ce85e2fa 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,6 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index f71a960410..3941583544 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,6 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index e74db90c43..86653cb384 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -20,6 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index 1d91b303ad..f065170092 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,6 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index efdc2b65b1..679fde5ae1 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -10,6 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually + cp debian/control debian/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,6 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then + mv debian/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi From 6028bc0f7915a09caea881462008561424a15829 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 16:28:49 -0400 Subject: [PATCH 041/411] Port Fedora and CentOS builds and remove web build Simplifies a number of aspects of the RPM build, including moving .copr/Makefile into the "fedora/" folder (and leaving a symlink), removing the jellyfin-web build components, and renaming it jellyfin-server like Debian did. --- .copr/Makefile | 60 +---------- deployment/Dockerfile.centos.amd64 | 32 ++++++ deployment/Dockerfile.fedora.amd64 | 33 ++++++ deployment/build.centos.amd64 | 24 +++++ deployment/build.fedora.amd64 | 24 +++++ fedora/.gitignore | 3 + fedora/Makefile | 29 ++++++ fedora/README.md | 43 ++++++++ fedora/jellyfin-firewalld.xml | 9 ++ fedora/jellyfin.env | 34 +++++++ fedora/jellyfin.override.conf | 7 ++ fedora/jellyfin.service | 15 +++ fedora/jellyfin.spec | 158 +++++++++++++++++++++++++++++ fedora/jellyfin.sudoers | 19 ++++ fedora/restart.sh | 36 +++++++ 15 files changed, 467 insertions(+), 59 deletions(-) mode change 100644 => 120000 .copr/Makefile create mode 100644 deployment/Dockerfile.centos.amd64 create mode 100644 deployment/Dockerfile.fedora.amd64 create mode 100755 deployment/build.centos.amd64 create mode 100755 deployment/build.fedora.amd64 create mode 100644 fedora/.gitignore create mode 100644 fedora/Makefile create mode 100644 fedora/README.md create mode 100644 fedora/jellyfin-firewalld.xml create mode 100644 fedora/jellyfin.env create mode 100644 fedora/jellyfin.override.conf create mode 100644 fedora/jellyfin.service create mode 100644 fedora/jellyfin.spec create mode 100644 fedora/jellyfin.sudoers create mode 100755 fedora/restart.sh diff --git a/.copr/Makefile b/.copr/Makefile deleted file mode 100644 index ba330ada95..0000000000 --- a/.copr/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -VERSION := $(shell sed -ne '/^Version:/s/.* *//p' \ - deployment/fedora-package-x64/pkg-src/jellyfin.spec) - -deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz: - curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \ - https://github.com/jellyfin/jellyfin-web/archive/v$(VERSION).tar.gz \ - || curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \ - https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz \ - -srpm: deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz - cd deployment/fedora-package-x64; \ - SOURCE_DIR=../.. \ - WORKDIR="$${PWD}"; \ - package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \ - pkg_src_dir="$${WORKDIR}/pkg-src"; \ - GNU_TAR=1; \ - tar \ - --transform "s,^\.,jellyfin-$(VERSION)," \ - --exclude='.git*' \ - --exclude='**/.git' \ - --exclude='**/.hg' \ - --exclude='**/.vs' \ - --exclude='**/.vscode' \ - --exclude='deployment' \ - --exclude='**/bin' \ - --exclude='**/obj' \ - --exclude='**/.nuget' \ - --exclude='*.deb' \ - --exclude='*.rpm' \ - -czf "pkg-src/jellyfin-$(VERSION).tar.gz" \ - -C $${SOURCE_DIR} ./ || GNU_TAR=0; \ - if [ $${GNU_TAR} -eq 0 ]; then \ - package_temporary_dir="$$(mktemp -d)"; \ - mkdir -p "$${package_temporary_dir}/jellyfin"; \ - tar \ - --exclude='.git*' \ - --exclude='**/.git' \ - --exclude='**/.hg' \ - --exclude='**/.vs' \ - --exclude='**/.vscode' \ - --exclude='deployment' \ - --exclude='**/bin' \ - --exclude='**/obj' \ - --exclude='**/.nuget' \ - --exclude='*.deb' \ - --exclude='*.rpm' \ - -czf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \ - -C $${SOURCE_DIR} ./; \ - mkdir -p "$${package_temporary_dir}/jellyfin-$(VERSION)"; \ - tar -xzf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \ - -C "$${package_temporary_dir}/jellyfin-$(VERSION); \ - rm -f "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz"; \ - tar -czf "$${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-$(VERSION).tar.gz" \ - -C "$${package_temporary_dir}" "jellyfin-$(VERSION); \ - rm -rf $${package_temporary_dir}; \ - fi; \ - rpmbuild -bs pkg-src/jellyfin.spec \ - --define "_sourcedir $$PWD/pkg-src/" \ - --define "_srcrpmdir $(outdir)" diff --git a/.copr/Makefile b/.copr/Makefile new file mode 120000 index 0000000000..ec3c90dfd9 --- /dev/null +++ b/.copr/Makefile @@ -0,0 +1 @@ +../fedora/Makefile \ No newline at end of file diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 new file mode 100644 index 0000000000..39788cc0ee --- /dev/null +++ b/deployment/Dockerfile.centos.amd64 @@ -0,0 +1,32 @@ +FROM centos:7 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV IS_DOCKER=YES + +# Prepare CentOS environment +RUN yum update -y \ + && yum install -y epel-release \ + && yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git + +# Install DotNET SDK +RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \ + && rpmdev-setuptree \ + && yum install -y dotnet-sdk-${SDK_VERSION} + +# Create symlinks and directories +RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \ + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 new file mode 100644 index 0000000000..73148763de --- /dev/null +++ b/deployment/Dockerfile.fedora.amd64 @@ -0,0 +1,33 @@ +FROM fedora:31 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV IS_DOCKER=YES + +# Prepare Fedora environment +RUN dnf update -y + +# Install build dependencies +RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel + +# Install DotNET SDK +RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ + && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ + && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} + +# Create symlinks and directories +RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \ + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64 new file mode 100755 index 0000000000..939bbc45a4 --- /dev/null +++ b/deployment/build.centos.amd64 @@ -0,0 +1,24 @@ +#!/bin/bash + +#= CentOS/RHEL 7+ amd64 .rpm + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Build RPM +make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +rm -f fedora/jellyfin*.tar.gz + +popd diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64 new file mode 100755 index 0000000000..8ac99decc1 --- /dev/null +++ b/deployment/build.fedora.amd64 @@ -0,0 +1,24 @@ +#!/bin/bash + +#= Fedora 29+ amd64 .rpm + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Build RPM +make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +rm -f fedora/jellyfin*.tar.gz + +popd diff --git a/fedora/.gitignore b/fedora/.gitignore new file mode 100644 index 0000000000..6019b98c22 --- /dev/null +++ b/fedora/.gitignore @@ -0,0 +1,3 @@ +*.rpm +*.zip +*.tar.gz \ No newline at end of file diff --git a/fedora/Makefile b/fedora/Makefile new file mode 100644 index 0000000000..1d2709a2fe --- /dev/null +++ b/fedora/Makefile @@ -0,0 +1,29 @@ +VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin.spec) + +srpm: + cd fedora/; \ + SOURCE_DIR=.. \ + WORKDIR="$${PWD}"; \ + package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \ + pkg_src_dir="$${WORKDIR}"; \ + GNU_TAR=1; \ + tar \ + --transform "s,^\.,jellyfin-server-$(VERSION)," \ + --exclude='.git*' \ + --exclude='**/.git' \ + --exclude='**/.hg' \ + --exclude='**/.vs' \ + --exclude='**/.vscode' \ + --exclude='deployment' \ + --exclude='**/bin' \ + --exclude='**/obj' \ + --exclude='**/.nuget' \ + --exclude='*.deb' \ + --exclude='*.rpm' \ + --exclude='jellyfin-server-$(VERSION).tar.gz' \ + -czf "jellyfin-server-$(VERSION).tar.gz" \ + -C $${SOURCE_DIR} ./ + cd fedora/; \ + rpmbuild -bs jellyfin.spec \ + --define "_sourcedir $$PWD/" \ + --define "_srcrpmdir $(outdir)" diff --git a/fedora/README.md b/fedora/README.md new file mode 100644 index 0000000000..7ed6f7efc6 --- /dev/null +++ b/fedora/README.md @@ -0,0 +1,43 @@ +# Jellyfin RPM + +## Build Fedora Package with docker + +Change into this directory `cd rpm-package` +Run the build script `./build-fedora-rpm.sh`. +Resulting RPM and src.rpm will be in `../../jellyfin-*.rpm` + +## ffmpeg + +The RPM package for Fedora/CentOS requires some additional repositories as ffmpeg is not in the main repositories. + +```shell +# ffmpeg from RPMfusion free +# Fedora +$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm +# CentOS 7 +$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm +``` + +## ISO mounting + +To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers` +``` +# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount +# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount +``` + +## Building with dotnet + +Jellyfin is build with `--self-contained` so no dotnet required for runtime. + +```shell +# dotnet required for building the RPM +# Fedora +$ sudo dnf copr enable @dotnet-sig/dotnet +# CentOS +$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm +``` + +## TODO + +- [ ] OpenSUSE \ No newline at end of file diff --git a/fedora/jellyfin-firewalld.xml b/fedora/jellyfin-firewalld.xml new file mode 100644 index 0000000000..538c5d65f8 --- /dev/null +++ b/fedora/jellyfin-firewalld.xml @@ -0,0 +1,9 @@ + + + Jellyfin + The Free Software Media System. + + + + + diff --git a/fedora/jellyfin.env b/fedora/jellyfin.env new file mode 100644 index 0000000000..de48f13af5 --- /dev/null +++ b/fedora/jellyfin.env @@ -0,0 +1,34 @@ +# Jellyfin default configuration options + +# Use this file to override the default configurations; add additional +# options with JELLYFIN_ADD_OPTS. + +# To override the user or this config file's location, use +# /etc/systemd/system/jellyfin.service.d/override.conf + +# +# This is a POSIX shell fragment +# + +# +# General options +# + +# Program directories +JELLYFIN_DATA_DIR="/var/lib/jellyfin" +JELLYFIN_CONFIG_DIR="/etc/jellyfin" +JELLYFIN_LOG_DIR="/var/log/jellyfin" +JELLYFIN_CACHE_DIR="/var/cache/jellyfin" + +# In-App service control +JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" + +# [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values +#JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg" + +# [OPTIONAL] run Jellyfin as a headless service +#JELLYFIN_SERVICE_OPT="--service" + +# [OPTIONAL] run Jellyfin without the web app +#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" + diff --git a/fedora/jellyfin.override.conf b/fedora/jellyfin.override.conf new file mode 100644 index 0000000000..8652450bb4 --- /dev/null +++ b/fedora/jellyfin.override.conf @@ -0,0 +1,7 @@ +# Jellyfin systemd configuration options + +# Use this file to override the user or environment file location. + +[Service] +#User = jellyfin +#EnvironmentFile = /etc/sysconfig/jellyfin diff --git a/fedora/jellyfin.service b/fedora/jellyfin.service new file mode 100644 index 0000000000..f3dc594b1c --- /dev/null +++ b/fedora/jellyfin.service @@ -0,0 +1,15 @@ +[Unit] +After=network.target +Description=Jellyfin is a free software media system that puts you in control of managing and streaming your media. + +[Service] +EnvironmentFile=/etc/sysconfig/jellyfin +WorkingDirectory=/var/lib/jellyfin +ExecStart=/usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +TimeoutSec=15 +Restart=on-failure +User=jellyfin +Group=jellyfin + +[Install] +WantedBy=multi-user.target diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec new file mode 100644 index 0000000000..e6a3170b56 --- /dev/null +++ b/fedora/jellyfin.spec @@ -0,0 +1,158 @@ +%global debug_package %{nil} +# Set the dotnet runtime +%if 0%{?fedora} +%global dotnet_runtime fedora-x64 +%else +%global dotnet_runtime centos-x64 +%endif + +Name: jellyfin-server +Version: 10.6.0 +Release: 1%{?dist} +Summary: The Free Software Media Browser +License: GPLv2 +URL: https://jellyfin.media +# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` +Source0: jellyfin-server-%{version}.tar.gz +Source11: jellyfin.service +Source12: jellyfin.env +Source13: jellyfin.sudoers +Source14: restart.sh +Source15: jellyfin.override.conf +Source16: jellyfin-firewalld.xml + +%{?systemd_requires} +BuildRequires: systemd +Requires(pre): shadow-utils +BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel +Requires: libcurl, fontconfig, freetype, openssl, glibc libicu +# Requirements not packaged in main repos +# COPR @dotnet-sig/dotnet or +# https://packages.microsoft.com/rhel/7/prod/ +BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 +# RPMfusion free +Requires: ffmpeg + +# Disable Automatic Dependency Processing +AutoReqProv: no + +%description +Jellyfin is a free software media system that puts you in control of managing and streaming your media. + + +%prep +%autosetup -n jellyfin-server-%{version} -b 0 + +%build + +%install +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \ + "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server +%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE +%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf +%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json +%{__mkdir} -p %{buildroot}%{_bindir} +tee %{buildroot}%{_bindir}/jellyfin << EOF +#!/bin/sh +exec %{_libdir}/jellyfin/jellyfin \${@} +EOF +%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin +%{__mkdir} -p %{buildroot}%{_sysconfdir}/jellyfin +%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin +%{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin + +%{__install} -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/jellyfin.service +%{__install} -D -m 0644 %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/jellyfin +%{__install} -D -m 0600 %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/jellyfin-sudoers +%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh +%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml + +%files +%attr(755,root,root) %{_bindir}/jellyfin +%{_libdir}/jellyfin/*.json +%{_libdir}/jellyfin/*.dll +%{_libdir}/jellyfin/*.so +%{_libdir}/jellyfin/*.a +%{_libdir}/jellyfin/createdump +# Needs 755 else only root can run it since binary build by dotnet is 722 +%attr(755,root,root) %{_libdir}/jellyfin/jellyfin +%{_libdir}/jellyfin/SOS_README.md +%{_unitdir}/jellyfin.service +%{_libexecdir}/jellyfin/restart.sh +%{_prefix}/lib/firewalld/services/jellyfin.xml +%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/jellyfin +%config %{_sysconfdir}/sysconfig/jellyfin +%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/jellyfin-sudoers +%config(noreplace) %{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf +%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/jellyfin/logging.json +%attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin +%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin +%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin +%{_datadir}/licenses/jellyfin/LICENSE + +%pre +getent group jellyfin >/dev/null || groupadd -r jellyfin +getent passwd jellyfin >/dev/null || \ + useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \ + -c "Jellyfin default user" jellyfin +exit 0 + +%post +# Move existing configuration cache and logs to their new locations and symlink them. +if [ $1 -gt 1 ] ; then + service_state=$(systemctl is-active jellyfin.service) + if [ "${service_state}" = "active" ]; then + systemctl stop jellyfin.service + fi + if [ ! -L %{_sharedstatedir}/jellyfin/config ]; then + mv %{_sharedstatedir}/jellyfin/config/* %{_sysconfdir}/jellyfin/ + rmdir %{_sharedstatedir}/jellyfin/config + ln -sf %{_sysconfdir}/jellyfin %{_sharedstatedir}/jellyfin/config + fi + if [ ! -L %{_sharedstatedir}/jellyfin/logs ]; then + mv %{_sharedstatedir}/jellyfin/logs/* %{_var}/log/jellyfin + rmdir %{_sharedstatedir}/jellyfin/logs + ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/jellyfin/logs + fi + if [ ! -L %{_sharedstatedir}/jellyfin/cache ]; then + mv %{_sharedstatedir}/jellyfin/cache/* %{_var}/cache/jellyfin + rmdir %{_sharedstatedir}/jellyfin/cache + ln -sf %{_var}/cache/jellyfin %{_sharedstatedir}/jellyfin/cache + fi + if [ "${service_state}" = "active" ]; then + systemctl start jellyfin.service + fi +fi +%systemd_post jellyfin.service + +%preun +%systemd_preun jellyfin.service + +%postun +%systemd_postun_with_restart jellyfin.service + +%changelog +* Mon Mar 23 2020 Jellyfin Packaging Team +- Forthcoming stable release +* Fri Oct 11 2019 Jellyfin Packaging Team +- New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 +* Sat Aug 31 2019 Jellyfin Packaging Team +- New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 +* Wed Jul 24 2019 Jellyfin Packaging Team +- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 +* Sat Jul 06 2019 Jellyfin Packaging Team +- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 +* Sun Jun 09 2019 Jellyfin Packaging Team +- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 +* Thu Jun 06 2019 Jellyfin Packaging Team +- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 +* Fri May 17 2019 Jellyfin Packaging Team +- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 +* Tue Apr 30 2019 Jellyfin Packaging Team +- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 +* Sat Apr 20 2019 Jellyfin Packaging Team +- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 +* Fri Apr 19 2019 Jellyfin Packaging Team +- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 diff --git a/fedora/jellyfin.sudoers b/fedora/jellyfin.sudoers new file mode 100644 index 0000000000..dd245af4b8 --- /dev/null +++ b/fedora/jellyfin.sudoers @@ -0,0 +1,19 @@ +# Allow jellyfin group to start, stop and restart itself +Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin +Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin +Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin + + +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD + +Defaults!RESTARTSERVER_SYSTEMD !requiretty +Defaults!STARTSERVER_SYSTEMD !requiretty +Defaults!STOPSERVER_SYSTEMD !requiretty + +# Allow the server to mount iso images +jellyfin ALL=(ALL) NOPASSWD: /bin/mount +jellyfin ALL=(ALL) NOPASSWD: /bin/umount + +Defaults:jellyfin !requiretty diff --git a/fedora/restart.sh b/fedora/restart.sh new file mode 100755 index 0000000000..9e53efecd0 --- /dev/null +++ b/fedora/restart.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# restart.sh - Jellyfin server restart script +# Part of the Jellyfin project (https://github.com/jellyfin) +# +# This script restarts the Jellyfin daemon on Linux when using +# the Restart button on the admin dashboard. It supports the +# systemctl, service, and traditional /etc/init.d (sysv) restart +# methods, chosen automatically by which one is found first (in +# that order). +# +# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. + +get_service_command() { + for command in systemctl service; do + if which $command &>/dev/null; then + echo $command && return + fi + done + echo "sysv" +} + +cmd="$( get_service_command )" +echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." +case $cmd in + 'systemctl') + echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now + ;; + 'service') + echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now + ;; + 'sysv') + echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now + ;; +esac +exit 0 From cf6dc609b72fd9b388c987ae38c0a8bf95168d15 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 17:54:17 -0400 Subject: [PATCH 042/411] Fix up single-segment platform names --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 5d3f8ec713..8256f9ea31 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ usage() { list_platforms() { declare -a platforms platforms=( - $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; print $2 "." $3 }' | sort ) + $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; } print ""; }' | sort ) ) echo -e "Valid platforms:" echo From 8e0a33c1aadabd91765594e8d9d5c3a53933b26d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:01:28 -0400 Subject: [PATCH 043/411] Handle single- or triple-part platform names --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 8256f9ea31..86c8447933 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ usage() { list_platforms() { declare -a platforms platforms=( - $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; } print ""; }' | sort ) + $( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; }; if ($4 != ""){ printf "." $4; }; print ""; }' | sort ) ) echo -e "Valid platforms:" echo From ab8de37080a985c742694722f311d719ec64bdc8 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:01:42 -0400 Subject: [PATCH 044/411] Add .tar.gz-based builds --- deployment/Dockerfile.linux.amd64 | 31 +++++++++++++++++++++++++++++++ deployment/Dockerfile.macos.amd64 | 31 +++++++++++++++++++++++++++++++ deployment/Dockerfile.portable | 30 ++++++++++++++++++++++++++++++ deployment/build.linux.amd64 | 27 +++++++++++++++++++++++++++ deployment/build.macos.amd64 | 27 +++++++++++++++++++++++++++ deployment/build.portable | 27 +++++++++++++++++++++++++++ 6 files changed, 173 insertions(+) create mode 100644 deployment/Dockerfile.linux.amd64 create mode 100644 deployment/Dockerfile.macos.amd64 create mode 100644 deployment/Dockerfile.portable create mode 100755 deployment/build.linux.amd64 create mode 100755 deployment/build.macos.amd64 create mode 100755 deployment/build.portable diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 new file mode 100644 index 0000000000..d8bec92145 --- /dev/null +++ b/deployment/Dockerfile.linux.amd64 @@ -0,0 +1,31 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.amd64 /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.macos.amd64 b/deployment/Dockerfile.macos.amd64 new file mode 100644 index 0000000000..aaf9a9692f --- /dev/null +++ b/deployment/Dockerfile.macos.amd64 @@ -0,0 +1,31 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.amd64 /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable new file mode 100644 index 0000000000..2893e140df --- /dev/null +++ b/deployment/Dockerfile.portable @@ -0,0 +1,30 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64 new file mode 100755 index 0000000000..0cbbd05cf9 --- /dev/null +++ b/deployment/build.linux.amd64 @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Generic Linux amd64 .tar.gz + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version} +rm -rf dist/jellyfin-server_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.macos.amd64 b/deployment/build.macos.amd64 new file mode 100755 index 0000000000..4dca2b6438 --- /dev/null +++ b/deployment/build.macos.amd64 @@ -0,0 +1,27 @@ +#!/bin/bash + +#= MacOS 10.13+ amd64 .tar.gz + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version} +rm -rf dist/jellyfin-server_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/build.portable b/deployment/build.portable new file mode 100755 index 0000000000..1e8a4ab623 --- /dev/null +++ b/deployment/build.portable @@ -0,0 +1,27 @@ +#!/bin/bash + +#= Portable .NET DLL .tar.gz + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version} +rm -rf dist/jellyfin-server_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd From 0242ce5fee689442472505be94000233c464ffbd Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:10:34 -0400 Subject: [PATCH 045/411] Add Windows build --- build.yaml | 25 +++++---- deployment/Dockerfile.windows.amd64 | 30 +++++++++++ deployment/build.windows.amd64 | 54 +++++++++++++++++++ .../windows => windows}/build-jellyfin.ps1 | 0 .../old/windows => windows}/dependencies.txt | 0 .../dialogs/confirmation.nsddef | 0 .../dialogs/confirmation.nsdinc | 0 .../dialogs/service-config.nsddef | 0 .../dialogs/service-config.nsdinc | 0 .../dialogs/setuptype.nsddef | 0 .../dialogs/setuptype.nsdinc | 0 .../windows => windows}/helpers/ShowError.nsh | 0 .../windows => windows}/helpers/StrSlash.nsh | 0 .../old/windows => windows}/jellyfin.nsi | 0 .../legacy/install-jellyfin.ps1 | 0 .../windows => windows}/legacy/install.bat | 0 16 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 deployment/Dockerfile.windows.amd64 create mode 100755 deployment/build.windows.amd64 rename {deployment/old/windows => windows}/build-jellyfin.ps1 (100%) rename {deployment/old/windows => windows}/dependencies.txt (100%) rename {deployment/old/windows => windows}/dialogs/confirmation.nsddef (100%) rename {deployment/old/windows => windows}/dialogs/confirmation.nsdinc (100%) rename {deployment/old/windows => windows}/dialogs/service-config.nsddef (100%) rename {deployment/old/windows => windows}/dialogs/service-config.nsdinc (100%) rename {deployment/old/windows => windows}/dialogs/setuptype.nsddef (100%) rename {deployment/old/windows => windows}/dialogs/setuptype.nsdinc (100%) rename {deployment/old/windows => windows}/helpers/ShowError.nsh (100%) rename {deployment/old/windows => windows}/helpers/StrSlash.nsh (100%) rename {deployment/old/windows => windows}/jellyfin.nsi (100%) rename {deployment/old/windows => windows}/legacy/install-jellyfin.ps1 (100%) rename {deployment/old/windows => windows}/legacy/install.bat (100%) diff --git a/build.yaml b/build.yaml index 123f77fb89..a76539d1f3 100644 --- a/build.yaml +++ b/build.yaml @@ -1,18 +1,17 @@ --- # We just wrap `build` so this is really it name: "jellyfin" -version: "10.5.0" +version: "10.6.0" packages: - - debian-package-x64 - - debian-package-armhf - - debian-package-arm64 - - ubuntu-package-x64 - - ubuntu-package-armhf - - ubuntu-package-arm64 - - fedora-package-x64 - - centos-package-x64 - - linux-x64 - - macos + - debian.amd64 + - debian.arm64 + - debian.armhf + - ubuntu.amd64 + - ubuntu.arm64 + - ubuntu.armhf + - fedora.amd64 + - centos.amd64 + - linux.amd64 + - macos.amd64 + - windows.amd64 - portable - - win-x64 - - win-x86 diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 new file mode 100644 index 0000000000..0397a023e2 --- /dev/null +++ b/deployment/Dockerfile.windows.amd64 @@ -0,0 +1,30 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV IS_DOCKER=YES + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${SOURCE_DIR}/deployment/build.windows.amd64 /build.sh + +VOLUME ${SOURCE_DIR}/ + +VOLUME ${ARTIFACT_DIR}/ + +ENTRYPOINT ["/build.sh"] diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64 new file mode 100755 index 0000000000..39bd41f990 --- /dev/null +++ b/deployment/build.windows.amd64 @@ -0,0 +1,54 @@ +#!/bin/bash + +#= Windows 7+ amd64 (x64) .zip + +set -o errexit +set -o xtrace + +# Version variables +NSSM_VERSION="nssm-2.24-101-g897c7ad" +NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" +FFMPEG_VERSION="ffmpeg-4.2.1-win64-static" +FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip" + +# Move to source directory +pushd ${SOURCE_DIR} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +output_dir="dist/jellyfin-server_${version}" + +# Build binary +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" + +# Prepare addins +addin_build_dir="$( mktemp -d )" +wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip +wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip +unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe ${output_dir}/nssm.exe +unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe ${output_dir}/ffmpeg.exe +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe ${output_dir}/ffprobe.exe +rm -rf ${addin_build_dir} + +# Prepare scripts +cp ${SOURCE_DIR}/windows/legacy/install-jellyfin.ps1 ${output_dir}/install-jellyfin.ps1 +cp ${SOURCE_DIR}/windows/legacy/install.bat ${output_dir}/install.bat + +# Create zip package +pushd dist +zip -qr jellyfin-server_${version}.portable.zip jellyfin-server_${version} +popd +rm -rf ${output_dir} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv dist/jellyfin[-_]*.zip ${ARTIFACT_DIR}/ + +if [[ ${IS_DOCKER} == YES ]]; then + chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} +fi + +popd diff --git a/deployment/old/windows/build-jellyfin.ps1 b/windows/build-jellyfin.ps1 similarity index 100% rename from deployment/old/windows/build-jellyfin.ps1 rename to windows/build-jellyfin.ps1 diff --git a/deployment/old/windows/dependencies.txt b/windows/dependencies.txt similarity index 100% rename from deployment/old/windows/dependencies.txt rename to windows/dependencies.txt diff --git a/deployment/old/windows/dialogs/confirmation.nsddef b/windows/dialogs/confirmation.nsddef similarity index 100% rename from deployment/old/windows/dialogs/confirmation.nsddef rename to windows/dialogs/confirmation.nsddef diff --git a/deployment/old/windows/dialogs/confirmation.nsdinc b/windows/dialogs/confirmation.nsdinc similarity index 100% rename from deployment/old/windows/dialogs/confirmation.nsdinc rename to windows/dialogs/confirmation.nsdinc diff --git a/deployment/old/windows/dialogs/service-config.nsddef b/windows/dialogs/service-config.nsddef similarity index 100% rename from deployment/old/windows/dialogs/service-config.nsddef rename to windows/dialogs/service-config.nsddef diff --git a/deployment/old/windows/dialogs/service-config.nsdinc b/windows/dialogs/service-config.nsdinc similarity index 100% rename from deployment/old/windows/dialogs/service-config.nsdinc rename to windows/dialogs/service-config.nsdinc diff --git a/deployment/old/windows/dialogs/setuptype.nsddef b/windows/dialogs/setuptype.nsddef similarity index 100% rename from deployment/old/windows/dialogs/setuptype.nsddef rename to windows/dialogs/setuptype.nsddef diff --git a/deployment/old/windows/dialogs/setuptype.nsdinc b/windows/dialogs/setuptype.nsdinc similarity index 100% rename from deployment/old/windows/dialogs/setuptype.nsdinc rename to windows/dialogs/setuptype.nsdinc diff --git a/deployment/old/windows/helpers/ShowError.nsh b/windows/helpers/ShowError.nsh similarity index 100% rename from deployment/old/windows/helpers/ShowError.nsh rename to windows/helpers/ShowError.nsh diff --git a/deployment/old/windows/helpers/StrSlash.nsh b/windows/helpers/StrSlash.nsh similarity index 100% rename from deployment/old/windows/helpers/StrSlash.nsh rename to windows/helpers/StrSlash.nsh diff --git a/deployment/old/windows/jellyfin.nsi b/windows/jellyfin.nsi similarity index 100% rename from deployment/old/windows/jellyfin.nsi rename to windows/jellyfin.nsi diff --git a/deployment/old/windows/legacy/install-jellyfin.ps1 b/windows/legacy/install-jellyfin.ps1 similarity index 100% rename from deployment/old/windows/legacy/install-jellyfin.ps1 rename to windows/legacy/install-jellyfin.ps1 diff --git a/deployment/old/windows/legacy/install.bat b/windows/legacy/install.bat similarity index 100% rename from deployment/old/windows/legacy/install.bat rename to windows/legacy/install.bat From 9169861baab62919e661ec663998c5a4436e4547 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:18:38 -0400 Subject: [PATCH 046/411] Add CODEOWNERS for GitHub --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..e902dc7124 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Joshua must review all changes to deployment and build.sh +deployment/* @joshuaboniface +build.sh @joshuaboniface From de66ab4d832664abb5c17005d326e543446aae7d Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:39:11 -0400 Subject: [PATCH 047/411] Use git checkout instead of file copies to clean --- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/build.debian.amd64 | 2 +- deployment/build.debian.arm64 | 2 +- deployment/build.debian.armhf | 2 +- deployment/build.ubuntu.amd64 | 2 +- deployment/build.ubuntu.arm64 | 2 +- deployment/build.ubuntu.armhf | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index b5a0380489..ea2aba5a89 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index cfe562df33..db8485b43d 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index ea8c8c8e62..717bc3c894 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index e61be4efcc..7c2b26dedd 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index e34ef7edd1..27f8ec11b5 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 6f92c81ab1..c888c42df6 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 0eb9ee5c83..8e205867b8 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -21,7 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index d1ce85e2fa..436602d195 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index 3941583544..9ca57b1e46 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 86653cb384..2b789cc475 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -21,7 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index f065170092..9b4e548501 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 679fde5ae1..59912a14f5 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - mv debian/control.orig debian/control + git checkout debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi From b95bd0e67874f925918eb912d445fb202e4fcee0 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:42:09 -0400 Subject: [PATCH 048/411] Bump shared_version to 10.6.0 too --- SharedVersion.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index d741f379d2..6981c1ca9d 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,4 +1,4 @@ using System.Reflection; -[assembly: AssemblyVersion("10.5.0")] -[assembly: AssemblyFileVersion("10.5.0")] +[assembly: AssemblyVersion("10.6.0")] +[assembly: AssemblyFileVersion("10.6.0")] From a561d4ca417b45e983bfe46a70397fb44f7b78a3 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 18:43:54 -0400 Subject: [PATCH 049/411] Remove arch from macos --- build.yaml | 2 +- deployment/{Dockerfile.macos.amd64 => Dockerfile.macos} | 2 +- deployment/{build.macos.amd64 => build.macos} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename deployment/{Dockerfile.macos.amd64 => Dockerfile.macos} (94%) rename deployment/{build.macos.amd64 => build.macos} (96%) diff --git a/build.yaml b/build.yaml index a76539d1f3..9e590e5a01 100644 --- a/build.yaml +++ b/build.yaml @@ -12,6 +12,6 @@ packages: - fedora.amd64 - centos.amd64 - linux.amd64 - - macos.amd64 - windows.amd64 + - macos - portable diff --git a/deployment/Dockerfile.macos.amd64 b/deployment/Dockerfile.macos similarity index 94% rename from deployment/Dockerfile.macos.amd64 rename to deployment/Dockerfile.macos index aaf9a9692f..ba5da40190 100644 --- a/deployment/Dockerfile.macos.amd64 +++ b/deployment/Dockerfile.macos @@ -22,7 +22,7 @@ RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4 && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet # Link to docker-build script -RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.amd64 /build.sh +RUN ln -sf ${SOURCE_DIR}/deployment/build.macos /build.sh VOLUME ${SOURCE_DIR}/ diff --git a/deployment/build.macos.amd64 b/deployment/build.macos similarity index 96% rename from deployment/build.macos.amd64 rename to deployment/build.macos index 4dca2b6438..16be29eeef 100755 --- a/deployment/build.macos.amd64 +++ b/deployment/build.macos @@ -1,6 +1,6 @@ #!/bin/bash -#= MacOS 10.13+ amd64 .tar.gz +#= MacOS 10.13+ .tar.gz set -o errexit set -o xtrace From c478a43fd56993062622c3252be938b01e80854c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 21:44:33 -0400 Subject: [PATCH 050/411] Update package description for Debian --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index f473dc41d2..648e28ae81 100644 --- a/debian/control +++ b/debian/control @@ -26,5 +26,5 @@ Depends: at, libfreetype6, libssl1.1 Recommends: jellyfin-web -Description: Jellyfin is a home media server. - It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. +Description: Jellyfin is the Free Software Media System. + This package provides the Jellyfin server backend and API. From e87a10235b960bf28a6a76da762b62a0f0399e2c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 21:49:07 -0400 Subject: [PATCH 051/411] Go back to cp-based control archive but right --- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/build.debian.amd64 | 4 ++-- deployment/build.debian.arm64 | 4 ++-- deployment/build.debian.armhf | 4 ++-- deployment/build.ubuntu.amd64 | 4 ++-- deployment/build.ubuntu.arm64 | 4 ++-- deployment/build.ubuntu.armhf | 4 ++-- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index ea2aba5a89..b5a0380489 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index db8485b43d..cfe562df33 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 717bc3c894..ea8c8c8e62 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 7c2b26dedd..e61be4efcc 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 27f8ec11b5..e34ef7edd1 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index c888c42df6..6f92c81ab1 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -12,7 +12,7 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv git + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 8e205867b8..beaf02bee2 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,7 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 436602d195..6394dddb02 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index 9ca57b1e46..d12660760f 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 2b789cc475..1b90f68f46 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -21,7 +21,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index 9b4e548501..c0a31d764f 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 59912a14f5..e2129357dd 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -10,7 +10,7 @@ pushd ${SOURCE_DIR} if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-3.1, since it's installed manually - cp debian/control debian/control.orig + cp -a debian/control /tmp/control.orig sed -i '/dotnet-sdk-3.1,/d' debian/control fi @@ -22,7 +22,7 @@ mkdir -p ${ARTIFACT_DIR}/ mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then - git checkout debian/control + cp -a /tmp/control.orig debian/control chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} fi From be9eb0f19ed207cabaad48c5b713ac80d9d77397 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 22:51:12 -0400 Subject: [PATCH 052/411] Unify dep installation and update --- deployment/Dockerfile.fedora.amd64 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 73148763de..01b99deb6d 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -9,10 +9,8 @@ ENV ARTIFACT_DIR=/dist ENV IS_DOCKER=YES # Prepare Fedora environment -RUN dnf update -y - -# Install build dependencies -RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel +RUN dnf update -y \ + && dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel # Install DotNET SDK RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ From fc5e9324920f5c4bf1341ff99b4e8fd2c07069ef Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 22:55:37 -0400 Subject: [PATCH 053/411] Fix makefile formatting --- fedora/Makefile | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/fedora/Makefile b/fedora/Makefile index 1d2709a2fe..5a76d7d944 100644 --- a/fedora/Makefile +++ b/fedora/Makefile @@ -2,27 +2,27 @@ VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin.spec) srpm: cd fedora/; \ - SOURCE_DIR=.. \ - WORKDIR="$${PWD}"; \ - package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \ - pkg_src_dir="$${WORKDIR}"; \ - GNU_TAR=1; \ - tar \ - --transform "s,^\.,jellyfin-server-$(VERSION)," \ - --exclude='.git*' \ - --exclude='**/.git' \ - --exclude='**/.hg' \ - --exclude='**/.vs' \ - --exclude='**/.vscode' \ - --exclude='deployment' \ - --exclude='**/bin' \ - --exclude='**/obj' \ - --exclude='**/.nuget' \ - --exclude='*.deb' \ - --exclude='*.rpm' \ - --exclude='jellyfin-server-$(VERSION).tar.gz' \ - -czf "jellyfin-server-$(VERSION).tar.gz" \ - -C $${SOURCE_DIR} ./ + SOURCE_DIR=.. \ + WORKDIR="$${PWD}"; \ + package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \ + pkg_src_dir="$${WORKDIR}"; \ + GNU_TAR=1; \ + tar \ + --transform "s,^\.,jellyfin-server-$(VERSION)," \ + --exclude='.git*' \ + --exclude='**/.git' \ + --exclude='**/.hg' \ + --exclude='**/.vs' \ + --exclude='**/.vscode' \ + --exclude='deployment' \ + --exclude='**/bin' \ + --exclude='**/obj' \ + --exclude='**/.nuget' \ + --exclude='*.deb' \ + --exclude='*.rpm' \ + --exclude='jellyfin-server-$(VERSION).tar.gz' \ + -czf "jellyfin-server-$(VERSION).tar.gz" \ + -C $${SOURCE_DIR} ./ cd fedora/; \ rpmbuild -bs jellyfin.spec \ --define "_sourcedir $$PWD/" \ From 891aa7c25585dacc490590bc7337b00161a781e6 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 23 Mar 2020 23:00:35 -0400 Subject: [PATCH 054/411] Update info in Fedora spec --- fedora/jellyfin.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index e6a3170b56..c15b4ee957 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -9,8 +9,8 @@ Name: jellyfin-server Version: 10.6.0 Release: 1%{?dist} -Summary: The Free Software Media Browser -License: GPLv2 +Summary: The Free Software Media System Server backend and API +License: GPLv3 URL: https://jellyfin.media # Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` Source0: jellyfin-server-%{version}.tar.gz From 05aa28a37729e73b99cca2892b34590e456a9f07 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 24 Mar 2020 00:01:48 -0400 Subject: [PATCH 055/411] Clean up redundant Makefile steps --- fedora/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/fedora/Makefile b/fedora/Makefile index 5a76d7d944..97904ddd35 100644 --- a/fedora/Makefile +++ b/fedora/Makefile @@ -4,9 +4,6 @@ srpm: cd fedora/; \ SOURCE_DIR=.. \ WORKDIR="$${PWD}"; \ - package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \ - pkg_src_dir="$${WORKDIR}"; \ - GNU_TAR=1; \ tar \ --transform "s,^\.,jellyfin-server-$(VERSION)," \ --exclude='.git*' \ From b9fdd96ece39a6ff0f4ff37ecba36d7a0f65fcba Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 24 Mar 2020 01:10:29 -0400 Subject: [PATCH 056/411] Remove old stuff --- deployment/old/README.md | 62 ------ deployment/old/centos-package-x64/Dockerfile | 39 ---- deployment/old/centos-package-x64/clean.sh | 32 ---- .../old/centos-package-x64/dependencies.txt | 1 - .../old/centos-package-x64/docker-build.sh | 18 -- deployment/old/centos-package-x64/package.sh | 34 ---- deployment/old/centos-package-x64/pkg-src | 1 - .../old/debian-package-arm64/Dockerfile.amd64 | 43 ----- .../old/debian-package-arm64/Dockerfile.arm64 | 34 ---- deployment/old/debian-package-arm64/clean.sh | 27 --- .../old/debian-package-arm64/dependencies.txt | 1 - .../old/debian-package-arm64/docker-build.sh | 21 -- .../old/debian-package-arm64/package.sh | 45 ----- deployment/old/debian-package-arm64/pkg-src | 1 - .../old/debian-package-armhf/Dockerfile.amd64 | 42 ---- .../old/debian-package-armhf/Dockerfile.armhf | 34 ---- deployment/old/debian-package-armhf/clean.sh | 27 --- .../old/debian-package-armhf/dependencies.txt | 1 - .../old/debian-package-armhf/docker-build.sh | 21 -- .../old/debian-package-armhf/package.sh | 45 ----- deployment/old/debian-package-armhf/pkg-src | 1 - deployment/old/debian-package-x64/Dockerfile | 34 ---- deployment/old/debian-package-x64/clean.sh | 27 --- .../old/debian-package-x64/dependencies.txt | 1 - .../old/debian-package-x64/docker-build.sh | 20 -- deployment/old/debian-package-x64/package.sh | 34 ---- .../old/debian-package-x64/pkg-src/changelog | 59 ------ .../old/debian-package-x64/pkg-src/compat | 1 - .../debian-package-x64/pkg-src/conf/jellyfin | 40 ---- .../pkg-src/conf/jellyfin-sudoers | 37 ---- .../pkg-src/conf/jellyfin.service.conf | 7 - .../pkg-src/conf/logging.json | 30 --- .../old/debian-package-x64/pkg-src/control | 31 --- .../old/debian-package-x64/pkg-src/copyright | 29 --- .../old/debian-package-x64/pkg-src/gbp.conf | 6 - .../old/debian-package-x64/pkg-src/install | 6 - .../debian-package-x64/pkg-src/jellyfin.init | 61 ------ .../pkg-src/jellyfin.service | 14 -- .../pkg-src/jellyfin.upstart | 20 -- .../debian-package-x64/pkg-src/po/POTFILES.in | 1 - .../pkg-src/po/templates.pot | 57 ------ .../old/debian-package-x64/pkg-src/postinst | 92 --------- .../old/debian-package-x64/pkg-src/postrm | 81 -------- .../old/debian-package-x64/pkg-src/preinst | 78 -------- .../old/debian-package-x64/pkg-src/prerm | 61 ------ .../old/debian-package-x64/pkg-src/rules | 66 ------- .../pkg-src/source.lintian-overrides | 3 - .../debian-package-x64/pkg-src/source/format | 1 - .../debian-package-x64/pkg-src/source/options | 11 -- deployment/old/fedora-package-x64/Dockerfile | 33 ---- deployment/old/fedora-package-x64/clean.sh | 32 ---- .../old/fedora-package-x64/dependencies.txt | 1 - .../old/fedora-package-x64/docker-build.sh | 18 -- deployment/old/fedora-package-x64/package.sh | 34 ---- .../old/fedora-package-x64/pkg-src/.gitignore | 3 - .../old/fedora-package-x64/pkg-src/README.md | 43 ----- .../pkg-src/jellyfin-firewalld.xml | 9 - .../fedora-package-x64/pkg-src/jellyfin.env | 34 ---- .../pkg-src/jellyfin.override.conf | 7 - .../pkg-src/jellyfin.service | 15 -- .../fedora-package-x64/pkg-src/jellyfin.spec | 181 ------------------ .../pkg-src/jellyfin.sudoers | 19 -- .../old/fedora-package-x64/pkg-src/restart.sh | 36 ---- deployment/old/linux-x64/Dockerfile | 37 ---- deployment/old/linux-x64/clean.sh | 27 --- deployment/old/linux-x64/dependencies.txt | 1 - deployment/old/linux-x64/docker-build.sh | 36 ---- deployment/old/linux-x64/package.sh | 34 ---- deployment/old/macos/Dockerfile | 37 ---- deployment/old/macos/clean.sh | 27 --- deployment/old/macos/dependencies.txt | 1 - deployment/old/macos/docker-build.sh | 36 ---- deployment/old/macos/package.sh | 34 ---- deployment/old/portable/Dockerfile | 37 ---- deployment/old/portable/clean.sh | 27 --- deployment/old/portable/dependencies.txt | 1 - deployment/old/portable/docker-build.sh | 36 ---- deployment/old/portable/package.sh | 34 ---- .../old/ubuntu-package-arm64/Dockerfile.amd64 | 59 ------ .../old/ubuntu-package-arm64/Dockerfile.arm64 | 40 ---- deployment/old/ubuntu-package-arm64/clean.sh | 27 --- .../old/ubuntu-package-arm64/dependencies.txt | 1 - .../old/ubuntu-package-arm64/docker-build.sh | 21 -- .../old/ubuntu-package-arm64/package.sh | 45 ----- deployment/old/ubuntu-package-arm64/pkg-src | 1 - .../old/ubuntu-package-armhf/Dockerfile.amd64 | 59 ------ .../old/ubuntu-package-armhf/Dockerfile.armhf | 40 ---- deployment/old/ubuntu-package-armhf/clean.sh | 27 --- .../old/ubuntu-package-armhf/dependencies.txt | 1 - .../old/ubuntu-package-armhf/docker-build.sh | 21 -- .../old/ubuntu-package-armhf/package.sh | 45 ----- deployment/old/ubuntu-package-armhf/pkg-src | 1 - deployment/old/ubuntu-package-x64/Dockerfile | 36 ---- deployment/old/ubuntu-package-x64/clean.sh | 27 --- .../old/ubuntu-package-x64/dependencies.txt | 1 - .../old/ubuntu-package-x64/docker-build.sh | 20 -- deployment/old/ubuntu-package-x64/package.sh | 34 ---- deployment/old/ubuntu-package-x64/pkg-src | 1 - .../old/unraid/docker-templates/README.md | 15 -- .../old/unraid/docker-templates/jellyfin.xml | 57 ------ deployment/old/win-x64/Dockerfile | 37 ---- deployment/old/win-x64/clean.sh | 27 --- deployment/old/win-x64/dependencies.txt | 1 - deployment/old/win-x64/docker-build.sh | 61 ------ deployment/old/win-x64/package.sh | 34 ---- deployment/old/win-x86/Dockerfile | 37 ---- deployment/old/win-x86/clean.sh | 27 --- deployment/old/win-x86/dependencies.txt | 1 - deployment/old/win-x86/docker-build.sh | 61 ------ deployment/old/win-x86/package.sh | 34 ---- 110 files changed, 3207 deletions(-) delete mode 100644 deployment/old/README.md delete mode 100644 deployment/old/centos-package-x64/Dockerfile delete mode 100755 deployment/old/centos-package-x64/clean.sh delete mode 100644 deployment/old/centos-package-x64/dependencies.txt delete mode 100755 deployment/old/centos-package-x64/docker-build.sh delete mode 100755 deployment/old/centos-package-x64/package.sh delete mode 120000 deployment/old/centos-package-x64/pkg-src delete mode 100644 deployment/old/debian-package-arm64/Dockerfile.amd64 delete mode 100644 deployment/old/debian-package-arm64/Dockerfile.arm64 delete mode 100755 deployment/old/debian-package-arm64/clean.sh delete mode 100644 deployment/old/debian-package-arm64/dependencies.txt delete mode 100755 deployment/old/debian-package-arm64/docker-build.sh delete mode 100755 deployment/old/debian-package-arm64/package.sh delete mode 120000 deployment/old/debian-package-arm64/pkg-src delete mode 100644 deployment/old/debian-package-armhf/Dockerfile.amd64 delete mode 100644 deployment/old/debian-package-armhf/Dockerfile.armhf delete mode 100755 deployment/old/debian-package-armhf/clean.sh delete mode 100644 deployment/old/debian-package-armhf/dependencies.txt delete mode 100755 deployment/old/debian-package-armhf/docker-build.sh delete mode 100755 deployment/old/debian-package-armhf/package.sh delete mode 120000 deployment/old/debian-package-armhf/pkg-src delete mode 100644 deployment/old/debian-package-x64/Dockerfile delete mode 100755 deployment/old/debian-package-x64/clean.sh delete mode 100644 deployment/old/debian-package-x64/dependencies.txt delete mode 100755 deployment/old/debian-package-x64/docker-build.sh delete mode 100755 deployment/old/debian-package-x64/package.sh delete mode 100644 deployment/old/debian-package-x64/pkg-src/changelog delete mode 100644 deployment/old/debian-package-x64/pkg-src/compat delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/logging.json delete mode 100644 deployment/old/debian-package-x64/pkg-src/control delete mode 100644 deployment/old/debian-package-x64/pkg-src/copyright delete mode 100644 deployment/old/debian-package-x64/pkg-src/gbp.conf delete mode 100644 deployment/old/debian-package-x64/pkg-src/install delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.init delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.service delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.upstart delete mode 100644 deployment/old/debian-package-x64/pkg-src/po/POTFILES.in delete mode 100644 deployment/old/debian-package-x64/pkg-src/po/templates.pot delete mode 100644 deployment/old/debian-package-x64/pkg-src/postinst delete mode 100644 deployment/old/debian-package-x64/pkg-src/postrm delete mode 100644 deployment/old/debian-package-x64/pkg-src/preinst delete mode 100644 deployment/old/debian-package-x64/pkg-src/prerm delete mode 100755 deployment/old/debian-package-x64/pkg-src/rules delete mode 100644 deployment/old/debian-package-x64/pkg-src/source.lintian-overrides delete mode 100644 deployment/old/debian-package-x64/pkg-src/source/format delete mode 100644 deployment/old/debian-package-x64/pkg-src/source/options delete mode 100644 deployment/old/fedora-package-x64/Dockerfile delete mode 100755 deployment/old/fedora-package-x64/clean.sh delete mode 100644 deployment/old/fedora-package-x64/dependencies.txt delete mode 100755 deployment/old/fedora-package-x64/docker-build.sh delete mode 100755 deployment/old/fedora-package-x64/package.sh delete mode 100644 deployment/old/fedora-package-x64/pkg-src/.gitignore delete mode 100644 deployment/old/fedora-package-x64/pkg-src/README.md delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.env delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.service delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.spec delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers delete mode 100755 deployment/old/fedora-package-x64/pkg-src/restart.sh delete mode 100644 deployment/old/linux-x64/Dockerfile delete mode 100755 deployment/old/linux-x64/clean.sh delete mode 100644 deployment/old/linux-x64/dependencies.txt delete mode 100755 deployment/old/linux-x64/docker-build.sh delete mode 100755 deployment/old/linux-x64/package.sh delete mode 100644 deployment/old/macos/Dockerfile delete mode 100755 deployment/old/macos/clean.sh delete mode 100644 deployment/old/macos/dependencies.txt delete mode 100755 deployment/old/macos/docker-build.sh delete mode 100755 deployment/old/macos/package.sh delete mode 100644 deployment/old/portable/Dockerfile delete mode 100755 deployment/old/portable/clean.sh delete mode 100644 deployment/old/portable/dependencies.txt delete mode 100755 deployment/old/portable/docker-build.sh delete mode 100755 deployment/old/portable/package.sh delete mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.amd64 delete mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.arm64 delete mode 100755 deployment/old/ubuntu-package-arm64/clean.sh delete mode 100644 deployment/old/ubuntu-package-arm64/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-arm64/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-arm64/package.sh delete mode 120000 deployment/old/ubuntu-package-arm64/pkg-src delete mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.amd64 delete mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.armhf delete mode 100755 deployment/old/ubuntu-package-armhf/clean.sh delete mode 100644 deployment/old/ubuntu-package-armhf/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-armhf/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-armhf/package.sh delete mode 120000 deployment/old/ubuntu-package-armhf/pkg-src delete mode 100644 deployment/old/ubuntu-package-x64/Dockerfile delete mode 100755 deployment/old/ubuntu-package-x64/clean.sh delete mode 100644 deployment/old/ubuntu-package-x64/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-x64/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-x64/package.sh delete mode 120000 deployment/old/ubuntu-package-x64/pkg-src delete mode 100644 deployment/old/unraid/docker-templates/README.md delete mode 100644 deployment/old/unraid/docker-templates/jellyfin.xml delete mode 100644 deployment/old/win-x64/Dockerfile delete mode 100755 deployment/old/win-x64/clean.sh delete mode 100644 deployment/old/win-x64/dependencies.txt delete mode 100755 deployment/old/win-x64/docker-build.sh delete mode 100755 deployment/old/win-x64/package.sh delete mode 100644 deployment/old/win-x86/Dockerfile delete mode 100755 deployment/old/win-x86/clean.sh delete mode 100644 deployment/old/win-x86/dependencies.txt delete mode 100755 deployment/old/win-x86/docker-build.sh delete mode 100755 deployment/old/win-x86/package.sh diff --git a/deployment/old/README.md b/deployment/old/README.md deleted file mode 100644 index a805f59ca3..0000000000 --- a/deployment/old/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Jellyfin Packaging - -This directory contains the packaging configuration of Jellyfin for multiple platforms. The specification is below; all package platforms must follow the specification to be compatable with the central `build` script. - -## Package List - -### Operating System Packages - -* `debian-package-x64`: Package for Debian and Ubuntu amd64 systems. -* `fedora-package-x64`: Package for Fedora, CentOS, and Red Hat Enterprise Linux amd64 systems. - -### Portable Builds (archives) - -* `linux-x64`: Portable binary archive for generic Linux amd64 systems. -* `macos`: Portable binary archive for MacOS amd64 systems. -* `win-x64`: Portable binary archive for Windows amd64 systems. -* `win-x86`: Portable binary archive for Windows i386 systems. - -### Other Builds - -These builds are not necessarily run from the `build` script, but are present for other platforms. - -* `portable`: Compiled `.dll` for use with .NET Core runtime on any system. -* `docker`: Docker manifests for auto-publishing. -* `unraid`: unRaid Docker template; not built by `build` but imported into unRaid directly. -* `windows`: Support files and scripts for Windows CI build. - -## Package Specification - -### Dependencies - -* If a platform requires additional build dependencies, the required binary names, i.e. to validate `which `, should be specified in a `dependencies.txt` file inside the platform directory. - -* Each dependency should be present on its own line. - -### Action Scripts - -* Actions are defined in BASH scripts with the name `.sh` within the platform directory. - -* The list of valid actions are: - - 1. `build`: Builds a set of binaries. - 2. `package`: Assembles the compiled binaries into a package. - 3. `sign`: Performs signing actions on a package. - 4. `publish`: Performs a publishing action for a package. - 5. `clean`: Cleans up any artifacts from the previous actions. - -* All package actions are optional, however at least one should generate output files, and any that do should contain a `clean` action. - -* Actions are executed in the order specified above, and later actions may depend on former actions. - -* Actions except for `clean` should `set -o errexit` to terminate on failed actions. - -* The `clean` action should always `exit 0` even if no work is done or it fails. - -* The `clean` action can be passed a variable as argument 1, named `keep_artifacts`, containing either the value `y` or `n`. It is indended to handle situations when the user runs `build --keep-artifacts` and should be handled intelligently. Usually, this is used to preserve Docker images while still removing temporary directories. - -### Output Files - -* Upon completion of the defined actions, at least one output file must be created in the `/pkg-dist` directory. - -* Output files will be moved to the directory `jellyfin-build/` one directory above the repository root upon completion. diff --git a/deployment/old/centos-package-x64/Dockerfile b/deployment/old/centos-package-x64/Dockerfile deleted file mode 100644 index 08219a2e4a..0000000000 --- a/deployment/old/centos-package-x64/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM centos:7 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/centos-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist - -# Prepare CentOS environment -RUN yum update -y \ - && yum install -y epel-release - -# Install build dependencies -RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git - -# Install recent NodeJS and Yarn -RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \ - && rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \ - && yum install -y yarn - -# Install DotNET SDK -RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \ - && rpmdev-setuptree \ - && yum install -y dotnet-sdk-${SDK_VERSION} - -# Create symlinks and directories -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/centos-package-x64/clean.sh b/deployment/old/centos-package-x64/clean.sh deleted file mode 100755 index 31455de0d4..0000000000 --- a/deployment/old/centos-package-x64/clean.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" -VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -package_source_dir="${WORKDIR}/pkg-src" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-centos-build" - -rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ - || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/centos-package-x64/dependencies.txt b/deployment/old/centos-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/centos-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/centos-package-x64/docker-build.sh b/deployment/old/centos-package-x64/docker-build.sh deleted file mode 100755 index 62dd144e50..0000000000 --- a/deployment/old/centos-package-x64/docker-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Builds the RPM inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Build RPM -make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS -rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/rpm -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/centos-package-x64/package.sh b/deployment/old/centos-package-x64/package.sh deleted file mode 100755 index 1b983f49d9..0000000000 --- a/deployment/old/centos-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-centos-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the RPMs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the RPMs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/centos-package-x64/pkg-src b/deployment/old/centos-package-x64/pkg-src deleted file mode 120000 index 3ff4d3cbf5..0000000000 --- a/deployment/old/centos-package-x64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../fedora-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-arm64/Dockerfile.amd64 b/deployment/old/debian-package-arm64/Dockerfile.amd64 deleted file mode 100644 index b63e08b7dd..0000000000 --- a/deployment/old/debian-package-arm64/Dockerfile.amd64 +++ /dev/null @@ -1,43 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Prepare the cross-toolchain -RUN dpkg --add-architecture arm64 \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 8 \ - && cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \ - && apt-get install -y gcc-8-source libstdc++-8-dev-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64 - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] -#ENTRYPOINT ["/bin/bash"] diff --git a/deployment/old/debian-package-arm64/Dockerfile.arm64 b/deployment/old/debian-package-arm64/Dockerfile.arm64 deleted file mode 100644 index 9ca4868441..0000000000 --- a/deployment/old/debian-package-arm64/Dockerfile.arm64 +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=arm64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-arm64/clean.sh b/deployment/old/debian-package-arm64/clean.sh deleted file mode 100755 index e7bfdf8b4b..0000000000 --- a/deployment/old/debian-package-arm64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_arm64-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-arm64/dependencies.txt b/deployment/old/debian-package-arm64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-arm64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-arm64/docker-build.sh b/deployment/old/debian-package-arm64/docker-build.sh deleted file mode 100755 index 67ab6bd74b..0000000000 --- a/deployment/old/debian-package-arm64/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarm64 - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-arm64/package.sh b/deployment/old/debian-package-arm64/package.sh deleted file mode 100755 index 2091982187..0000000000 --- a/deployment/old/debian-package-arm64/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_arm64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.arm64" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-arm64/pkg-src b/deployment/old/debian-package-arm64/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/debian-package-arm64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/debian-package-armhf/Dockerfile.amd64 b/deployment/old/debian-package-armhf/Dockerfile.amd64 deleted file mode 100644 index 1b64b53148..0000000000 --- a/deployment/old/debian-package-armhf/Dockerfile.amd64 +++ /dev/null @@ -1,42 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Prepare the cross-toolchain -RUN dpkg --add-architecture armhf \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 8 \ - && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \ - && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip binutils-arm-linux-gnueabihf libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/Dockerfile.armhf b/deployment/old/debian-package-armhf/Dockerfile.armhf deleted file mode 100644 index dd398b5aa5..0000000000 --- a/deployment/old/debian-package-armhf/Dockerfile.armhf +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=armhf - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/clean.sh b/deployment/old/debian-package-armhf/clean.sh deleted file mode 100755 index 35a3d3e9ad..0000000000 --- a/deployment/old/debian-package-armhf/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_armhf-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-armhf/dependencies.txt b/deployment/old/debian-package-armhf/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-armhf/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-armhf/docker-build.sh b/deployment/old/debian-package-armhf/docker-build.sh deleted file mode 100755 index 1bd7fb2911..0000000000 --- a/deployment/old/debian-package-armhf/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarmhf - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-armhf/package.sh b/deployment/old/debian-package-armhf/package.sh deleted file mode 100755 index 4a27dd8283..0000000000 --- a/deployment/old/debian-package-armhf/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_armhf-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.armhf" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-armhf/pkg-src b/deployment/old/debian-package-armhf/pkg-src deleted file mode 120000 index 0bb6d55249..0000000000 --- a/deployment/old/debian-package-armhf/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-x64/Dockerfile b/deployment/old/debian-package-x64/Dockerfile deleted file mode 100644 index e863d1edf9..0000000000 --- a/deployment/old/debian-package-x64/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-x64/clean.sh b/deployment/old/debian-package-x64/clean.sh deleted file mode 100755 index 4e507bcb27..0000000000 --- a/deployment/old/debian-package-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-x64/dependencies.txt b/deployment/old/debian-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-x64/docker-build.sh b/deployment/old/debian-package-x64/docker-build.sh deleted file mode 100755 index 962a522ebc..0000000000 --- a/deployment/old/debian-package-x64/docker-build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -dpkg-buildpackage -us -uc - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-x64/package.sh b/deployment/old/debian-package-x64/package.sh deleted file mode 100755 index 5a416959ab..0000000000 --- a/deployment/old/debian-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-x64/pkg-src/changelog b/deployment/old/debian-package-x64/pkg-src/changelog deleted file mode 100644 index 51c4822370..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/changelog +++ /dev/null @@ -1,59 +0,0 @@ -jellyfin (10.5.0-1) unstable; urgency=medium - - * New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 - - -- Jellyfin Packaging Team Fri, 11 Oct 2019 20:12:38 -0400 - -jellyfin (10.4.0-1) unstable; urgency=medium - - * New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 - - -- Jellyfin Packaging Team Sat, 31 Aug 2019 21:38:56 -0400 - -jellyfin (10.3.7-1) unstable; urgency=medium - - * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 - - -- Jellyfin Packaging Team Wed, 24 Jul 2019 10:48:28 -0400 - -jellyfin (10.3.6-1) unstable; urgency=medium - - * New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 - - -- Jellyfin Packaging Team Sat, 06 Jul 2019 13:34:19 -0400 - -jellyfin (10.3.5-1) unstable; urgency=medium - - * New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 - - -- Jellyfin Packaging Team Sun, 09 Jun 2019 21:47:35 -0400 - -jellyfin (10.3.4-1) unstable; urgency=medium - - * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 - - -- Jellyfin Packaging Team Thu, 06 Jun 2019 22:45:31 -0400 - -jellyfin (10.3.3-1) unstable; urgency=medium - - * New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 - - -- Jellyfin Packaging Team Fri, 17 May 2019 23:12:08 -0400 - -jellyfin (10.3.2-1) unstable; urgency=medium - - * New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 - - -- Jellyfin Packaging Team Tue, 30 Apr 2019 20:18:44 -0400 - -jellyfin (10.3.1-1) unstable; urgency=medium - - * New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 - - -- Jellyfin Packaging Team Sat, 20 Apr 2019 14:24:07 -0400 - -jellyfin (10.3.0-1) unstable; urgency=medium - - * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 - - -- Jellyfin Packaging Team Fri, 19 Apr 2019 14:24:29 -0400 diff --git a/deployment/old/debian-package-x64/pkg-src/compat b/deployment/old/debian-package-x64/pkg-src/compat deleted file mode 100644 index 45a4fb75db..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/compat +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin deleted file mode 100644 index c6e595f15a..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin +++ /dev/null @@ -1,40 +0,0 @@ -# Jellyfin default configuration options -# This is a POSIX shell fragment - -# Use this file to override the default configurations; add additional -# options with JELLYFIN_ADD_OPTS. - -# Under systemd, use -# /etc/systemd/system/jellyfin.service.d/jellyfin.service.conf -# to override the user or this config file's location. - -# -# General options -# - -# Program directories -JELLYFIN_DATA_DIR="/var/lib/jellyfin" -JELLYFIN_CONFIG_DIR="/etc/jellyfin" -JELLYFIN_LOG_DIR="/var/log/jellyfin" -JELLYFIN_CACHE_DIR="/var/cache/jellyfin" - -# Restart script for in-app server control -JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh" - -# ffmpeg binary paths, overriding the system values -JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" - -# [OPTIONAL] run Jellyfin as a headless service -#JELLYFIN_SERVICE_OPT="--service" - -# [OPTIONAL] run Jellyfin without the web app -#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" - -# -# SysV init/Upstart options -# - -# Application username -JELLYFIN_USER="jellyfin" -# Full application command -JELLYFIN_ARGS="$JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers deleted file mode 100644 index b481ba4ad4..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers +++ /dev/null @@ -1,37 +0,0 @@ -#Allow jellyfin group to start, stop and restart itself -Cmnd_Alias RESTARTSERVER_SYSV = /sbin/service jellyfin restart, /usr/sbin/service jellyfin restart -Cmnd_Alias STARTSERVER_SYSV = /sbin/service jellyfin start, /usr/sbin/service jellyfin start -Cmnd_Alias STOPSERVER_SYSV = /sbin/service jellyfin stop, /usr/sbin/service jellyfin stop -Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin -Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin -Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin -Cmnd_Alias RESTARTSERVER_INITD = /etc/init.d/jellyfin restart -Cmnd_Alias STARTSERVER_INITD = /etc/init.d/jellyfin start -Cmnd_Alias STOPSERVER_INITD = /etc/init.d/jellyfin stop - - -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_INITD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_INITD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_INITD - -Defaults!RESTARTSERVER_SYSV !requiretty -Defaults!STARTSERVER_SYSV !requiretty -Defaults!STOPSERVER_SYSV !requiretty -Defaults!RESTARTSERVER_SYSTEMD !requiretty -Defaults!STARTSERVER_SYSTEMD !requiretty -Defaults!STOPSERVER_SYSTEMD !requiretty -Defaults!RESTARTSERVER_INITD !requiretty -Defaults!STARTSERVER_INITD !requiretty -Defaults!STOPSERVER_INITD !requiretty - -#Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - -Defaults:jellyfin !requiretty diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf deleted file mode 100644 index 1b69dd74ef..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Jellyfin systemd configuration options - -# Use this file to override the user or environment file location. - -[Service] -#User = jellyfin -#EnvironmentFile = /etc/default/jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/conf/logging.json b/deployment/old/debian-package-x64/pkg-src/conf/logging.json deleted file mode 100644 index f32b2089eb..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/logging.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Serilog": { - "MinimumLevel": "Information", - "WriteTo": [ - { - "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" - } - }, - { - "Name": "Async", - "Args": { - "configure": [ - { - "Name": "File", - "Args": { - "path": "%JELLYFIN_LOG_DIR%//jellyfin.log", - "fileSizeLimitBytes": 10485700, - "rollOnFileSizeLimit": true, - "retainedFileCountLimit": 10, - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" - } - } - ] - } - } - ] - } -} diff --git a/deployment/old/debian-package-x64/pkg-src/control b/deployment/old/debian-package-x64/pkg-src/control deleted file mode 100644 index 13fd3ecabb..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/control +++ /dev/null @@ -1,31 +0,0 @@ -Source: jellyfin -Section: misc -Priority: optional -Maintainer: Jellyfin Team -Build-Depends: debhelper (>= 9), - dotnet-sdk-3.1, - libc6-dev, - libcurl4-openssl-dev, - libfontconfig1-dev, - libfreetype6-dev, - libssl-dev, - wget, - npm | nodejs -Standards-Version: 3.9.4 -Homepage: https://jellyfin.media/ -Vcs-Git: https://github.org/jellyfin/jellyfin.git -Vcs-Browser: https://github.org/jellyfin/jellyfin - -Package: jellyfin -Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Architecture: any -Depends: at, - libsqlite3-0, - jellyfin-ffmpeg, - libfontconfig1, - libfreetype6, - libssl1.1 -Description: Jellyfin is a home media server. - It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. diff --git a/deployment/old/debian-package-x64/pkg-src/copyright b/deployment/old/debian-package-x64/pkg-src/copyright deleted file mode 100644 index 0d7a2a6007..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/copyright +++ /dev/null @@ -1,29 +0,0 @@ -Format: http://dep.debian.net/deps/dep5 -Upstream-Name: jellyfin -Source: https://github.com/jellyfin/jellyfin - -Files: * -Copyright: 2018 Jellyfin Team -License: GPL-2.0+ - -Files: debian/* -Copyright: 2018 Joshua Boniface -Copyright: 2014 Carlos Hernandez -License: GPL-2.0+ - -License: GPL-2.0+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/deployment/old/debian-package-x64/pkg-src/gbp.conf b/deployment/old/debian-package-x64/pkg-src/gbp.conf deleted file mode 100644 index 60b3d28723..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/gbp.conf +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -pristine-tar = False -cleaner = fakeroot debian/rules clean - -[import-orig] -filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/deployment/old/debian-package-x64/pkg-src/install b/deployment/old/debian-package-x64/pkg-src/install deleted file mode 100644 index 994322d141..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/install +++ /dev/null @@ -1,6 +0,0 @@ -usr/lib/jellyfin usr/lib/ -debian/conf/jellyfin etc/default/ -debian/conf/logging.json etc/jellyfin/ -debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/ -debian/conf/jellyfin-sudoers etc/sudoers.d/ -debian/bin/restart.sh usr/lib/jellyfin/ diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.init b/deployment/old/debian-package-x64/pkg-src/jellyfin.init deleted file mode 100644 index 7f5642bac1..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.init +++ /dev/null @@ -1,61 +0,0 @@ -### BEGIN INIT INFO -# Provides: Jellyfin Media Server -# Required-Start: $local_fs $network -# Required-Stop: $local_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Jellyfin Media Server -# Description: Runs Jellyfin Server -### END INIT INFO - -set -e - -# Carry out specific functions when asked to by the system - -if test -f /etc/default/jellyfin; then - . /etc/default/jellyfin -fi - -. /lib/lsb/init-functions - -PIDFILE="/run/jellyfin.pid" - -case "$1" in - start) - log_daemon_msg "Starting Jellyfin Media Server" "jellyfin" || true - - if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - stop) - log_daemon_msg "Stopping Jellyfin Media Server" "jellyfin" || true - if start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - restart) - log_daemon_msg "Restarting Jellyfin Media Server" "jellyfin" || true - start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE --remove-pidfile - if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - status) - status_of_proc -p $PIDFILE /usr/bin/jellyfin jellyfin && exit 0 || exit $? - ;; - - *) - echo "Usage: $0 {start|stop|restart|status}" - exit 1 - ;; -esac diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.service b/deployment/old/debian-package-x64/pkg-src/jellyfin.service deleted file mode 100644 index 1305e238b0..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description = Jellyfin Media Server -After = network.target - -[Service] -Type = simple -EnvironmentFile = /etc/default/jellyfin -User = jellyfin -ExecStart = /usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} -Restart = on-failure -TimeoutSec = 15 - -[Install] -WantedBy = multi-user.target diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart b/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart deleted file mode 100644 index ef5bc9bcaf..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart +++ /dev/null @@ -1,20 +0,0 @@ -description "jellyfin daemon" - -start on (local-filesystems and net-device-up IFACE!=lo) -stop on runlevel [!2345] - -console log -respawn -respawn limit 10 5 - -kill timeout 20 - -script - set -x - echo "Starting $UPSTART_JOB" - - # Log file - logger -t "$0" "DEBUG: `set`" - . /etc/default/jellyfin - exec su -u $JELLYFIN_USER -c /usr/bin/jellyfin $JELLYFIN_ARGS -end script diff --git a/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in b/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in deleted file mode 100644 index cef83a3407..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in +++ /dev/null @@ -1 +0,0 @@ -[type: gettext/rfc822deb] templates diff --git a/deployment/old/debian-package-x64/pkg-src/po/templates.pot b/deployment/old/debian-package-x64/pkg-src/po/templates.pot deleted file mode 100644 index 2cdcae4173..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/po/templates.pot +++ /dev/null @@ -1,57 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: jellyfin-server\n" -"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n" -"POT-Creation-Date: 2015-06-12 20:51-0600\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: note -#. Description -#: ../templates:1001 -msgid "Jellyfin permission info:" -msgstr "" - -#. Type: note -#. Description -#: ../templates:1001 -msgid "" -"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the " -"user jellyfin has read and write access to any folders you wish to add to your " -"library. Otherwise please run jellyfin under a different user." -msgstr "" - -#. Type: string -#. Description -#: ../templates:2001 -msgid "Username to run Jellyfin as:" -msgstr "" - -#. Type: string -#. Description -#: ../templates:2001 -msgid "The user that jellyfin will run as." -msgstr "" - -#. Type: note -#. Description -#: ../templates:3001 -msgid "Jellyfin still running" -msgstr "" - -#. Type: note -#. Description -#: ../templates:3001 -msgid "Jellyfin is currently running. Please close it and try again." -msgstr "" diff --git a/deployment/old/debian-package-x64/pkg-src/postinst b/deployment/old/debian-package-x64/pkg-src/postinst deleted file mode 100644 index 860222e051..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/postinst +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -case "$1" in - configure) - # create jellyfin group if it does not exist - if [[ -z "$(getent group jellyfin)" ]]; then - addgroup --quiet --system jellyfin > /dev/null 2>&1 - fi - # create jellyfin user if it does not exist - if [[ -z "$(getent passwd jellyfin)" ]]; then - adduser --system --ingroup jellyfin --shell /bin/false jellyfin --no-create-home --home ${PROGRAMDATA} \ - --gecos "Jellyfin default user" > /dev/null 2>&1 - fi - # ensure $PROGRAMDATA exists - if [[ ! -d $PROGRAMDATA ]]; then - mkdir $PROGRAMDATA - fi - # ensure $CONFIGDATA exists - if [[ ! -d $CONFIGDATA ]]; then - mkdir $CONFIGDATA - fi - # ensure $LOGDATA exists - if [[ ! -d $LOGDATA ]]; then - mkdir $LOGDATA - fi - # ensure $CACHEDATA exists - if [[ ! -d $CACHEDATA ]]; then - mkdir $CACHEDATA - fi - # Ensure permissions are correct on all config directories - chown -R jellyfin $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - - chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true - - # Install jellyfin symlink into /usr/bin - ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin - - ;; - abort-upgrade|abort-remove|abort-deconfigure) - ;; - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER - -if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - # Manual init script handling - deb-systemd-helper unmask jellyfin.service >/dev/null || true - # was-enabled defaults to true, so new installations run enable. - if deb-systemd-helper --quiet was-enabled jellyfin.service; then - # Enables the unit on first installation, creates new - # symlinks on upgrades if the unit file has changed. - deb-systemd-helper enable jellyfin.service >/dev/null || true - else - # Update the statefile to add new symlinks (if any), which need to be - # cleaned up on purge. Also remove old symlinks. - deb-systemd-helper update-state jellyfin.service >/dev/null || true - fi -fi - -# End automatically added section -# Automatically added by dh_installinit -if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then - if [[ -d "/run/systemd/systemd" ]]; then - systemctl --system daemon-reload >/dev/null || true - deb-systemd-invoke start jellyfin >/dev/null || true - elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then - update-rc.d jellyfin defaults >/dev/null - invoke-rc.d jellyfin start || exit $? - fi -fi -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/postrm b/deployment/old/debian-package-x64/pkg-src/postrm deleted file mode 100644 index 1d00a984ec..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/postrm +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -# In case this system is running systemd, we make systemd reload the unit files -# to pick up changes. -if [[ -d /run/systemd/system ]] ; then - systemctl --system daemon-reload >/dev/null || true -fi - -case "$1" in - purge) - echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true - - if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.connf" ]]; then - update-rc.d jellyfin remove >/dev/null 2>&1 || true - fi - - if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - deb-systemd-helper purge jellyfin.service >/dev/null - deb-systemd-helper unmask jellyfin.service >/dev/null - fi - - # Remove user and group - userdel jellyfin > /dev/null 2>&1 || true - delgroup --quiet jellyfin > /dev/null 2>&1 || true - # Remove config dir - if [[ -d $CONFIGDATA ]]; then - rm -rf $CONFIGDATA - fi - # Remove log dir - if [[ -d $LOGDATA ]]; then - rm -rf $LOGDATA - fi - # Remove cache dir - if [[ -d $CACHEDATA ]]; then - rm -rf $CACHEDATA - fi - # Remove program data dir - if [[ -d $PROGRAMDATA ]]; then - rm -rf $PROGRAMDATA - fi - # Remove binary symlink - [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin - # Remove sudoers config - [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers - # Remove anything at the default locations; catches situations where the user moved the defaults - [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin - [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin - [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin - [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin - ;; - remove) - if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - deb-systemd-helper mask jellyfin.service >/dev/null - fi - ;; - upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - ;; - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/preinst b/deployment/old/debian-package-x64/pkg-src/preinst deleted file mode 100644 index 2713fb9b80..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/preinst +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -# In case this system is running systemd, we make systemd reload the unit files -# to pick up changes. -if [[ -d /run/systemd/system ]] ; then - systemctl --system daemon-reload >/dev/null || true -fi - -case "$1" in - install|upgrade) - # try graceful termination; - if [[ -d /run/systemd/system ]]; then - deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true - elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then - invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true - fi - # try and figure out if jellyfin is running - PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) - [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) - # if its running, let's stop it - if [[ -n "$JELLYFIN_PID" ]]; then - echo "Stopping Jellyfin!" - # if jellyfin is still running, kill it - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - CPIDS=$(pgrep -P $JELLYFIN_PID) - sleep 2 && kill -KILL $CPIDS - kill -TERM $CPIDS > /dev/null 2>&1 - fi - sleep 1 - # if it's still running, show error - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - echo "Could not successfully stop JellyfinServer, please do so before uninstalling." - exit 1 - else - [[ -f $PIDFILE ]] && rm $PIDFILE - fi - fi - - # Clean up old Emby cruft that can break the user's system - [[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby - - # If we have existing config, log, or cache dirs in /var/lib/jellyfin, move them into the right place - if [[ -d $PROGRAMDATA/config ]]; then - mv $PROGRAMDATA/config $CONFIGDATA - fi - if [[ -d $PROGRAMDATA/logs ]]; then - mv $PROGRAMDATA/logs $LOGDATA - fi - if [[ -d $PROGRAMDATA/logs ]]; then - mv $PROGRAMDATA/cache $CACHEDATA - fi - - ;; - abort-upgrade) - ;; - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/prerm b/deployment/old/debian-package-x64/pkg-src/prerm deleted file mode 100644 index e965cb7d71..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/prerm +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -case "$1" in - remove|upgrade|deconfigure) - echo "Stopping Jellyfin!" - # try graceful termination; - if [[ -d /run/systemd/system ]]; then - deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true - elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then - invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true - fi - # Ensure that it is shutdown - PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) - [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) - # if its running, let's stop it - if [[ -n "$JELLYFIN_PID" ]]; then - # if jellyfin is still running, kill it - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - CPIDS=$(pgrep -P $JELLYFIN_PID) - sleep 2 && kill -KILL $CPIDS - kill -TERM $CPIDS > /dev/null 2>&1 - fi - sleep 1 - # if it's still running, show error - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - echo "Could not successfully stop Jellyfin, please do so before uninstalling." - exit 1 - else - [[ -f $PIDFILE ]] && rm $PIDFILE - fi - fi - if [[ -f /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so ]]; then - rm /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so - fi - ;; - failed-upgrade) - ;; - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/rules b/deployment/old/debian-package-x64/pkg-src/rules deleted file mode 100755 index c2d57dfb22..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/rules +++ /dev/null @@ -1,66 +0,0 @@ -#! /usr/bin/make -f -CONFIG := Release -TERM := xterm -SHELL := /bin/bash -WEB_TARGET := $(CURDIR)/MediaBrowser.WebDashboard/jellyfin-web -WEB_VERSION := $(shell sed -n -e 's/^version: "\(.*\)"/\1/p' $(CURDIR)/build.yaml) - -HOST_ARCH := $(shell arch) -BUILD_ARCH := ${DEB_HOST_MULTIARCH} -ifeq ($(HOST_ARCH),x86_64) - # Building AMD64 - DOTNETRUNTIME := debian-x64 - ifeq ($(BUILD_ARCH),arm-linux-gnueabihf) - # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm - endif - ifeq ($(BUILD_ARCH),aarch64-linux-gnu) - # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm64 - endif -endif -ifeq ($(HOST_ARCH),armv7l) - # Building ARM - DOTNETRUNTIME := debian-arm -endif -ifeq ($(HOST_ARCH),arm64) - # Building ARM - DOTNETRUNTIME := debian-arm64 -endif - -export DH_VERBOSE=1 -export DOTNET_CLI_TELEMETRY_OPTOUT=1 - -%: - dh $@ - -# disable "make check" -override_dh_auto_test: - -# disable stripping debugging symbols -override_dh_clistrip: - -override_dh_auto_build: - echo $(WEB_VERSION) - # Clone down and build Web frontend - mkdir -p $(WEB_TARGET) - wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/v$(WEB_VERSION).tar.gz || wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz - mkdir -p $(CURDIR)/web - tar -xzf web-src.tgz -C $(CURDIR)/web/ --strip 1 - cd $(CURDIR)/web/ && npm install yarn - cd $(CURDIR)/web/ && node_modules/yarn/bin/yarn install - mv $(CURDIR)/web/dist/* $(WEB_TARGET)/ - # Build the application - dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ - "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server - -override_dh_auto_clean: - dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true - rm -f '$(CURDIR)/web-src.tgz' - rm -rf '$(CURDIR)/usr' - rm -rf '$(CURDIR)/web' - rm -rf '$(WEB_TARGET)' - -# Force the service name to jellyfin even if we're building jellyfin-nightly -override_dh_installinit: - dh_installinit --name=jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides b/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides deleted file mode 100644 index aeb332f13a..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides +++ /dev/null @@ -1,3 +0,0 @@ -# This is an override for the following lintian errors: -jellyfin source: license-problem-md5sum-non-free-file Emby.Drawing/ImageMagick/fonts/webdings.ttf* -jellyfin source: source-is-missing diff --git a/deployment/old/debian-package-x64/pkg-src/source/format b/deployment/old/debian-package-x64/pkg-src/source/format deleted file mode 100644 index d3827e75a5..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source/format +++ /dev/null @@ -1 +0,0 @@ -1.0 diff --git a/deployment/old/debian-package-x64/pkg-src/source/options b/deployment/old/debian-package-x64/pkg-src/source/options deleted file mode 100644 index 17b5373d5e..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source/options +++ /dev/null @@ -1,11 +0,0 @@ -tar-ignore='.git*' -tar-ignore='**/.git' -tar-ignore='**/.hg' -tar-ignore='**/.vs' -tar-ignore='**/.vscode' -tar-ignore='deployment' -tar-ignore='**/bin' -tar-ignore='**/obj' -tar-ignore='**/.nuget' -tar-ignore='*.deb' -tar-ignore='ThirdParty' diff --git a/deployment/old/fedora-package-x64/Dockerfile b/deployment/old/fedora-package-x64/Dockerfile deleted file mode 100644 index 87120f3a05..0000000000 --- a/deployment/old/fedora-package-x64/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM fedora:31 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist - -# Prepare Fedora environment -RUN dnf update -y - -# Install build dependencies -RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn - -# Install DotNET SDK -RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ - && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ - && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} - -# Create symlinks and directories -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/fedora-package-x64/clean.sh b/deployment/old/fedora-package-x64/clean.sh deleted file mode 100755 index 700c8f1bb3..0000000000 --- a/deployment/old/fedora-package-x64/clean.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" -VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -package_source_dir="${WORKDIR}/pkg-src" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-fedora-build" - -rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ - || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/fedora-package-x64/dependencies.txt b/deployment/old/fedora-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/fedora-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/fedora-package-x64/docker-build.sh b/deployment/old/fedora-package-x64/docker-build.sh deleted file mode 100755 index 740e8d35ca..0000000000 --- a/deployment/old/fedora-package-x64/docker-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Builds the RPM inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Build RPM -make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS -rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/rpm -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/fedora-package-x64/package.sh b/deployment/old/fedora-package-x64/package.sh deleted file mode 100755 index ae6962dd1f..0000000000 --- a/deployment/old/fedora-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-fedora-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the RPMs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the RPMs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/fedora-package-x64/pkg-src/.gitignore b/deployment/old/fedora-package-x64/pkg-src/.gitignore deleted file mode 100644 index 6019b98c22..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.rpm -*.zip -*.tar.gz \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/README.md b/deployment/old/fedora-package-x64/pkg-src/README.md deleted file mode 100644 index 7ed6f7efc6..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Jellyfin RPM - -## Build Fedora Package with docker - -Change into this directory `cd rpm-package` -Run the build script `./build-fedora-rpm.sh`. -Resulting RPM and src.rpm will be in `../../jellyfin-*.rpm` - -## ffmpeg - -The RPM package for Fedora/CentOS requires some additional repositories as ffmpeg is not in the main repositories. - -```shell -# ffmpeg from RPMfusion free -# Fedora -$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm -# CentOS 7 -$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm -``` - -## ISO mounting - -To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers` -``` -# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount -# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount -``` - -## Building with dotnet - -Jellyfin is build with `--self-contained` so no dotnet required for runtime. - -```shell -# dotnet required for building the RPM -# Fedora -$ sudo dnf copr enable @dotnet-sig/dotnet -# CentOS -$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm -``` - -## TODO - -- [ ] OpenSUSE \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml b/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml deleted file mode 100644 index 538c5d65f8..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - Jellyfin - The Free Software Media System. - - - - - diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.env b/deployment/old/fedora-package-x64/pkg-src/jellyfin.env deleted file mode 100644 index de48f13af5..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.env +++ /dev/null @@ -1,34 +0,0 @@ -# Jellyfin default configuration options - -# Use this file to override the default configurations; add additional -# options with JELLYFIN_ADD_OPTS. - -# To override the user or this config file's location, use -# /etc/systemd/system/jellyfin.service.d/override.conf - -# -# This is a POSIX shell fragment -# - -# -# General options -# - -# Program directories -JELLYFIN_DATA_DIR="/var/lib/jellyfin" -JELLYFIN_CONFIG_DIR="/etc/jellyfin" -JELLYFIN_LOG_DIR="/var/log/jellyfin" -JELLYFIN_CACHE_DIR="/var/cache/jellyfin" - -# In-App service control -JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" - -# [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values -#JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg" - -# [OPTIONAL] run Jellyfin as a headless service -#JELLYFIN_SERVICE_OPT="--service" - -# [OPTIONAL] run Jellyfin without the web app -#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" - diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf b/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf deleted file mode 100644 index 8652450bb4..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Jellyfin systemd configuration options - -# Use this file to override the user or environment file location. - -[Service] -#User = jellyfin -#EnvironmentFile = /etc/sysconfig/jellyfin diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.service b/deployment/old/fedora-package-x64/pkg-src/jellyfin.service deleted file mode 100644 index f3dc594b1c..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -After=network.target -Description=Jellyfin is a free software media system that puts you in control of managing and streaming your media. - -[Service] -EnvironmentFile=/etc/sysconfig/jellyfin -WorkingDirectory=/var/lib/jellyfin -ExecStart=/usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} -TimeoutSec=15 -Restart=on-failure -User=jellyfin -Group=jellyfin - -[Install] -WantedBy=multi-user.target diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec deleted file mode 100644 index 33c6f6f648..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec +++ /dev/null @@ -1,181 +0,0 @@ -%global debug_package %{nil} -# Set the dotnet runtime -%if 0%{?fedora} -%global dotnet_runtime fedora-x64 -%else -%global dotnet_runtime centos-x64 -%endif - -Name: jellyfin -Version: 10.5.0 -Release: 1%{?dist} -Summary: The Free Software Media Browser -License: GPLv2 -URL: https://jellyfin.media -# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` -Source0: https://github.com/%{name}/%{name}/archive/%{name}-%{version}.tar.gz -# Jellyfin Webinterface downloaded by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` -Source1: https://github.com/%{name}/%{name}-web/archive/%{name}-web-%{version}.tar.gz -Source11: jellyfin.service -Source12: jellyfin.env -Source13: jellyfin.sudoers -Source14: restart.sh -Source15: jellyfin.override.conf -Source16: jellyfin-firewalld.xml - -%{?systemd_requires} -BuildRequires: systemd -Requires(pre): shadow-utils -BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel, git -%if 0%{?fedora} -BuildRequires: nodejs-yarn, git -%else -# Requirements not packaged in main repos -# From https://rpm.nodesource.com/pub_10.x/el/7/x86_64/ -BuildRequires: nodejs >= 10 yarn -%endif -Requires: libcurl, fontconfig, freetype, openssl, glibc libicu -# Requirements not packaged in main repos -# COPR @dotnet-sig/dotnet or -# https://packages.microsoft.com/rhel/7/prod/ -BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 -# RPMfusion free -Requires: ffmpeg - -# Disable Automatic Dependency Processing -AutoReqProv: no - -%description -Jellyfin is a free software media system that puts you in control of managing and streaming your media. - - -%prep -%autosetup -n %{name}-%{version} -b 0 -b 1 -web_build_dir="$(mktemp -d)" -web_target="$PWD/MediaBrowser.WebDashboard/jellyfin-web" -pushd ../jellyfin-web-%{version} || pushd ../jellyfin-web-master -%if 0%{?fedora} -nodejs-yarn install -%else -yarn install -%endif -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd - -%build - -%install -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \ - "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server -%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE -%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/%{name}.service.d/override.conf -%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/%{name}/logging.json -%{__mkdir} -p %{buildroot}%{_bindir} -tee %{buildroot}%{_bindir}/jellyfin << EOF -#!/bin/sh -exec %{_libdir}/%{name}/%{name} \${@} -EOF -%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin -%{__mkdir} -p %{buildroot}%{_sysconfdir}/%{name} -%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin -%{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin - -%{__install} -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service -%{__install} -D -m 0644 %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/%{name} -%{__install} -D -m 0600 %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers -%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/%{name}/restart.sh -%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/%{name}.xml - -%files -%{_libdir}/%{name}/jellyfin-web/* -%attr(755,root,root) %{_bindir}/%{name} -%{_libdir}/%{name}/*.json -%{_libdir}/%{name}/*.dll -%{_libdir}/%{name}/*.so -%{_libdir}/%{name}/*.a -%{_libdir}/%{name}/createdump -# Needs 755 else only root can run it since binary build by dotnet is 722 -%attr(755,root,root) %{_libdir}/%{name}/jellyfin -%{_libdir}/%{name}/SOS_README.md -%{_unitdir}/%{name}.service -%{_libexecdir}/%{name}/restart.sh -%{_prefix}/lib/firewalld/services/%{name}.xml -%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name} -%config %{_sysconfdir}/sysconfig/%{name} -%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers -%config(noreplace) %{_sysconfdir}/systemd/system/%{name}.service.d/override.conf -%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/%{name}/logging.json -%attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin -%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin -%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin -%if 0%{?fedora} -%license LICENSE -%else -%{_datadir}/licenses/%{name}/LICENSE -%endif - -%pre -getent group jellyfin >/dev/null || groupadd -r jellyfin -getent passwd jellyfin >/dev/null || \ - useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \ - -c "Jellyfin default user" jellyfin -exit 0 - -%post -# Move existing configuration cache and logs to their new locations and symlink them. -if [ $1 -gt 1 ] ; then - service_state=$(systemctl is-active jellyfin.service) - if [ "${service_state}" = "active" ]; then - systemctl stop jellyfin.service - fi - if [ ! -L %{_sharedstatedir}/%{name}/config ]; then - mv %{_sharedstatedir}/%{name}/config/* %{_sysconfdir}/%{name}/ - rmdir %{_sharedstatedir}/%{name}/config - ln -sf %{_sysconfdir}/%{name} %{_sharedstatedir}/%{name}/config - fi - if [ ! -L %{_sharedstatedir}/%{name}/logs ]; then - mv %{_sharedstatedir}/%{name}/logs/* %{_var}/log/jellyfin - rmdir %{_sharedstatedir}/%{name}/logs - ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/%{name}/logs - fi - if [ ! -L %{_sharedstatedir}/%{name}/cache ]; then - mv %{_sharedstatedir}/%{name}/cache/* %{_var}/cache/jellyfin - rmdir %{_sharedstatedir}/%{name}/cache - ln -sf %{_var}/cache/jellyfin %{_sharedstatedir}/%{name}/cache - fi - if [ "${service_state}" = "active" ]; then - systemctl start jellyfin.service - fi -fi -%systemd_post jellyfin.service - -%preun -%systemd_preun jellyfin.service - -%postun -%systemd_postun_with_restart jellyfin.service - -%changelog -* Fri Oct 11 2019 Jellyfin Packaging Team -- New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 -* Sat Aug 31 2019 Jellyfin Packaging Team -- New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 -* Wed Jul 24 2019 Jellyfin Packaging Team -- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 -* Sat Jul 06 2019 Jellyfin Packaging Team -- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 -* Sun Jun 09 2019 Jellyfin Packaging Team -- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 -* Thu Jun 06 2019 Jellyfin Packaging Team -- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 -* Fri May 17 2019 Jellyfin Packaging Team -- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 -* Tue Apr 30 2019 Jellyfin Packaging Team -- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 -* Sat Apr 20 2019 Jellyfin Packaging Team -- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 -* Fri Apr 19 2019 Jellyfin Packaging Team -- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers b/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers deleted file mode 100644 index dd245af4b8..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers +++ /dev/null @@ -1,19 +0,0 @@ -# Allow jellyfin group to start, stop and restart itself -Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin -Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin -Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin - - -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD - -Defaults!RESTARTSERVER_SYSTEMD !requiretty -Defaults!STARTSERVER_SYSTEMD !requiretty -Defaults!STOPSERVER_SYSTEMD !requiretty - -# Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - -Defaults:jellyfin !requiretty diff --git a/deployment/old/fedora-package-x64/pkg-src/restart.sh b/deployment/old/fedora-package-x64/pkg-src/restart.sh deleted file mode 100755 index 9b64b6d728..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/restart.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# restart.sh - Jellyfin server restart script -# Part of the Jellyfin project (https://github.com/jellyfin) -# -# This script restarts the Jellyfin daemon on Linux when using -# the Restart button on the admin dashboard. It supports the -# systemctl, service, and traditional /etc/init.d (sysv) restart -# methods, chosen automatically by which one is found first (in -# that order). -# -# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. - -get_service_command() { - for command in systemctl service; do - if which $command &>/dev/null; then - echo $command && return - fi - done - echo "sysv" -} - -cmd="$( get_service_command )" -echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." -case $cmd in - 'systemctl') - echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now - ;; - 'service') - echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now - ;; - 'sysv') - echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now - ;; -esac -exit 0 diff --git a/deployment/old/linux-x64/Dockerfile b/deployment/old/linux-x64/Dockerfile deleted file mode 100644 index c47057546d..0000000000 --- a/deployment/old/linux-x64/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/linux-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/linux-x64/clean.sh b/deployment/old/linux-x64/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/linux-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/linux-x64/dependencies.txt b/deployment/old/linux-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/linux-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/linux-x64/docker-build.sh b/deployment/old/linux-x64/docker-build.sh deleted file mode 100755 index e33328a36a..0000000000 --- a/deployment/old/linux-x64/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/linux-x64/package.sh b/deployment/old/linux-x64/package.sh deleted file mode 100755 index dfe8a9aa4a..0000000000 --- a/deployment/old/linux-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/macos/Dockerfile b/deployment/old/macos/Dockerfile deleted file mode 100644 index b522df8848..0000000000 --- a/deployment/old/macos/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/macos -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/macos/clean.sh b/deployment/old/macos/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/macos/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/macos/dependencies.txt b/deployment/old/macos/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/macos/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/macos/docker-build.sh b/deployment/old/macos/docker-build.sh deleted file mode 100755 index f9417388d7..0000000000 --- a/deployment/old/macos/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/macos/package.sh b/deployment/old/macos/package.sh deleted file mode 100755 index 464c0d382f..0000000000 --- a/deployment/old/macos/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-macos-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/portable/Dockerfile b/deployment/old/portable/Dockerfile deleted file mode 100644 index 965eb82b86..0000000000 --- a/deployment/old/portable/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/portable -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/portable/clean.sh b/deployment/old/portable/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/portable/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/portable/dependencies.txt b/deployment/old/portable/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/portable/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/portable/docker-build.sh b/deployment/old/portable/docker-build.sh deleted file mode 100755 index 094190bbf6..0000000000 --- a/deployment/old/portable/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/portable/package.sh b/deployment/old/portable/package.sh deleted file mode 100755 index 0ceb54dda1..0000000000 --- a/deployment/old/portable/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-portable-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 deleted file mode 100644 index b11994a18a..0000000000 --- a/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 +++ /dev/null @@ -1,59 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Prepare the cross-toolchain -RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && dpkg --add-architecture arm64 \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64 - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 deleted file mode 100644 index 8f004b2f1a..0000000000 --- a/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=arm64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/clean.sh b/deployment/old/ubuntu-package-arm64/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-arm64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-arm64/dependencies.txt b/deployment/old/ubuntu-package-arm64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-arm64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-arm64/docker-build.sh b/deployment/old/ubuntu-package-arm64/docker-build.sh deleted file mode 100755 index 67ab6bd74b..0000000000 --- a/deployment/old/ubuntu-package-arm64/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarm64 - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-arm64/package.sh b/deployment/old/ubuntu-package-arm64/package.sh deleted file mode 100755 index d1140a7274..0000000000 --- a/deployment/old/ubuntu-package-arm64/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu_arm64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.arm64" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/pkg-src b/deployment/old/ubuntu-package-arm64/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/ubuntu-package-arm64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 deleted file mode 100644 index e475b14389..0000000000 --- a/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 +++ /dev/null @@ -1,59 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Prepare the cross-toolchain -RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && dpkg --add-architecture armhf \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.armhf b/deployment/old/ubuntu-package-armhf/Dockerfile.armhf deleted file mode 100644 index 0e71fa6938..0000000000 --- a/deployment/old/ubuntu-package-armhf/Dockerfile.armhf +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=armhf - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/clean.sh b/deployment/old/ubuntu-package-armhf/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-armhf/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-armhf/dependencies.txt b/deployment/old/ubuntu-package-armhf/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-armhf/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-armhf/docker-build.sh b/deployment/old/ubuntu-package-armhf/docker-build.sh deleted file mode 100755 index 1bd7fb2911..0000000000 --- a/deployment/old/ubuntu-package-armhf/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarmhf - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-armhf/package.sh b/deployment/old/ubuntu-package-armhf/package.sh deleted file mode 100755 index 2ceb3e8165..0000000000 --- a/deployment/old/ubuntu-package-armhf/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu_armhf-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.armhf" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-armhf/pkg-src b/deployment/old/ubuntu-package-armhf/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/ubuntu-package-armhf/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-x64/Dockerfile b/deployment/old/ubuntu-package-x64/Dockerfile deleted file mode 100644 index e2dda6392c..0000000000 --- a/deployment/old/ubuntu-package-x64/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Ubuntu build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 \ - && ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-x64/clean.sh b/deployment/old/ubuntu-package-x64/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-x64/dependencies.txt b/deployment/old/ubuntu-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-x64/docker-build.sh b/deployment/old/ubuntu-package-x64/docker-build.sh deleted file mode 100755 index 962a522ebc..0000000000 --- a/deployment/old/ubuntu-package-x64/docker-build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -dpkg-buildpackage -us -uc - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-x64/package.sh b/deployment/old/ubuntu-package-x64/package.sh deleted file mode 100755 index 08c003778b..0000000000 --- a/deployment/old/ubuntu-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-x64/pkg-src b/deployment/old/ubuntu-package-x64/pkg-src deleted file mode 120000 index 0bb6d55249..0000000000 --- a/deployment/old/ubuntu-package-x64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/unraid/docker-templates/README.md b/deployment/old/unraid/docker-templates/README.md deleted file mode 100644 index 2c268e8b3e..0000000000 --- a/deployment/old/unraid/docker-templates/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# docker-templates - -### Installation: - -Open unRaid GUI (at least unRaid 6.5) - -Click on the Docker tab - -Add the following line under "Template Repositories" - -https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates - -Click save than click on Add Container and select jellyfin. - -Adjust to your paths to your liking and off you go! diff --git a/deployment/old/unraid/docker-templates/jellyfin.xml b/deployment/old/unraid/docker-templates/jellyfin.xml deleted file mode 100644 index 57b4cc5ae1..0000000000 --- a/deployment/old/unraid/docker-templates/jellyfin.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml - False - MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos - Jellyfin - - Jellyfin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br] - You can add as many mount points as needed for recordings, movies ,etc. [br][br] - [b][span style='color: #E80000;']Directions:[/span][/b][br] - [b]/config[/b] : This is where Jellyfin will store it's databases and configuration.[br][br] - [b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br] - [b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point[br][br] - [b]Cache[/b] : This is where Jellyfin will store and manage cached files like images to serve to clients. This is not where all images are stored.[br][br] - [b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it. - - - Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free! - - https://www.reddit.com/r/jellyfin/ - https://hub.docker.com/r/jellyfin/jellyfin/ - https://github.com/jellyfin/jellyfin/> - jellyfin/jellyfin - https://jellyfin.media/ - true - false - - host - - - 8096 - 8096 - tcp - - - - - - /mnt/user/appdata/Jellyfin - /config - rw - - - /mnt/user - /media - rw - - - /mnt/user/appdata/Jellyfin/cache/ - /cache - rw - - - http://[IP]:[PORT:8096]/ - https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/jellyfin-icon.png - - diff --git a/deployment/old/win-x64/Dockerfile b/deployment/old/win-x64/Dockerfile deleted file mode 100644 index 8a33749541..0000000000 --- a/deployment/old/win-x64/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/win-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x64/clean.sh b/deployment/old/win-x64/clean.sh deleted file mode 100755 index 6c183f3371..0000000000 --- a/deployment/old/win-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x64-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/win-x64/dependencies.txt b/deployment/old/win-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/win-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/win-x64/docker-build.sh b/deployment/old/win-x64/docker-build.sh deleted file mode 100755 index 79e5fb0bcd..0000000000 --- a/deployment/old/win-x64/docker-build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Builds the ZIP archive inside the Docker container - -set -o errexit -set -o xtrace - -# Version variables -NSSM_VERSION="nssm-2.24-101-g897c7ad" -NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.2.1-win64-static" -FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip" - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" - -# Prepare addins -addin_build_dir="$( mktemp -d )" -wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip -wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip -unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe -unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe -rm -rf ${addin_build_dir} - -# Prepare scripts -cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 -cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat - -# Create zip package -pushd /dist -zip -r /jellyfin_${version}.portable.zip jellyfin_${version} -popd -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x64/package.sh b/deployment/old/win-x64/package.sh deleted file mode 100755 index a8ab190fa5..0000000000 --- a/deployment/old/win-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/win-x86/Dockerfile b/deployment/old/win-x86/Dockerfile deleted file mode 100644 index f8dc5be83d..0000000000 --- a/deployment/old/win-x86/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/win-x86 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x86/clean.sh b/deployment/old/win-x86/clean.sh deleted file mode 100755 index 8b78c5e4b6..0000000000 --- a/deployment/old/win-x86/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x86-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/win-x86/dependencies.txt b/deployment/old/win-x86/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/win-x86/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/win-x86/docker-build.sh b/deployment/old/win-x86/docker-build.sh deleted file mode 100755 index 977dcf78fa..0000000000 --- a/deployment/old/win-x86/docker-build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Builds the ZIP archive inside the Docker container - -set -o errexit -set -o xtrace - -# Version variables -NSSM_VERSION="nssm-2.24-101-g897c7ad" -NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.2.1-win32-static" -FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip" - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" - -# Prepare addins -addin_build_dir="$( mktemp -d )" -wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip -wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip -unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe -unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe -rm -rf ${addin_build_dir} - -# Prepare scripts -cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 -cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat - -# Create zip package -pushd /dist -zip -r /jellyfin_${version}.portable.zip jellyfin_${version} -popd -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x86/package.sh b/deployment/old/win-x86/package.sh deleted file mode 100755 index 65e7e2928c..0000000000 --- a/deployment/old/win-x86/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x86-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" From cd616746b9ec4c1e07b84e207104bad01c614dae Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 24 Mar 2020 11:17:01 -0400 Subject: [PATCH 057/411] Use more specific mv source glob --- deployment/build.debian.amd64 | 2 +- deployment/build.debian.arm64 | 2 +- deployment/build.debian.armhf | 2 +- deployment/build.ubuntu.amd64 | 2 +- deployment/build.ubuntu.arm64 | 2 +- deployment/build.ubuntu.armhf | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index beaf02bee2..f44c6a7d1d 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -18,7 +18,7 @@ fi dpkg-buildpackage -us -uc --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 6394dddb02..0127671f3d 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -19,7 +19,7 @@ export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index d12660760f..02e3db4fca 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -19,7 +19,7 @@ export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 1b90f68f46..107ddbe02d 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -18,7 +18,7 @@ fi dpkg-buildpackage -us -uc --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index c0a31d764f..b13868f44b 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -19,7 +19,7 @@ export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index e2129357dd..0b4dd308a2 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -19,7 +19,7 @@ export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin[-_]* ${ARTIFACT_DIR}/ +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control From 8094687b9c589038ce87879c51785cd63c8ef8ac Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 30 Mar 2020 02:40:06 -0400 Subject: [PATCH 058/411] Add Debian/Ubuntu metapackage equivs file --- debian/metapackage/jellyfin | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 debian/metapackage/jellyfin diff --git a/debian/metapackage/jellyfin b/debian/metapackage/jellyfin new file mode 100644 index 0000000000..9a41eafaed --- /dev/null +++ b/debian/metapackage/jellyfin @@ -0,0 +1,13 @@ +Source: jellyfin +Section: misc +Priority: optional +Homepage: https://jellyfin.org +Standards-Version: 3.9.2 + +Package: jellyfin +Version: 10.6.0 +Maintainer: Jellyfin Packaging Team +Depends: jellyfin-server, jellyfin-web +Description: Provides the Jellyfin Free Software Media System + Provides the full Jellyfin experience, including both the server and web interface. + From 49da8766d8aac23ab6f6c64a4fee1885b04918f3 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Mon, 30 Mar 2020 02:43:13 -0400 Subject: [PATCH 059/411] Update bump_version script 1. Remove some cruft 2. Update the metapackage as well 3. Changelogs are automated, don't bother EDITOR-ing them --- bump_version | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/bump_version b/bump_version index 106dd7a78e..c868c541e6 100755 --- a/bump_version +++ b/bump_version @@ -10,10 +10,6 @@ usage() { echo -e "" echo -e "Usage:" echo -e " $ bump_version " - echo -e "" - echo -e "The web_branch defaults to the same branch name as the current main branch." - echo -e "This helps facilitate releases where both branches would be called release-X.Y.Z" - echo -e "and would already be created before running this script." } if [[ -z $1 ]]; then @@ -54,37 +50,28 @@ else new_version_deb="${new_version}-1" fi -# Set the Dockerfile web version to the specified new_version -old_version="$( - grep "JELLYFIN_WEB_VERSION=" Dockerfile \ - | sed -E 's/ARG JELLYFIN_WEB_VERSION=v([0-9\.]+[-a-z0-9]*)/\1/' -)" -echo $old_version - -old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars -sed -i "s/${old_version_sed}/${new_version}/g" Dockerfile* +# Update the metapackage equivs file +debian_equivs_file="debian/metapackage/jellyfin" +sed -i "s/${old_version_sed}/${new_version}/g" ${debian_equivs_file} # Write out a temporary Debian changelog with our new stuff appended and some templated formatting -debian_changelog_file="deployment/debian-package-x64/pkg-src/changelog" +debian_changelog_file="debian/changelog" debian_changelog_temp="$( mktemp )" # Create new temp file with our changelog -echo -e "### DEBIAN PACKAGE CHANGELOG: Verify this file looks correct or edit accordingly, then delete this line, write, and exit. -jellyfin (${new_version_deb}) unstable; urgency=medium +echo -e "jellyfin (${new_version_deb}) unstable; urgency=medium * New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v${new_version} -- Jellyfin Packaging Team $( date --rfc-2822 ) " >> ${debian_changelog_temp} cat ${debian_changelog_file} >> ${debian_changelog_temp} -# Edit the file to verify -$EDITOR ${debian_changelog_temp} # Move into place mv ${debian_changelog_temp} ${debian_changelog_file} # Clean up rm -f ${debian_changelog_temp} # Write out a temporary Yum changelog with our new stuff prepended and some templated formatting -fedora_spec_file="deployment/fedora-package-x64/pkg-src/jellyfin.spec" +fedora_spec_file="fedora/jellyfin.spec" fedora_changelog_temp="$( mktemp )" fedora_spec_temp_dir="$( mktemp -d )" fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin.spec.tmp" @@ -98,13 +85,10 @@ sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00 # Remove the header from xx01 sed -i '/^%changelog/d' xx01 # Create new temp file with our changelog -echo -e "### YUM SPEC CHANGELOG: Verify this file looks correct or edit accordingly, then delete this line, write, and exit. -%changelog +echo -e "%changelog * $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team - New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v${new_version}" >> ${fedora_changelog_temp} cat xx01 >> ${fedora_changelog_temp} -# Edit the file to verify -$EDITOR ${fedora_changelog_temp} # Reassembble cat xx00 ${fedora_changelog_temp} > ${fedora_spec_temp} popd @@ -114,5 +98,5 @@ mv ${fedora_spec_temp} ${fedora_spec_file} rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir} # Stage the changed files for commit -git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file} Dockerfile* +git add ${shared_version_file} ${build_file} ${debian_equivs_file} ${debian_changelog_file} ${fedora_spec_file} git status From d10ae74b3827ff8dd8cff4cfa3c0330cf7c7ad3b Mon Sep 17 00:00:00 2001 From: artiume Date: Thu, 2 Apr 2020 14:14:57 -0400 Subject: [PATCH 060/411] Force Audio Transcoding for LiveTV Transcoding Noticing some sync issues when transcoding livetv, the only thing I was able to do to fix it was to force the audio stream to be transcoded as well. This was how I originally wrote the code and we changed it during the review process. I am reverting it back to the original code. --- MediaBrowser.Api/Playback/MediaInfoService.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index d74ec3ca63..040022a3d5 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -520,10 +520,7 @@ namespace MediaBrowser.Api.Playback streamInfo.StartPositionTicks = startTimeTicks; mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; - if (!allowAudioStreamCopy) - { - mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; - } + mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; From 2fb9e36493a2d6be0f2e7a542caf027075892833 Mon Sep 17 00:00:00 2001 From: Didier Dafond <41897414+dafo90@users.noreply.github.com> Date: Thu, 2 Apr 2020 22:02:14 +0200 Subject: [PATCH 061/411] Authentication request log with IP --- Emby.Server.Implementations/Library/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 7b17cc913f..d4d97345b9 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -357,7 +357,7 @@ namespace Emby.Server.Implementations.Library IncrementInvalidLoginAttemptCount(user); } - _logger.LogInformation("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied"); + _logger.LogInformation("Authentication request for {0} {1} (IP: {2}).", user.Name, success ? "has succeeded" : "has been denied", remoteEndPoint); return success ? user : null; } From ca71ac72abf5d5ff31d50553283a43de298e0c73 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Thu, 2 Apr 2020 17:45:04 -0400 Subject: [PATCH 062/411] 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 063/411] 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 499deb4fe591731f4c8542cf8afa8f93e7e68f8a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 3 Apr 2020 20:05:58 -0400 Subject: [PATCH 064/411] Add section describing the repository with a link to contribution docs --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b08723d6a..2e8687bdc3 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,11 @@ Most of the translations can be found in the web client but we have several othe Detailed Translation Status -## Development +## Jellyfin Server + +This repository contains the code for Jellyfin's back-end server. Note that this is only one of many projects/repositories under the Jellyfin GitHub [organization](https://github.com/jellyfin/). If you want to contribute, can start by checking out our [documentation](https://jellyfin.org/docs/general/contributing/index.html) to see what to work on. + +## Server Development These instructions will help you get set up with a local development environment in order to contribute to this repository. Before you start, please be sure to completely read our [guidelines on development contributions](https://jellyfin.org/docs/general/contributing/development.html). Note that this project is supported on all major operating systems (Windows, Mac and Linux). From 7ced986e0b73d45b243ede518f9166464af3de0d Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 3 Apr 2020 20:06:53 -0400 Subject: [PATCH 065/411] Remove section on serving over HTTPS This functionality has not been merged yet --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 2e8687bdc3..de5ea3f301 100644 --- a/README.md +++ b/README.md @@ -155,9 +155,3 @@ It is not necessary to host the frontend web client as part of the backend serve To instruct the server not to host the web content, there is a `nowebcontent` configuration flag that must be set. This can specified using the command line switch `--nowebcontent` or the environment variable `JELLYFIN_NOWEBCONTENT=true`. Since this is a common scenario, there is also a separate launch profile defined for Visual Studio called `Jellyfin.Server (nowebcontent)` that can be selected from the 'Start Debugging' dropdown in the main toolbar. - -#### Serving Over HTTPS - -The .NET Core SDK includes a certificate that can be used to serve content over HTTPS while developing. When running from Visual Studio, VS Code, or using `dotnet run`, this behavior is automatically enabled by setting the environment variable `ASPNETCORE_ENVIRONMENT=Development` and you can access the HTTPS version of the site at https://localhost:8920. - -By default, the development certificate is not trusted so you will see a security warning when you browse to the site over HTTPS. On most browsers you can easily bypass this warning and continue to the site. However, if you want to get rid of the warning, you can configure your machine to trust the development certificate by following the instructions in the [ASP.NET Core documentation](https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl#trust-the-aspnet-core-https-development-certificate-on-windows-and-macos). From e9e12b8eb97947f3387dcfac00fe9aa5e845d9e9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:26:24 -0400 Subject: [PATCH 066/411] Register ISubtitleEncoder correctly --- Emby.Server.Implementations/ApplicationHost.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c959cc974a..27dd0ec1ad 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -845,16 +845,7 @@ namespace Emby.Server.Implementations AuthService = new AuthService(LoggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); serviceCollection.AddSingleton(AuthService); - SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder( - LibraryManager, - LoggerFactory.CreateLogger(), - ApplicationPaths, - FileSystemManager, - MediaEncoder, - HttpClient, - MediaSourceManager, - ProcessFactory); - serviceCollection.AddSingleton(SubtitleEncoder); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); serviceCollection.AddSingleton(); @@ -880,6 +871,7 @@ namespace Emby.Server.Implementations public void InitializeServices() { HttpServer = Resolve(); + SubtitleEncoder = Resolve(); } public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) From 92b0d40ad40cdf0e35ecd1a9e1680fd5b8e6ef2f Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:33:25 -0400 Subject: [PATCH 067/411] Move service initializations into correct method --- .../ApplicationHost.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 27dd0ec1ad..f985320350 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -851,6 +851,16 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); + } + + /// + /// Create services registered with the service container that need to be initialized at application startup. + /// + public void InitializeServices() + { + HttpServer = Resolve(); + AuthService = Resolve(); + SubtitleEncoder = Resolve(); _displayPreferencesRepository.Initialize(); @@ -865,15 +875,6 @@ namespace Emby.Server.Implementations ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; } - /// - /// Create services registered with the service container that need to be initialized at application startup. - /// - public void InitializeServices() - { - HttpServer = Resolve(); - SubtitleEncoder = Resolve(); - } - public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) { // Distinct these to prevent users from reporting problems that aren't actually problems From 314711147181ae26af4857b113b0e2acdcf10931 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:34:01 -0400 Subject: [PATCH 068/411] Register IAuthService correctly --- Emby.Server.Implementations/ApplicationHost.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f985320350..00700a3064 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -842,8 +842,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(authContext); serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(LoggerFactory.CreateLogger(), authContext, ServerConfigurationManager, SessionManager, NetworkManager); - serviceCollection.AddSingleton(AuthService); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); From 358deecf52370ef764b65d56f83a21d0eebcd91a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:38:59 -0400 Subject: [PATCH 069/411] Register ISessionContext correctly --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 00700a3064..a90f14b1f5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -840,7 +840,7 @@ namespace Emby.Server.Implementations var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton(authContext); - serviceCollection.AddSingleton(new SessionContext(UserManager, authContext, SessionManager)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); From 18c1823cead357ada7094675175b20b3399cafcb Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:40:33 -0400 Subject: [PATCH 070/411] Register IAuthorizationContext correctly --- Emby.Server.Implementations/ApplicationHost.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index a90f14b1f5..c177989116 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -838,8 +838,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(activityLogRepo); serviceCollection.AddSingleton(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); - var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); - serviceCollection.AddSingleton(authContext); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); From 3dbbe54f6c94ae279aa74eff3bfa380a665e02a9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:42:21 -0400 Subject: [PATCH 071/411] Register IResourceFileManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c177989116..46fa4d4b4d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -845,7 +845,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); From 4ba07b114d78b042dce111e2345f87b375d8da6e Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:46:35 -0400 Subject: [PATCH 072/411] Register and initialize IActivityRepository correctly --- Emby.Server.Implementations/ApplicationHost.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 46fa4d4b4d..3209ab3b78 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -834,9 +834,8 @@ namespace Emby.Server.Implementations LibraryManager); serviceCollection.AddSingleton(EncodingManager); - var activityLogRepo = GetActivityLogRepository(); - serviceCollection.AddSingleton(activityLogRepo); - serviceCollection.AddSingleton(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -860,6 +859,7 @@ namespace Emby.Server.Implementations AuthService = Resolve(); SubtitleEncoder = Resolve(); + ((ActivityRepository)Resolve()).Initialize(); _displayPreferencesRepository.Initialize(); var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths); @@ -963,15 +963,6 @@ namespace Emby.Server.Implementations return repo; } - private IActivityRepository GetActivityLogRepository() - { - var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); - - repo.Initialize(); - - return repo; - } - /// /// Dirty hacks. /// From 7884c3813d682ca6ae8d7df4a6ba11f61e1d8ae0 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:51:56 -0400 Subject: [PATCH 073/411] Register IEncodingManager correctly; remove unnecessary properties in ApplicationHost --- .../ApplicationHost.cs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3209ab3b78..7d72b8e08b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -282,16 +282,12 @@ namespace Emby.Server.Implementations /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } - private ISubtitleEncoder SubtitleEncoder { get; set; } - private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } public LocalizationManager LocalizationManager { get; set; } - private IEncodingManager EncodingManager { get; set; } - private IChannelManager ChannelManager { get; set; } /// @@ -326,8 +322,6 @@ namespace Emby.Server.Implementations /// The installation manager. protected IInstallationManager InstallationManager { get; private set; } - protected IAuthService AuthService { get; private set; } - public IStartupOptions StartupOptions { get; } internal IImageEncoder ImageEncoder { get; private set; } @@ -740,7 +734,7 @@ namespace Emby.Server.Implementations FileSystemManager, ProcessFactory, LocalizationManager, - () => SubtitleEncoder, + ServiceProvider.GetRequiredService, startupConfig, StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); @@ -826,13 +820,7 @@ namespace Emby.Server.Implementations ChapterManager = new ChapterManager(ItemRepository); serviceCollection.AddSingleton(ChapterManager); - EncodingManager = new MediaEncoder.EncodingManager( - LoggerFactory.CreateLogger(), - FileSystemManager, - MediaEncoder, - ChapterManager, - LibraryManager); - serviceCollection.AddSingleton(EncodingManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -856,8 +844,6 @@ namespace Emby.Server.Implementations public void InitializeServices() { HttpServer = Resolve(); - AuthService = Resolve(); - SubtitleEncoder = Resolve(); ((ActivityRepository)Resolve()).Initialize(); _displayPreferencesRepository.Initialize(); @@ -989,7 +975,7 @@ namespace Emby.Server.Implementations CollectionFolder.XmlSerializer = XmlSerializer; CollectionFolder.JsonSerializer = JsonSerializer; CollectionFolder.ApplicationHost = this; - AuthenticatedAttribute.AuthService = AuthService; + AuthenticatedAttribute.AuthService = ServiceProvider.GetRequiredService(); } private async void PluginInstalled(object sender, GenericEventArgs args) From 78370911c20d83b4f7fc9ca1fb89e5fdd978f0ef Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 12:56:36 -0400 Subject: [PATCH 074/411] Register IDeviceDiscovery, IChapterManager, IAttachmentExtractor correctly --- Emby.Server.Implementations/ApplicationHost.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7d72b8e08b..777c12eaed 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -302,8 +302,6 @@ namespace Emby.Server.Implementations private ISubtitleManager SubtitleManager { get; set; } - private IChapterManager ChapterManager { get; set; } - private IDeviceManager DeviceManager { get; set; } internal IUserViewManager UserViewManager { get; set; } @@ -815,10 +813,9 @@ namespace Emby.Server.Implementations ServerConfigurationManager); serviceCollection.AddSingleton(NotificationManager); - serviceCollection.AddSingleton(new DeviceDiscovery(ServerConfigurationManager)); + serviceCollection.AddSingleton(); - ChapterManager = new ChapterManager(ItemRepository); - serviceCollection.AddSingleton(ChapterManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -835,7 +832,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); + serviceCollection.AddSingleton(); } /// From f1d0fb1edb56e488bb1ef8f7b4ca2cd9b878f312 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 13:03:32 -0400 Subject: [PATCH 075/411] Register INotificationManager correctly; resolve services correctly --- Emby.Server.Implementations/ApplicationHost.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 777c12eaed..0fcfedd6bc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -732,7 +732,7 @@ namespace Emby.Server.Implementations FileSystemManager, ProcessFactory, LocalizationManager, - ServiceProvider.GetRequiredService, + Resolve, startupConfig, StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); @@ -807,11 +807,7 @@ namespace Emby.Server.Implementations UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); serviceCollection.AddSingleton(UserViewManager); - NotificationManager = new NotificationManager( - LoggerFactory.CreateLogger(), - UserManager, - ServerConfigurationManager); - serviceCollection.AddSingleton(NotificationManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -840,6 +836,7 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { + NotificationManager = Resolve(); HttpServer = Resolve(); ((ActivityRepository)Resolve()).Initialize(); @@ -972,7 +969,7 @@ namespace Emby.Server.Implementations CollectionFolder.XmlSerializer = XmlSerializer; CollectionFolder.JsonSerializer = JsonSerializer; CollectionFolder.ApplicationHost = this; - AuthenticatedAttribute.AuthService = ServiceProvider.GetRequiredService(); + AuthenticatedAttribute.AuthService = Resolve(); } private async void PluginInstalled(object sender, GenericEventArgs args) From 14563654113192fbf162c5238c79a0f908587c05 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 13:10:39 -0400 Subject: [PATCH 076/411] Register IUserViewManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0fcfedd6bc..aa1e9ab9d4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -304,8 +304,6 @@ namespace Emby.Server.Implementations private IDeviceManager DeviceManager { get; set; } - internal IUserViewManager UserViewManager { get; set; } - private IAuthenticationRepository AuthenticationRepository { get; set; } private ITVSeriesManager TVSeriesManager { get; set; } @@ -737,7 +735,7 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager, MediaEncoder); + LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, Resolve, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); var musicManager = new MusicManager(LibraryManager); @@ -804,8 +802,7 @@ namespace Emby.Server.Implementations LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); serviceCollection.AddSingleton(LiveTvManager); - UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); - serviceCollection.AddSingleton(UserViewManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -962,7 +959,7 @@ namespace Emby.Server.Implementations BaseItem.UserDataManager = UserDataManager; BaseItem.ChannelManager = ChannelManager; Video.LiveTvManager = LiveTvManager; - Folder.UserViewManager = UserViewManager; + Folder.UserViewManager = Resolve(); UserView.TVSeriesManager = TVSeriesManager; UserView.CollectionManager = CollectionManager; BaseItem.MediaSourceManager = MediaSourceManager; From 3d5b4f869c9edb1e297b89b82221ee837ac6330e Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 13:16:41 -0400 Subject: [PATCH 077/411] Register ILiveTvManager and IPlaylistManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 6 +++--- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index aa1e9ab9d4..94d656e16c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -797,10 +797,9 @@ namespace Emby.Server.Implementations CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); serviceCollection.AddSingleton(CollectionManager); - serviceCollection.AddSingleton(typeof(IPlaylistManager), typeof(PlaylistManager)); + serviceCollection.AddSingleton(); - LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, LoggerFactory, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, FileSystemManager, () => ChannelManager); - serviceCollection.AddSingleton(LiveTvManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -833,6 +832,7 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { + LiveTvManager = Resolve(); NotificationManager = Resolve(); HttpServer = Resolve(); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index b64fe8634c..eabc207867 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -49,7 +49,7 @@ namespace Emby.Server.Implementations.LiveTv private readonly ILibraryManager _libraryManager; private readonly ITaskManager _taskManager; private readonly IJsonSerializer _jsonSerializer; - private readonly Func _channelManager; + private readonly IChannelManager _channelManager; private readonly IDtoService _dtoService; private readonly ILocalizationManager _localization; @@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.LiveTv ILocalizationManager localization, IJsonSerializer jsonSerializer, IFileSystem fileSystem, - Func channelManager) + IChannelManager channelManager) { _config = config; _logger = loggerFactory.CreateLogger(nameof(LiveTvManager)); @@ -2482,7 +2482,7 @@ namespace Emby.Server.Implementations.LiveTv .OrderBy(i => i.SortName) .ToList(); - folders.AddRange(_channelManager().GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery + folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery { UserId = user.Id, IsRecordingsFolder = true, From bb3db9e845208984a9b2093d81642cc0efc844a0 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 13:56:01 -0400 Subject: [PATCH 078/411] Register ISessionManager, IDlnaManager and ICollectionManager correctly; replace private properties with fields --- .../ApplicationHost.cs | 69 +++++++------------ 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 94d656e16c..48414e26e1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,6 +120,10 @@ namespace Emby.Server.Implementations { private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; + private ISessionManager _sessionManager; + private ILiveTvManager _liveTvManager; + private INotificationManager _notificationManager; + private IHttpServer _httpServer; /// /// Gets a value indicating whether this instance can self restart. @@ -266,11 +270,7 @@ namespace Emby.Server.Implementations /// The provider manager. private IProviderManager ProviderManager { get; set; } - /// - /// Gets or sets the HTTP server. - /// - /// The HTTP server. - private IHttpServer HttpServer { get; set; } + private IDtoService DtoService { get; set; } @@ -282,10 +282,6 @@ namespace Emby.Server.Implementations /// The media encoder. private IMediaEncoder MediaEncoder { get; set; } - private ISessionManager SessionManager { get; set; } - - private ILiveTvManager LiveTvManager { get; set; } - public LocalizationManager LocalizationManager { get; set; } private IChannelManager ChannelManager { get; set; } @@ -298,7 +294,7 @@ namespace Emby.Server.Implementations internal SqliteItemRepository ItemRepository { get; set; } - private INotificationManager NotificationManager { get; set; } + private ISubtitleManager SubtitleManager { get; set; } @@ -308,8 +304,6 @@ namespace Emby.Server.Implementations private ITVSeriesManager TVSeriesManager { get; set; } - private ICollectionManager CollectionManager { get; set; } - private IMediaSourceManager MediaSourceManager { get; set; } /// @@ -541,7 +535,7 @@ namespace Emby.Server.Implementations Logger.LogInformation("Executed all pre-startup entry points in {Elapsed:g}", stopWatch.Elapsed); Logger.LogInformation("Core startup complete"); - HttpServer.GlobalResponse = null; + _httpServer.GlobalResponse = null; stopWatch.Restart(); await Task.WhenAll(StartEntryPoints(entryPoints, false)).ConfigureAwait(false); @@ -609,7 +603,7 @@ namespace Emby.Server.Implementations return; } - await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false); + await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false); } public async Task ExecuteHttpHandlerAsync(HttpContext context, Func next) @@ -625,7 +619,7 @@ namespace Emby.Server.Implementations var localPath = context.Request.Path.ToString(); var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger()); - await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); + await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false); } /// @@ -771,31 +765,17 @@ namespace Emby.Server.Implementations ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); serviceCollection.AddSingleton(ProviderManager); - DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); + DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => _liveTvManager); serviceCollection.AddSingleton(DtoService); ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); - SessionManager = new SessionManager( - LoggerFactory.CreateLogger(), - UserDataManager, - LibraryManager, - UserManager, - musicManager, - DtoService, - ImageProcessor, - this, - AuthenticationRepository, - DeviceManager, - MediaSourceManager); - serviceCollection.AddSingleton(SessionManager); + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton( - new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LoggerFactory, JsonSerializer, this)); + serviceCollection.AddSingleton(); - CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LoggerFactory, ProviderManager); - serviceCollection.AddSingleton(CollectionManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -832,9 +812,10 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { - LiveTvManager = Resolve(); - NotificationManager = Resolve(); - HttpServer = Resolve(); + _sessionManager = Resolve(); + _liveTvManager = Resolve(); + _notificationManager = Resolve(); + _httpServer = Resolve(); ((ActivityRepository)Resolve()).Initialize(); _displayPreferencesRepository.Initialize(); @@ -958,10 +939,10 @@ namespace Emby.Server.Implementations BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; BaseItem.ChannelManager = ChannelManager; - Video.LiveTvManager = LiveTvManager; + Video.LiveTvManager = _liveTvManager; Folder.UserViewManager = Resolve(); UserView.TVSeriesManager = TVSeriesManager; - UserView.CollectionManager = CollectionManager; + UserView.CollectionManager = Resolve(); BaseItem.MediaSourceManager = MediaSourceManager; CollectionFolder.XmlSerializer = XmlSerializer; CollectionFolder.JsonSerializer = JsonSerializer; @@ -1024,7 +1005,7 @@ namespace Emby.Server.Implementations .Where(i => i != null) .ToArray(); - HttpServer.Init(GetExportTypes(), GetExports(), GetUrlPrefixes()); + _httpServer.Init(GetExportTypes(), GetExports(), GetUrlPrefixes()); LibraryManager.AddParts( GetExports(), @@ -1040,7 +1021,7 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - LiveTvManager.AddParts(GetExports(), GetExports(), GetExports()); + _liveTvManager.AddParts(GetExports(), GetExports(), GetExports()); SubtitleManager.AddParts(GetExports()); @@ -1048,7 +1029,7 @@ namespace Emby.Server.Implementations MediaSourceManager.AddParts(GetExports()); - NotificationManager.AddParts(GetExports(), GetExports()); + _notificationManager.AddParts(GetExports(), GetExports()); UserManager.AddParts(GetExports(), GetExports()); IsoManager.AddParts(GetExports()); @@ -1194,7 +1175,7 @@ namespace Emby.Server.Implementations } } - if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) + if (!_httpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase)) { requiresRestart = true; } @@ -1254,7 +1235,7 @@ namespace Emby.Server.Implementations { try { - await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -1618,7 +1599,7 @@ namespace Emby.Server.Implementations try { - await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { From f78423bd494d2439191e57705ec4031cb211e4d4 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 14:32:35 -0400 Subject: [PATCH 079/411] Register IChannerManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 48414e26e1..f437390c0e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,6 +120,7 @@ namespace Emby.Server.Implementations { private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; + private IChannelManager _channelManager; private ISessionManager _sessionManager; private ILiveTvManager _liveTvManager; private INotificationManager _notificationManager; @@ -284,7 +285,7 @@ namespace Emby.Server.Implementations public LocalizationManager LocalizationManager { get; set; } - private IChannelManager ChannelManager { get; set; } + /// /// Gets or sets the user data repository. @@ -768,8 +769,7 @@ namespace Emby.Server.Implementations DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => _liveTvManager); serviceCollection.AddSingleton(DtoService); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); - serviceCollection.AddSingleton(ChannelManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -812,6 +812,7 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { + _channelManager = Resolve(); _sessionManager = Resolve(); _liveTvManager = Resolve(); _notificationManager = Resolve(); @@ -938,7 +939,7 @@ namespace Emby.Server.Implementations User.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; - BaseItem.ChannelManager = ChannelManager; + BaseItem.ChannelManager = _channelManager; Video.LiveTvManager = _liveTvManager; Folder.UserViewManager = Resolve(); UserView.TVSeriesManager = TVSeriesManager; @@ -1025,7 +1026,7 @@ namespace Emby.Server.Implementations SubtitleManager.AddParts(GetExports()); - ChannelManager.AddParts(GetExports()); + _channelManager.AddParts(GetExports()); MediaSourceManager.AddParts(GetExports()); From cb2d99e8318d6831b8c7ba80997c172e72a6ee95 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 14:40:04 -0400 Subject: [PATCH 080/411] Construct LiveTvDtoService and LiveTvManager correctly --- .../ApplicationHost.cs | 1 + .../LiveTv/LiveTvDtoService.cs | 5 ++--- .../LiveTv/LiveTvManager.cs | 21 +++++++------------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f437390c0e..e10627ad11 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -779,6 +779,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index 6e903a18ef..55300c9671 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -24,7 +24,6 @@ namespace Emby.Server.Implementations.LiveTv { private readonly ILogger _logger; private readonly IImageProcessor _imageProcessor; - private readonly IDtoService _dtoService; private readonly IApplicationHost _appHost; private readonly ILibraryManager _libraryManager; @@ -32,13 +31,13 @@ namespace Emby.Server.Implementations.LiveTv public LiveTvDtoService( IDtoService dtoService, IImageProcessor imageProcessor, - ILoggerFactory loggerFactory, + ILogger logger, IApplicationHost appHost, ILibraryManager libraryManager) { _dtoService = dtoService; _imageProcessor = imageProcessor; - _logger = loggerFactory.CreateLogger(nameof(LiveTvDtoService)); + _logger = logger; _appHost = appHost; _libraryManager = libraryManager; } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index eabc207867..d8ff8a72fa 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -45,29 +45,24 @@ namespace Emby.Server.Implementations.LiveTv private readonly ILogger _logger; private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; + private readonly IDtoService _dtoService; private readonly IUserDataManager _userDataManager; private readonly ILibraryManager _libraryManager; private readonly ITaskManager _taskManager; + private readonly ILocalizationManager _localization; private readonly IJsonSerializer _jsonSerializer; + private readonly IFileSystem _fileSystem; private readonly IChannelManager _channelManager; - - private readonly IDtoService _dtoService; - private readonly ILocalizationManager _localization; - private readonly LiveTvDtoService _tvDtoService; private ILiveTvService[] _services = Array.Empty(); - private ITunerHost[] _tunerHosts = Array.Empty(); private IListingsProvider[] _listingProviders = Array.Empty(); - private readonly IFileSystem _fileSystem; public LiveTvManager( - IServerApplicationHost appHost, IServerConfigurationManager config, - ILoggerFactory loggerFactory, + ILogger logger, IItemRepository itemRepo, - IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, @@ -76,10 +71,11 @@ namespace Emby.Server.Implementations.LiveTv ILocalizationManager localization, IJsonSerializer jsonSerializer, IFileSystem fileSystem, - IChannelManager channelManager) + IChannelManager channelManager, + LiveTvDtoService liveTvDtoService) { _config = config; - _logger = loggerFactory.CreateLogger(nameof(LiveTvManager)); + _logger = logger; _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; @@ -90,8 +86,7 @@ namespace Emby.Server.Implementations.LiveTv _dtoService = dtoService; _userDataManager = userDataManager; _channelManager = channelManager; - - _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory, appHost, _libraryManager); + _tvDtoService = liveTvDtoService; } public event EventHandler> SeriesTimerCancelled; From 75b05ca1e6dce73c2d419dd83cbc5f9693688faa Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 14:41:03 -0400 Subject: [PATCH 081/411] Register and construct DtoService correctly --- .../ApplicationHost.cs | 11 +++---- Emby.Server.Implementations/Dto/DtoService.cs | 30 ++++++++++--------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e10627ad11..44056dc9e5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -271,10 +271,6 @@ namespace Emby.Server.Implementations /// The provider manager. private IProviderManager ProviderManager { get; set; } - - - private IDtoService DtoService { get; set; } - public IImageProcessor ImageProcessor { get; set; } /// @@ -711,7 +707,7 @@ namespace Emby.Server.Implementations XmlSerializer, NetworkManager, () => ImageProcessor, - () => DtoService, + Resolve, this, JsonSerializer, FileSystemManager, @@ -766,8 +762,9 @@ namespace Emby.Server.Implementations ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); serviceCollection.AddSingleton(ProviderManager); - DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => _liveTvManager); - serviceCollection.AddSingleton(DtoService); + // TODO: Refactor to eliminate circular dependency here so Lazy<> isn't required + serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 65711e89d8..001cfe5dbb 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -38,21 +38,23 @@ namespace Emby.Server.Implementations.Dto private readonly IProviderManager _providerManager; private readonly IApplicationHost _appHost; - private readonly Func _mediaSourceManager; - private readonly Func _livetvManager; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly Lazy _livetvManagerLazy; + + private ILiveTvManager LivetvManager => _livetvManagerLazy.Value; public DtoService( - ILoggerFactory loggerFactory, + ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IProviderManager providerManager, IApplicationHost appHost, - Func mediaSourceManager, - Func livetvManager) + IMediaSourceManager mediaSourceManager, + Lazy livetvManager) { - _logger = loggerFactory.CreateLogger(nameof(DtoService)); + _logger = logger; _libraryManager = libraryManager; _userDataRepository = userDataRepository; _itemRepo = itemRepo; @@ -60,7 +62,7 @@ namespace Emby.Server.Implementations.Dto _providerManager = providerManager; _appHost = appHost; _mediaSourceManager = mediaSourceManager; - _livetvManager = livetvManager; + _livetvManagerLazy = livetvManager; } /// @@ -125,12 +127,12 @@ namespace Emby.Server.Implementations.Dto if (programTuples.Count > 0) { - _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); + LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); } if (channelTuples.Count > 0) { - _livetvManager().AddChannelInfo(channelTuples, options, user); + LivetvManager.AddChannelInfo(channelTuples, options, user); } return returnItems; @@ -142,12 +144,12 @@ namespace Emby.Server.Implementations.Dto if (item is LiveTvChannel tvChannel) { var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) }; - _livetvManager().AddChannelInfo(list, options, user); + LivetvManager.AddChannelInfo(list, options, user); } else if (item is LiveTvProgram) { var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) }; - var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user); + var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user); Task.WaitAll(task); } @@ -223,7 +225,7 @@ namespace Emby.Server.Implementations.Dto if (item is IHasMediaSources && options.ContainsField(ItemFields.MediaSources)) { - dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(item, true, user).ToArray(); + dto.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray(); NormalizeMediaSourceContainers(dto); } @@ -254,7 +256,7 @@ namespace Emby.Server.Implementations.Dto dto.Etag = item.GetEtag(user); } - var liveTvManager = _livetvManager(); + var liveTvManager = LivetvManager; var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path); if (activeRecording != null) { @@ -1045,7 +1047,7 @@ namespace Emby.Server.Implementations.Dto } else { - mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray(); + mediaStreams = _mediaSourceManager.GetStaticMediaSources(item, true)[0].MediaStreams.ToArray(); } dto.MediaStreams = mediaStreams; From 51b9a6e94b06db6ae4b21cf2372287a98778a075 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 14:56:50 -0400 Subject: [PATCH 082/411] Register IProviderManager correctly --- .../ApplicationHost.cs | 15 ++-- Emby.Server.Implementations/Dto/DtoService.cs | 8 +-- .../Manager/ProviderManager.cs | 71 ++++++++----------- 3 files changed, 39 insertions(+), 55 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 44056dc9e5..8a3398e01a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -265,12 +265,6 @@ namespace Emby.Server.Implementations /// The directory watchers. private ILibraryMonitor LibraryMonitor { get; set; } - /// - /// Gets or sets the provider manager. - /// - /// The provider manager. - private IProviderManager ProviderManager { get; set; } - public IImageProcessor ImageProcessor { get; set; } /// @@ -726,7 +720,7 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, Resolve, MediaEncoder); + LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, Resolve, Resolve, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); var musicManager = new MusicManager(LibraryManager); @@ -759,8 +753,7 @@ namespace Emby.Server.Implementations SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); serviceCollection.AddSingleton(SubtitleManager); - ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LoggerFactory, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); - serviceCollection.AddSingleton(ProviderManager); + serviceCollection.AddSingleton(); // TODO: Refactor to eliminate circular dependency here so Lazy<> isn't required serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); @@ -931,7 +924,7 @@ namespace Emby.Server.Implementations BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; - BaseItem.ProviderManager = ProviderManager; + BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = ItemRepository; User.UserManager = UserManager; @@ -1013,7 +1006,7 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - ProviderManager.AddParts( + Resolve().AddParts( GetExports(), GetExports(), GetExports(), diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 001cfe5dbb..c8ea861ce4 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -39,9 +39,9 @@ namespace Emby.Server.Implementations.Dto private readonly IApplicationHost _appHost; private readonly IMediaSourceManager _mediaSourceManager; - private readonly Lazy _livetvManagerLazy; + private readonly Lazy _livetvManagerFactory; - private ILiveTvManager LivetvManager => _livetvManagerLazy.Value; + private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; public DtoService( ILogger logger, @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Dto IProviderManager providerManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, - Lazy livetvManager) + Lazy livetvManagerFactory) { _logger = logger; _libraryManager = libraryManager; @@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Dto _providerManager = providerManager; _appHost = appHost; _mediaSourceManager = mediaSourceManager; - _livetvManagerLazy = livetvManager; + _livetvManagerFactory = livetvManagerFactory; } /// diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 7125f34c55..337bc37eb9 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -36,60 +36,51 @@ namespace MediaBrowser.Providers.Manager /// public class ProviderManager : IProviderManager, IDisposable { - /// - /// The _logger - /// private readonly ILogger _logger; - - /// - /// The _HTTP client - /// private readonly IHttpClient _httpClient; - - /// - /// The _directory watchers - /// private readonly ILibraryMonitor _libraryMonitor; - - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IServerConfigurationManager ConfigurationManager { get; set; } + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationPaths _appPaths; + private readonly IJsonSerializer _json; + private readonly ILibraryManager _libraryManager; + private readonly ISubtitleManager _subtitleManager; + private readonly IServerConfigurationManager _configurationManager; private IImageProvider[] ImageProviders { get; set; } - private readonly IFileSystem _fileSystem; - private IMetadataService[] _metadataServices = { }; private IMetadataProvider[] _metadataProviders = { }; private IEnumerable _savers; - private readonly IServerApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private IExternalId[] _externalIds; - private readonly Func _libraryManagerFactory; private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); public event EventHandler> RefreshStarted; public event EventHandler> RefreshCompleted; public event EventHandler>> RefreshProgress; - private ISubtitleManager _subtitleManager; - /// /// Initializes a new instance of the class. /// - public ProviderManager(IHttpClient httpClient, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILoggerFactory loggerFactory, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func libraryManagerFactory, IJsonSerializer json) + public ProviderManager( + IHttpClient httpClient, + ISubtitleManager subtitleManager, + IServerConfigurationManager configurationManager, + ILibraryMonitor libraryMonitor, + ILogger logger, + IFileSystem fileSystem, + IServerApplicationPaths appPaths, + ILibraryManager libraryManager, + IJsonSerializer json) { - _logger = loggerFactory.CreateLogger("ProviderManager"); + _logger = logger; _httpClient = httpClient; - ConfigurationManager = configurationManager; + _configurationManager = configurationManager; _libraryMonitor = libraryMonitor; _fileSystem = fileSystem; _appPaths = appPaths; - _libraryManagerFactory = libraryManagerFactory; + _libraryManager = libraryManager; _json = json; _subtitleManager = subtitleManager; } @@ -176,7 +167,7 @@ namespace MediaBrowser.Providers.Manager public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) { - return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); + return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); } public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken) @@ -188,7 +179,7 @@ namespace MediaBrowser.Providers.Manager var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true); - return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); + return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); } public async Task> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken) @@ -273,7 +264,7 @@ namespace MediaBrowser.Providers.Manager public IEnumerable GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions) { - return GetImageProviders(item, _libraryManagerFactory().GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); + return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); } private IEnumerable GetImageProviders(BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) @@ -328,7 +319,7 @@ namespace MediaBrowser.Providers.Manager private IEnumerable GetRemoteImageProviders(BaseItem item, bool includeDisabled) { var options = GetMetadataOptions(item); - var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item); + var libraryOptions = _libraryManager.GetLibraryOptions(item); return GetImageProviders(item, libraryOptions, options, new ImageRefreshOptions( @@ -593,7 +584,7 @@ namespace MediaBrowser.Providers.Manager { var type = item.GetType().Name; - return ConfigurationManager.Configuration.MetadataOptions + return _configurationManager.Configuration.MetadataOptions .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ?? new MetadataOptions(); } @@ -623,7 +614,7 @@ namespace MediaBrowser.Providers.Manager /// Task. private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable savers) { - var libraryOptions = _libraryManagerFactory().GetLibraryOptions(item); + var libraryOptions = _libraryManager.GetLibraryOptions(item); foreach (var saver in savers.Where(i => IsSaverEnabledForItem(i, item, libraryOptions, updateType, false))) { @@ -743,7 +734,7 @@ namespace MediaBrowser.Providers.Manager if (!searchInfo.ItemId.Equals(Guid.Empty)) { - referenceItem = _libraryManagerFactory().GetItemById(searchInfo.ItemId); + referenceItem = _libraryManager.GetItemById(searchInfo.ItemId); } return GetRemoteSearchResults(searchInfo, referenceItem, cancellationToken); @@ -771,7 +762,7 @@ namespace MediaBrowser.Providers.Manager } else { - libraryOptions = _libraryManagerFactory().GetLibraryOptions(referenceItem); + libraryOptions = _libraryManager.GetLibraryOptions(referenceItem); } var options = GetMetadataOptions(referenceItem); @@ -786,11 +777,11 @@ namespace MediaBrowser.Providers.Manager if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataLanguage)) { - searchInfo.SearchInfo.MetadataLanguage = ConfigurationManager.Configuration.PreferredMetadataLanguage; + searchInfo.SearchInfo.MetadataLanguage = _configurationManager.Configuration.PreferredMetadataLanguage; } if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode)) { - searchInfo.SearchInfo.MetadataCountryCode = ConfigurationManager.Configuration.MetadataCountryCode; + searchInfo.SearchInfo.MetadataCountryCode = _configurationManager.Configuration.MetadataCountryCode; } var resultList = new List(); @@ -1010,7 +1001,7 @@ namespace MediaBrowser.Providers.Manager private async Task StartProcessingRefreshQueue() { - var libraryManager = _libraryManagerFactory(); + var libraryManager = _libraryManager; if (_disposed) { @@ -1088,7 +1079,7 @@ namespace MediaBrowser.Providers.Manager private async Task RefreshArtist(MusicArtist item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - var albums = _libraryManagerFactory() + var albums = _libraryManager .GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { nameof(MusicAlbum) }, From 0ce82ab33288727fcbb400d72db62df440ef104d Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:05:50 -0400 Subject: [PATCH 083/411] Remove unnecessary fields in ApplicationHost --- .../ApplicationHost.cs | 21 ++++++++----------- Jellyfin.Server/Program.cs | 1 - 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8a3398e01a..81736f053d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,9 +120,7 @@ namespace Emby.Server.Implementations { private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; - private IChannelManager _channelManager; private ISessionManager _sessionManager; - private ILiveTvManager _liveTvManager; private INotificationManager _notificationManager; private IHttpServer _httpServer; @@ -803,10 +801,7 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { - _channelManager = Resolve(); _sessionManager = Resolve(); - _liveTvManager = Resolve(); - _notificationManager = Resolve(); _httpServer = Resolve(); ((ActivityRepository)Resolve()).Initialize(); @@ -821,6 +816,8 @@ namespace Emby.Server.Implementations ((UserDataManager)UserDataManager).Repository = userDataRepo; ItemRepository.Initialize(userDataRepo, UserManager); ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; + + FindParts(); } public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) @@ -930,8 +927,8 @@ namespace Emby.Server.Implementations User.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; - BaseItem.ChannelManager = _channelManager; - Video.LiveTvManager = _liveTvManager; + BaseItem.ChannelManager = Resolve(); + Video.LiveTvManager = Resolve(); Folder.UserViewManager = Resolve(); UserView.TVSeriesManager = TVSeriesManager; UserView.CollectionManager = Resolve(); @@ -978,9 +975,9 @@ namespace Emby.Server.Implementations } /// - /// Finds the parts. + /// Finds plugin components and register them with the appropriate services. /// - public void FindParts() + private void FindParts() { InstallationManager = ServiceProvider.GetService(); InstallationManager.PluginInstalled += PluginInstalled; @@ -1013,15 +1010,15 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - _liveTvManager.AddParts(GetExports(), GetExports(), GetExports()); + Resolve().AddParts(GetExports(), GetExports(), GetExports()); SubtitleManager.AddParts(GetExports()); - _channelManager.AddParts(GetExports()); + Resolve().AddParts(GetExports()); MediaSourceManager.AddParts(GetExports()); - _notificationManager.AddParts(GetExports(), GetExports()); + Resolve().AddParts(GetExports(), GetExports()); UserManager.AddParts(GetExports(), GetExports()); IsoManager.AddParts(GetExports()); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 4abdd59aaf..6be9ba4f2d 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -210,7 +210,6 @@ namespace Jellyfin.Server // 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; appHost.InitializeServices(); - appHost.FindParts(); Migrations.MigrationRunner.Run(appHost, _loggerFactory); try From 3d8b81039d0f735a893fde8e0bef4bdfefd2db8a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:08:04 -0400 Subject: [PATCH 084/411] Log refresh progress at Debug level --- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 337bc37eb9..cfff897672 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -958,7 +958,7 @@ namespace MediaBrowser.Providers.Manager public void OnRefreshProgress(BaseItem item, double progress) { var id = item.Id; - _logger.LogInformation("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress); + _logger.LogDebug("OnRefreshProgress {0} {1}", id.ToString("N", CultureInfo.InvariantCulture), progress); // TODO: Need to hunt down the conditions for this happening _activeRefreshes.AddOrUpdate( From dd5a55aeba0ad90c277d0acb243558e3a37d2506 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:12:02 -0400 Subject: [PATCH 085/411] Register ISubtitleManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 10 ++-------- MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 81736f053d..6db832b798 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -121,7 +121,6 @@ namespace Emby.Server.Implementations private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; private ISessionManager _sessionManager; - private INotificationManager _notificationManager; private IHttpServer _httpServer; /// @@ -283,10 +282,6 @@ namespace Emby.Server.Implementations internal SqliteItemRepository ItemRepository { get; set; } - - - private ISubtitleManager SubtitleManager { get; set; } - private IDeviceManager DeviceManager { get; set; } private IAuthenticationRepository AuthenticationRepository { get; set; } @@ -748,8 +743,7 @@ namespace Emby.Server.Implementations MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); serviceCollection.AddSingleton(MediaSourceManager); - SubtitleManager = new SubtitleManager(LoggerFactory, FileSystemManager, LibraryMonitor, MediaSourceManager, LocalizationManager); - serviceCollection.AddSingleton(SubtitleManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -1012,7 +1006,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); - SubtitleManager.AddParts(GetExports()); + Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 583c7e8ea4..127d29c04e 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -25,22 +25,22 @@ namespace MediaBrowser.Providers.Subtitles { public class SubtitleManager : ISubtitleManager { - private ISubtitleProvider[] _subtitleProviders; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _monitor; private readonly IMediaSourceManager _mediaSourceManager; + private readonly ILocalizationManager _localization; - private ILocalizationManager _localization; + private ISubtitleProvider[] _subtitleProviders; public SubtitleManager( - ILoggerFactory loggerFactory, + ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, ILocalizationManager localizationManager) { - _logger = loggerFactory.CreateLogger(nameof(SubtitleManager)); + _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _mediaSourceManager = mediaSourceManager; From 573da63d41e47af7cdd54cb1f5017b0317a6ba64 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:28:21 -0400 Subject: [PATCH 086/411] Register and construct IMediaSourceManager correctly --- .../ApplicationHost.cs | 11 ++++------ .../Library/MediaSourceManager.cs | 22 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6db832b798..0f1b24bbc2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -288,8 +288,6 @@ namespace Emby.Server.Implementations private ITVSeriesManager TVSeriesManager { get; set; } - private IMediaSourceManager MediaSourceManager { get; set; } - /// /// Gets the installation manager. /// @@ -740,14 +738,13 @@ namespace Emby.Server.Implementations DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); serviceCollection.AddSingleton(DeviceManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LoggerFactory, JsonSerializer, FileSystemManager, UserDataManager, () => MediaEncoder); - serviceCollection.AddSingleton(MediaSourceManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - // TODO: Refactor to eliminate circular dependency here so Lazy<> isn't required + // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddSingleton(); @@ -926,7 +923,7 @@ namespace Emby.Server.Implementations Folder.UserViewManager = Resolve(); UserView.TVSeriesManager = TVSeriesManager; UserView.CollectionManager = Resolve(); - BaseItem.MediaSourceManager = MediaSourceManager; + BaseItem.MediaSourceManager = Resolve(); CollectionFolder.XmlSerializer = XmlSerializer; CollectionFolder.JsonSerializer = JsonSerializer; CollectionFolder.ApplicationHost = this; @@ -1010,7 +1007,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports()); - MediaSourceManager.AddParts(GetExports()); + Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports(), GetExports()); UserManager.AddParts(GetExports(), GetExports()); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 70d5bd9f4e..01fe98f3af 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -33,13 +33,13 @@ namespace Emby.Server.Implementations.Library private readonly ILibraryManager _libraryManager; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; - - private IMediaSourceProvider[] _providers; private readonly ILogger _logger; private readonly IUserDataManager _userDataManager; - private readonly Func _mediaEncoder; - private ILocalizationManager _localizationManager; - private IApplicationPaths _appPaths; + private readonly IMediaEncoder _mediaEncoder; + private readonly ILocalizationManager _localizationManager; + private readonly IApplicationPaths _appPaths; + + private IMediaSourceProvider[] _providers; public MediaSourceManager( IItemRepository itemRepo, @@ -47,16 +47,16 @@ namespace Emby.Server.Implementations.Library ILocalizationManager localizationManager, IUserManager userManager, ILibraryManager libraryManager, - ILoggerFactory loggerFactory, + ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager, - Func mediaEncoder) + IMediaEncoder mediaEncoder) { _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; - _logger = loggerFactory.CreateLogger(nameof(MediaSourceManager)); + _logger = logger; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; _userDataManager = userDataManager; @@ -496,7 +496,7 @@ namespace Emby.Server.Implementations.Library // hack - these two values were taken from LiveTVMediaSourceProvider string cacheKey = request.OpenToken; - await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths) + await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _appPaths) .AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken) .ConfigureAwait(false); } @@ -621,7 +621,7 @@ namespace Emby.Server.Implementations.Library if (liveStreamInfo is IDirectStreamProvider) { - var info = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest + var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest { MediaSource = mediaSource, ExtractChapters = false, @@ -674,7 +674,7 @@ namespace Emby.Server.Implementations.Library mediaSource.AnalyzeDurationMs = 3000; } - mediaInfo = await _mediaEncoder().GetMediaInfo(new MediaInfoRequest + mediaInfo = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest { MediaSource = mediaSource, MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, From 7b31b0e322332690fa9bda7d16a5f9a0cdeffa8d Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:33:23 -0400 Subject: [PATCH 087/411] Inject logger correctly into ActivityManager and ActivityRepository --- Emby.Server.Implementations/Activity/ActivityManager.cs | 4 ++-- Emby.Server.Implementations/Activity/ActivityRepository.cs | 4 ++-- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index ee10845cfa..34ccd4bba1 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -18,11 +18,11 @@ namespace Emby.Server.Implementations.Activity private readonly IUserManager _userManager; public ActivityManager( - ILoggerFactory loggerFactory, + ILogger logger, IActivityRepository repo, IUserManager userManager) { - _logger = loggerFactory.CreateLogger(nameof(ActivityManager)); + _logger = logger; _repo = repo; _userManager = userManager; } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 7be72319ea..0fd2625217 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -20,8 +20,8 @@ namespace Emby.Server.Implementations.Activity private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); private readonly IFileSystem _fileSystem; - public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) - : base(loggerFactory.CreateLogger(nameof(ActivityRepository))) + public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) + : base(logger) { DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); _fileSystem = fileSystem; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index ce576a6c3b..42379c73bb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -471,7 +471,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); var outputVideoCodec = GetVideoEncoder(state, encodingOptions); - + var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; if (!hasTextSubs) From 71c84905de8f1c303c991b240d22a2f9616965e8 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:40:06 -0400 Subject: [PATCH 088/411] Register IDeviceManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 5 +---- Emby.Server.Implementations/Devices/DeviceManager.cs | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0f1b24bbc2..9d4964a9e4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -282,8 +282,6 @@ namespace Emby.Server.Implementations internal SqliteItemRepository ItemRepository { get; set; } - private IDeviceManager DeviceManager { get; set; } - private IAuthenticationRepository AuthenticationRepository { get; set; } private ITVSeriesManager TVSeriesManager { get; set; } @@ -735,8 +733,7 @@ namespace Emby.Server.Implementations TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); serviceCollection.AddSingleton(TVSeriesManager); - DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager); - serviceCollection.AddSingleton(DeviceManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index adb8e793d2..579cb895e4 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -38,10 +38,11 @@ namespace Emby.Server.Implementations.Devices private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; - private readonly IAuthenticationRepository _authRepo; + private readonly Dictionary _capabilitiesCache; public event EventHandler>> DeviceOptionsUpdated; + public event EventHandler> CameraImageUploaded; private readonly object _cameraUploadSyncLock = new object(); @@ -65,10 +66,9 @@ namespace Emby.Server.Implementations.Devices _libraryManager = libraryManager; _localizationManager = localizationManager; _authRepo = authRepo; + _capabilitiesCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } - - private Dictionary _capabilitiesCache = new Dictionary(StringComparer.OrdinalIgnoreCase); public void SaveCapabilities(string deviceId, ClientCapabilities capabilities) { var path = Path.Combine(GetDevicePath(deviceId), "capabilities.json"); From 11693d6024f4b8dafb69ac4a4fcb85ee2caad065 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 15:44:44 -0400 Subject: [PATCH 089/411] Register ITvManagerService correctly --- Emby.Server.Implementations/ApplicationHost.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9d4964a9e4..c5b8878f84 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -272,8 +272,6 @@ namespace Emby.Server.Implementations public LocalizationManager LocalizationManager { get; set; } - - /// /// Gets or sets the user data repository. /// @@ -284,8 +282,6 @@ namespace Emby.Server.Implementations private IAuthenticationRepository AuthenticationRepository { get; set; } - private ITVSeriesManager TVSeriesManager { get; set; } - /// /// Gets the installation manager. /// @@ -730,8 +726,7 @@ namespace Emby.Server.Implementations ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); - TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); - serviceCollection.AddSingleton(TVSeriesManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -918,7 +913,7 @@ namespace Emby.Server.Implementations BaseItem.ChannelManager = Resolve(); Video.LiveTvManager = Resolve(); Folder.UserViewManager = Resolve(); - UserView.TVSeriesManager = TVSeriesManager; + UserView.TVSeriesManager = Resolve(); UserView.CollectionManager = Resolve(); BaseItem.MediaSourceManager = Resolve(); CollectionFolder.XmlSerializer = XmlSerializer; From efe3ebaab8fbb064652ac4923297f315e4a798e7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 16:01:10 -0400 Subject: [PATCH 090/411] Eliminate circular dependency between LibraryManager and ImageProcessor --- Emby.Drawing/ImageProcessor.cs | 25 ------------------- Emby.Photos/PhotoProvider.cs | 2 +- .../ApplicationHost.cs | 2 +- MediaBrowser.Api/Images/ImageService.cs | 9 ++++++- .../Drawing/IImageProcessor.cs | 9 ------- 5 files changed, 10 insertions(+), 37 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index eca4b56eb9..a9cab147c8 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -33,7 +33,6 @@ namespace Emby.Drawing private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; private readonly IImageEncoder _imageEncoder; - private readonly Func _libraryManager; private readonly Func _mediaEncoder; private bool _disposed = false; @@ -45,20 +44,17 @@ namespace Emby.Drawing /// The server application paths. /// The filesystem. /// The image encoder. - /// The library manager. /// The media encoder. public ImageProcessor( ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IImageEncoder imageEncoder, - Func libraryManager, Func mediaEncoder) { _logger = logger; _fileSystem = fileSystem; _imageEncoder = imageEncoder; - _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; _appPaths = appPaths; } @@ -126,21 +122,9 @@ namespace Emby.Drawing throw new ArgumentNullException(nameof(options)); } - var libraryManager = _libraryManager(); - ItemImageInfo originalImage = options.Image; BaseItem item = options.Item; - if (!originalImage.IsLocalFile) - { - if (item == null) - { - item = libraryManager.GetItemById(options.ItemId); - } - - originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false); - } - string originalImagePath = originalImage.Path; DateTime dateModified = originalImage.DateModified; ImageDimensions? originalImageSize = null; @@ -312,10 +296,6 @@ namespace Emby.Drawing /// public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info) - => GetImageDimensions(item, info, true); - - /// - public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem) { int width = info.Width; int height = info.Height; @@ -332,11 +312,6 @@ namespace Emby.Drawing info.Width = size.Width; info.Height = size.Height; - if (updateItem) - { - _libraryManager().UpdateImages(item); - } - return size; } diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs index 63631e512b..987cb7fb2a 100644 --- a/Emby.Photos/PhotoProvider.cs +++ b/Emby.Photos/PhotoProvider.cs @@ -160,7 +160,7 @@ namespace Emby.Photos try { - var size = _imageProcessor.GetImageDimensions(item, img, false); + var size = _imageProcessor.GetImageDimensions(item, img); if (size.Width > 0 && size.Height > 0) { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c5b8878f84..a6ad005e0d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -723,7 +723,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder); + ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => MediaEncoder); serviceCollection.AddSingleton(ImageProcessor); serviceCollection.AddSingleton(); diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index af455987bc..c11e0c1b7e 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -332,7 +332,8 @@ namespace MediaBrowser.Api.Images var fileInfo = _fileSystem.GetFileInfo(info.Path); length = fileInfo.Length; - ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true); + ImageDimensions size = _imageProcessor.GetImageDimensions(item, info); + _libraryManager.UpdateImages(item); width = size.Width; height = size.Height; @@ -606,6 +607,12 @@ namespace MediaBrowser.Api.Images IDictionary headers, bool isHeadRequest) { + if (!image.IsLocalFile) + { + item ??= _libraryManager.GetItemById(itemId); + image = await _libraryManager.ConvertImageToLocal(item, image, request.Index ?? 0).ConfigureAwait(false); + } + var options = new ImageProcessingOptions { CropWhiteSpace = cropwhitespace, diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 79399807fd..36c746624e 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -40,15 +40,6 @@ namespace MediaBrowser.Controller.Drawing /// ImageDimensions ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info); - /// - /// Gets the dimensions of the image. - /// - /// The base item. - /// The information. - /// Whether or not the item info should be updated. - /// ImageDimensions - ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem); - /// /// Gets the image cache tag. /// From 07cebbeae2bbbe63f0f97428e73e419220ab5804 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 17:12:24 -0400 Subject: [PATCH 091/411] Register and construct IImageProcessor, SqliteItemRepository and IImageEncoder correctly --- Emby.Drawing/ImageProcessor.cs | 8 ++--- .../ApplicationHost.cs | 33 ++++++++----------- .../Data/SqliteItemRepository.cs | 13 ++++---- .../Emby.Server.Implementations.csproj | 1 + Jellyfin.Drawing.Skia/SkiaEncoder.cs | 17 +++++++--- Jellyfin.Server/CoreAppHost.cs | 3 -- Jellyfin.Server/Program.cs | 20 ----------- 7 files changed, 38 insertions(+), 57 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index a9cab147c8..903b958a4f 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -33,7 +33,7 @@ namespace Emby.Drawing private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; private readonly IImageEncoder _imageEncoder; - private readonly Func _mediaEncoder; + private readonly IMediaEncoder _mediaEncoder; private bool _disposed = false; @@ -50,7 +50,7 @@ namespace Emby.Drawing IServerApplicationPaths appPaths, IFileSystem fileSystem, IImageEncoder imageEncoder, - Func mediaEncoder) + IMediaEncoder mediaEncoder) { _logger = logger; _fileSystem = fileSystem; @@ -359,13 +359,13 @@ namespace Emby.Drawing { string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); - string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png"; + string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png"; var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); var file = _fileSystem.GetFileInfo(outputPath); if (!file.Exists) { - await _mediaEncoder().ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); + await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath); } else diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index a6ad005e0d..ff4fb54b55 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -48,6 +48,7 @@ using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; +using Jellyfin.Drawing.Skia; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -262,8 +263,6 @@ namespace Emby.Server.Implementations /// The directory watchers. private ILibraryMonitor LibraryMonitor { get; set; } - public IImageProcessor ImageProcessor { get; set; } - /// /// Gets or sets the media encoder. /// @@ -278,8 +277,6 @@ namespace Emby.Server.Implementations /// The user data repository. private IUserDataManager UserDataManager { get; set; } - internal SqliteItemRepository ItemRepository { get; set; } - private IAuthenticationRepository AuthenticationRepository { get; set; } /// @@ -290,8 +287,6 @@ namespace Emby.Server.Implementations public IStartupOptions StartupOptions { get; } - internal IImageEncoder ImageEncoder { get; private set; } - protected IProcessFactory ProcessFactory { get; private set; } protected readonly IXmlSerializer XmlSerializer; @@ -316,7 +311,6 @@ namespace Emby.Server.Implementations ILoggerFactory loggerFactory, IStartupOptions options, IFileSystem fileSystem, - IImageEncoder imageEncoder, INetworkManager networkManager) { XmlSerializer = new MyXmlSerializer(); @@ -334,8 +328,6 @@ namespace Emby.Server.Implementations StartupOptions = options; - ImageEncoder = imageEncoder; - fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); NetworkManager.NetworkChanged += OnNetworkChanged; @@ -603,6 +595,11 @@ namespace Emby.Server.Implementations /// protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig) { + var imageEncoderType = SkiaEncoder.IsNativeLibAvailable() + ? typeof(SkiaEncoder) + : typeof(NullImageEncoder); + serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType); + serviceCollection.AddMemoryCache(); serviceCollection.AddSingleton(ConfigurationManager); @@ -672,8 +669,7 @@ namespace Emby.Server.Implementations FileSystemManager); serviceCollection.AddSingleton(_displayPreferencesRepository); - ItemRepository = new SqliteItemRepository(ServerConfigurationManager, this, LoggerFactory.CreateLogger(), LocalizationManager); - serviceCollection.AddSingleton(ItemRepository); + serviceCollection.AddSingleton(); AuthenticationRepository = GetAuthenticationRepository(); serviceCollection.AddSingleton(AuthenticationRepository); @@ -685,7 +681,7 @@ namespace Emby.Server.Implementations _userRepository, XmlSerializer, NetworkManager, - () => ImageProcessor, + Resolve, Resolve, this, JsonSerializer, @@ -723,8 +719,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - ImageProcessor = new ImageProcessor(LoggerFactory.CreateLogger(), ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => MediaEncoder); - serviceCollection.AddSingleton(ImageProcessor); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -797,8 +792,10 @@ namespace Emby.Server.Implementations ((UserManager)UserManager).Initialize(); ((UserDataManager)UserDataManager).Repository = userDataRepo; - ItemRepository.Initialize(userDataRepo, UserManager); - ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; + + var itemRepo = (SqliteItemRepository)Resolve(); + itemRepo.Initialize(userDataRepo, UserManager); + ((LibraryManager)LibraryManager).ItemRepository = itemRepo; FindParts(); } @@ -898,15 +895,13 @@ namespace Emby.Server.Implementations /// private void SetStaticProperties() { - ItemRepository.ImageProcessor = ImageProcessor; - // For now there's no real way to inject these properly BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = LibraryManager; BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = LocalizationManager; - BaseItem.ItemRepository = ItemRepository; + BaseItem.ItemRepository = Resolve(); User.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e3242f7b4f..227f1a5e7e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -39,12 +39,11 @@ namespace Emby.Server.Implementations.Data { private const string ChaptersTableName = "Chapters2"; - /// - /// The _app paths - /// private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; private readonly ILocalizationManager _localization; + // TODO: Remove this dependency + private readonly IImageProcessor _imageProcessor; private readonly TypeMapper _typeMapper; private readonly JsonSerializerOptions _jsonOptions; @@ -71,7 +70,8 @@ namespace Emby.Server.Implementations.Data IServerConfigurationManager config, IServerApplicationHost appHost, ILogger logger, - ILocalizationManager localization) + ILocalizationManager localization, + IImageProcessor imageProcessor) : base(logger) { if (config == null) @@ -82,6 +82,7 @@ namespace Emby.Server.Implementations.Data _config = config; _appHost = appHost; _localization = localization; + _imageProcessor = imageProcessor; _typeMapper = new TypeMapper(); _jsonOptions = JsonDefaults.GetOptions(); @@ -98,8 +99,6 @@ namespace Emby.Server.Implementations.Data /// protected override TempStoreMode TempStore => TempStoreMode.Memory; - public IImageProcessor ImageProcessor { get; set; } - /// /// Opens the connection to the database /// @@ -1991,7 +1990,7 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrEmpty(chapter.ImagePath)) { - chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter); + chapter.ImageTag = _imageProcessor.GetImageCacheTag(item, chapter); } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d302d89843..6c20842c7f 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -4,6 +4,7 @@ + diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index a67118f188..97717e6263 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -78,12 +78,21 @@ namespace Jellyfin.Drawing.Skia => new HashSet() { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; /// - /// Test to determine if the native lib is available. + /// Check if the native lib is available. /// - public static void TestSkia() + /// True if the native lib is available, otherwise false. + public static bool IsNativeLibAvailable() { - // test an operation that requires the native library - SKPMColor.PreMultiply(SKColors.Black); + try + { + // test an operation that requires the native library + SKPMColor.PreMultiply(SKColors.Black); + return true; + } + catch (Exception) + { + return false; + } } private static bool IsTransparent(SKColor color) diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 1d5313c13e..b35200e757 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -20,21 +20,18 @@ namespace Jellyfin.Server /// The to be used by the . /// The to be used by the . /// The to be used by the . - /// The to be used by the . /// The to be used by the . public CoreAppHost( ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, - IImageEncoder imageEncoder, INetworkManager networkManager) : base( applicationPaths, loggerFactory, options, fileSystem, - imageEncoder, networkManager) { } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 6be9ba4f2d..5e9c15e20e 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -183,7 +183,6 @@ namespace Jellyfin.Server _loggerFactory, options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), - GetImageEncoder(appPaths), new NetworkManager(_loggerFactory.CreateLogger())); try @@ -553,25 +552,6 @@ namespace Jellyfin.Server } } - private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths) - { - try - { - // Test if the native lib is available - SkiaEncoder.TestSkia(); - - return new SkiaEncoder( - _loggerFactory.CreateLogger(), - appPaths); - } - catch (Exception ex) - { - _logger.LogWarning(ex, $"Skia not available. Will fallback to {nameof(NullImageEncoder)}."); - } - - return new NullImageEncoder(); - } - private static void StartNewInstance(StartupOptions options) { _logger.LogInformation("Starting new instance"); From d173358065c65710476b225168bd8a348d99cf11 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 17:19:16 -0400 Subject: [PATCH 092/411] Move ApplicationHost certificate initialization to constructor --- .../ApplicationHost.cs | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ff4fb54b55..57ec71c9da 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -331,6 +331,13 @@ namespace Emby.Server.Implementations fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); NetworkManager.NetworkChanged += OnNetworkChanged; + + CertificateInfo = new CertificateInfo + { + Path = ServerConfigurationManager.Configuration.CertificatePath, + Password = ServerConfigurationManager.Configuration.CertificatePassword + }; + Certificate = GetCertificate(CertificateInfo); } public string ExpandVirtualPath(string path) @@ -712,9 +719,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); - CertificateInfo = GetCertificateInfo(true); - Certificate = GetCertificate(CertificateInfo); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -1106,16 +1110,6 @@ namespace Emby.Server.Implementations }); } - private CertificateInfo GetCertificateInfo(bool generateCertificate) - { - // Custom cert - return new CertificateInfo - { - Path = ServerConfigurationManager.Configuration.CertificatePath, - Password = ServerConfigurationManager.Configuration.CertificatePassword - }; - } - /// /// Called when [configuration updated]. /// @@ -1148,8 +1142,7 @@ namespace Emby.Server.Implementations } var currentCertPath = CertificateInfo?.Path; - var newCertInfo = GetCertificateInfo(false); - var newCertPath = newCertInfo?.Path; + var newCertPath = ServerConfigurationManager.Configuration.CertificatePath; if (!string.Equals(currentCertPath, newCertPath, StringComparison.OrdinalIgnoreCase)) { From c2b21ce5531a2b53fd0d0cc8231b4fd7d4261abe Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 17:33:00 -0400 Subject: [PATCH 093/411] Register and construct ILibraryMonitor correctly --- .../ApplicationHost.cs | 11 +--- .../IO/LibraryMonitor.cs | 58 +++++++++---------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 57ec71c9da..51c6f13424 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -257,12 +257,6 @@ namespace Emby.Server.Implementations /// The library manager. internal ILibraryManager LibraryManager { get; set; } - /// - /// Gets or sets the directory watchers. - /// - /// The directory watchers. - private ILibraryMonitor LibraryMonitor { get; set; } - /// /// Gets or sets the media encoder. /// @@ -708,14 +702,13 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, Resolve, Resolve, MediaEncoder); + LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, Resolve, FileSystemManager, Resolve, Resolve, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); var musicManager = new MusicManager(LibraryManager); serviceCollection.AddSingleton(musicManager); - LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); - serviceCollection.AddSingleton(LibraryMonitor); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index b1fb8cc635..5a1eb43bcb 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -17,6 +17,11 @@ namespace Emby.Server.Implementations.IO { public class LibraryMonitor : ILibraryMonitor { + private readonly ILogger _logger; + private readonly ILibraryManager _libraryManager; + private readonly IServerConfigurationManager _configurationManager; + private readonly IFileSystem _fileSystem; + /// /// The file system watchers. /// @@ -113,34 +118,23 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path); + _logger.LogError(ex, "Error in ReportFileSystemChanged for {path}", path); } } } - /// - /// Gets or sets the logger. - /// - /// The logger. - private ILogger Logger { get; set; } - - private ILibraryManager LibraryManager { get; set; } - private IServerConfigurationManager ConfigurationManager { get; set; } - - private readonly IFileSystem _fileSystem; - /// /// Initializes a new instance of the class. /// public LibraryMonitor( - ILoggerFactory loggerFactory, + ILogger logger, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) { - LibraryManager = libraryManager; - Logger = loggerFactory.CreateLogger(GetType().Name); - ConfigurationManager = configurationManager; + _libraryManager = libraryManager; + _logger = logger; + _configurationManager = configurationManager; _fileSystem = fileSystem; } @@ -151,7 +145,7 @@ namespace Emby.Server.Implementations.IO return false; } - var options = LibraryManager.GetLibraryOptions(item); + var options = _libraryManager.GetLibraryOptions(item); if (options != null) { @@ -163,12 +157,12 @@ namespace Emby.Server.Implementations.IO public void Start() { - LibraryManager.ItemAdded += OnLibraryManagerItemAdded; - LibraryManager.ItemRemoved += OnLibraryManagerItemRemoved; + _libraryManager.ItemAdded += OnLibraryManagerItemAdded; + _libraryManager.ItemRemoved += OnLibraryManagerItemRemoved; var pathsToWatch = new List(); - var paths = LibraryManager + var paths = _libraryManager .RootFolder .Children .Where(IsLibraryMonitorEnabled) @@ -261,7 +255,7 @@ namespace Emby.Server.Implementations.IO if (!Directory.Exists(path)) { // Seeing a crash in the mono runtime due to an exception being thrown on a different thread - Logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path); + _logger.LogInformation("Skipping realtime monitor for {Path} because the path does not exist", path); return; } @@ -297,7 +291,7 @@ namespace Emby.Server.Implementations.IO if (_fileSystemWatchers.TryAdd(path, newWatcher)) { newWatcher.EnableRaisingEvents = true; - Logger.LogInformation("Watching directory " + path); + _logger.LogInformation("Watching directory " + path); } else { @@ -307,7 +301,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Error watching path: {path}", path); + _logger.LogError(ex, "Error watching path: {path}", path); } }); } @@ -333,7 +327,7 @@ namespace Emby.Server.Implementations.IO { using (watcher) { - Logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path); + _logger.LogInformation("Stopping directory watching for path {Path}", watcher.Path); watcher.Created -= OnWatcherChanged; watcher.Deleted -= OnWatcherChanged; @@ -372,7 +366,7 @@ namespace Emby.Server.Implementations.IO var ex = e.GetException(); var dw = (FileSystemWatcher)sender; - Logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path); + _logger.LogError(ex, "Error in Directory watcher for: {Path}", dw.Path); DisposeWatcher(dw, true); } @@ -390,7 +384,7 @@ namespace Emby.Server.Implementations.IO } catch (Exception ex) { - Logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath); + _logger.LogError(ex, "Exception in ReportFileSystemChanged. Path: {FullPath}", e.FullPath); } } @@ -416,13 +410,13 @@ namespace Emby.Server.Implementations.IO { if (_fileSystem.AreEqual(i, path)) { - Logger.LogDebug("Ignoring change to {Path}", path); + _logger.LogDebug("Ignoring change to {Path}", path); return true; } if (_fileSystem.ContainsSubPath(i, path)) { - Logger.LogDebug("Ignoring change to {Path}", path); + _logger.LogDebug("Ignoring change to {Path}", path); return true; } @@ -430,7 +424,7 @@ namespace Emby.Server.Implementations.IO var parent = Path.GetDirectoryName(i); if (!string.IsNullOrEmpty(parent) && _fileSystem.AreEqual(parent, path)) { - Logger.LogDebug("Ignoring change to {Path}", path); + _logger.LogDebug("Ignoring change to {Path}", path); return true; } @@ -485,7 +479,7 @@ namespace Emby.Server.Implementations.IO } } - var newRefresher = new FileRefresher(path, ConfigurationManager, LibraryManager, Logger); + var newRefresher = new FileRefresher(path, _configurationManager, _libraryManager, _logger); newRefresher.Completed += NewRefresher_Completed; _activeRefreshers.Add(newRefresher); } @@ -502,8 +496,8 @@ namespace Emby.Server.Implementations.IO /// public void Stop() { - LibraryManager.ItemAdded -= OnLibraryManagerItemAdded; - LibraryManager.ItemRemoved -= OnLibraryManagerItemRemoved; + _libraryManager.ItemAdded -= OnLibraryManagerItemAdded; + _libraryManager.ItemRemoved -= OnLibraryManagerItemRemoved; foreach (var watcher in _fileSystemWatchers.Values.ToList()) { From 7fd25f94f3432f1c9319e49f4d62454f45ad7515 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 18:22:29 -0400 Subject: [PATCH 094/411] Inject and construct ISearchEngine and IMusicManager correctly --- Emby.Server.Implementations/ApplicationHost.cs | 5 ++--- Emby.Server.Implementations/Library/SearchEngine.cs | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 51c6f13424..ba149590fb 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -705,12 +705,11 @@ namespace Emby.Server.Implementations LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, Resolve, FileSystemManager, Resolve, Resolve, MediaEncoder); serviceCollection.AddSingleton(LibraryManager); - var musicManager = new MusicManager(LibraryManager); - serviceCollection.AddSingleton(musicManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(new SearchEngine(LoggerFactory, LibraryManager, UserManager)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 11d6c737ac..59a77607d2 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -17,16 +17,15 @@ namespace Emby.Server.Implementations.Library { public class SearchEngine : ISearchEngine { + private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; - private readonly ILogger _logger; - public SearchEngine(ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager) + public SearchEngine(ILogger logger, ILibraryManager libraryManager, IUserManager userManager) { + _logger = logger; _libraryManager = libraryManager; _userManager = userManager; - - _logger = loggerFactory.CreateLogger("SearchEngine"); } public QueryResult GetSearchHints(SearchQuery query) From fe9f4e06d1362eb6d0e7cc5e68d8819b872cb7bc Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 18:28:46 -0400 Subject: [PATCH 095/411] Register and construct LibraryManager correctly --- .../ApplicationHost.cs | 21 +- .../Library/LibraryManager.cs | 227 ++++++++---------- 2 files changed, 107 insertions(+), 141 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ba149590fb..aca1abf9fd 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -251,12 +251,6 @@ namespace Emby.Server.Implementations /// The user manager. public IUserManager UserManager { get; set; } - /// - /// Gets or sets the library manager. - /// - /// The library manager. - internal ILibraryManager LibraryManager { get; set; } - /// /// Gets or sets the media encoder. /// @@ -702,8 +696,11 @@ namespace Emby.Server.Implementations StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); - LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, Resolve, FileSystemManager, Resolve, Resolve, MediaEncoder); - serviceCollection.AddSingleton(LibraryManager); + // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required + serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -789,9 +786,7 @@ namespace Emby.Server.Implementations ((UserDataManager)UserDataManager).Repository = userDataRepo; - var itemRepo = (SqliteItemRepository)Resolve(); - itemRepo.Initialize(userDataRepo, UserManager); - ((LibraryManager)LibraryManager).ItemRepository = itemRepo; + ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, UserManager); FindParts(); } @@ -894,7 +889,7 @@ namespace Emby.Server.Implementations // For now there's no real way to inject these properly BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; - BaseItem.LibraryManager = LibraryManager; + BaseItem.LibraryManager = Resolve(); BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = Resolve(); @@ -970,7 +965,7 @@ namespace Emby.Server.Implementations _httpServer.Init(GetExportTypes(), GetExports(), GetUrlPrefixes()); - LibraryManager.AddParts( + Resolve().AddParts( GetExports(), GetExports(), GetExports(), diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 8ec4d08be5..e2985c26cb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -54,9 +54,29 @@ namespace Emby.Server.Implementations.Library /// public class LibraryManager : ILibraryManager { + private readonly ILogger _logger; + private readonly ITaskManager _taskManager; + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataRepository; + private readonly IServerConfigurationManager _configurationManager; + private readonly Lazy _libraryMonitorFactory; + private readonly Lazy _providerManagerFactory; + private readonly Lazy _userviewManagerFactory; + private readonly IServerApplicationHost _appHost; + private readonly IMediaEncoder _mediaEncoder; + private readonly IFileSystem _fileSystem; + private readonly IItemRepository _itemRepository; + private readonly ConcurrentDictionary _libraryItemsCache; + private NamingOptions _namingOptions; private string[] _videoFileExtensions; + private ILibraryMonitor LibraryMonitor => _libraryMonitorFactory.Value; + + private IProviderManager ProviderManager => _providerManagerFactory.Value; + + private IUserViewManager UserViewManager => _userviewManagerFactory.Value; + /// /// Gets or sets the postscan tasks. /// @@ -89,12 +109,6 @@ namespace Emby.Server.Implementations.Library /// The comparers. private IBaseItemComparer[] Comparers { get; set; } - /// - /// Gets or sets the active item repository - /// - /// The item repository. - public IItemRepository ItemRepository { get; set; } - /// /// Occurs when [item added]. /// @@ -110,90 +124,47 @@ namespace Emby.Server.Implementations.Library /// public event EventHandler ItemRemoved; - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// The _task manager - /// - private readonly ITaskManager _taskManager; - - /// - /// The _user manager - /// - private readonly IUserManager _userManager; - - /// - /// The _user data repository - /// - private readonly IUserDataManager _userDataRepository; - - /// - /// Gets or sets the configuration manager. - /// - /// The configuration manager. - private IServerConfigurationManager ConfigurationManager { get; set; } - - private readonly Func _libraryMonitorFactory; - private readonly Func _providerManagerFactory; - private readonly Func _userviewManager; public bool IsScanRunning { get; private set; } - private IServerApplicationHost _appHost; - private readonly IMediaEncoder _mediaEncoder; - - /// - /// The _library items cache - /// - private readonly ConcurrentDictionary _libraryItemsCache; - - /// - /// Gets the library items cache. - /// - /// The library items cache. - private ConcurrentDictionary LibraryItemsCache => _libraryItemsCache; - - private readonly IFileSystem _fileSystem; - /// /// Initializes a new instance of the class. /// /// The application host - /// The logger factory. + /// The logger. /// The task manager. /// The user manager. /// The configuration manager. /// The user data repository. public LibraryManager( IServerApplicationHost appHost, - ILoggerFactory loggerFactory, + ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, - Func libraryMonitorFactory, + Lazy libraryMonitorFactory, IFileSystem fileSystem, - Func providerManagerFactory, - Func userviewManager, - IMediaEncoder mediaEncoder) + Lazy providerManagerFactory, + Lazy userviewManagerFactory, + IMediaEncoder mediaEncoder, + IItemRepository itemRepository) { _appHost = appHost; - _logger = loggerFactory.CreateLogger(nameof(LibraryManager)); + _logger = logger; _taskManager = taskManager; _userManager = userManager; - ConfigurationManager = configurationManager; + _configurationManager = configurationManager; _userDataRepository = userDataRepository; _libraryMonitorFactory = libraryMonitorFactory; _fileSystem = fileSystem; _providerManagerFactory = providerManagerFactory; - _userviewManager = userviewManager; + _userviewManagerFactory = userviewManagerFactory; _mediaEncoder = mediaEncoder; + _itemRepository = itemRepository; _libraryItemsCache = new ConcurrentDictionary(); - ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated; + _configurationManager.ConfigurationUpdated += ConfigurationUpdated; RecordConfigurationValues(configurationManager.Configuration); } @@ -272,7 +243,7 @@ namespace Emby.Server.Implementations.Library /// The instance containing the event data. private void ConfigurationUpdated(object sender, EventArgs e) { - var config = ConfigurationManager.Configuration; + var config = _configurationManager.Configuration; var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted; @@ -306,7 +277,7 @@ namespace Emby.Server.Implementations.Library } } - LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; }); + _libraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; }); } public void DeleteItem(BaseItem item, DeleteOptions options) @@ -437,10 +408,10 @@ namespace Emby.Server.Implementations.Library item.SetParent(null); - ItemRepository.DeleteItem(item.Id, CancellationToken.None); + _itemRepository.DeleteItem(item.Id, CancellationToken.None); foreach (var child in children) { - ItemRepository.DeleteItem(child.Id, CancellationToken.None); + _itemRepository.DeleteItem(child.Id, CancellationToken.None); } _libraryItemsCache.TryRemove(item.Id, out BaseItem removed); @@ -509,15 +480,15 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(type)); } - if (key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal)) + if (key.StartsWith(_configurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal)) { // Try to normalize paths located underneath program-data in an attempt to make them more portable - key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length) + key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length) .TrimStart(new[] { '/', '\\' }) .Replace("/", "\\"); } - if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) + if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds) { key = key.ToLowerInvariant(); } @@ -550,7 +521,7 @@ namespace Emby.Server.Implementations.Library collectionType = GetContentTypeOverride(fullPath, true); } - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) + var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService) { Parent = parent, Path = fullPath, @@ -720,7 +691,7 @@ namespace Emby.Server.Implementations.Library /// Cannot create the root folder until plugins have loaded. public AggregateFolder CreateRootFolder() { - var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; + var rootFolderPath = _configurationManager.ApplicationPaths.RootFolderPath; Directory.CreateDirectory(rootFolderPath); @@ -734,7 +705,7 @@ namespace Emby.Server.Implementations.Library } // Add in the plug-in folders - var path = Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "playlists"); + var path = Path.Combine(_configurationManager.ApplicationPaths.DataPath, "playlists"); Directory.CreateDirectory(path); @@ -786,7 +757,7 @@ namespace Emby.Server.Implementations.Library { if (_userRootFolder == null) { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; _logger.LogDebug("Creating userRootPath at {path}", userRootPath); Directory.CreateDirectory(userRootPath); @@ -980,7 +951,7 @@ namespace Emby.Server.Implementations.Library where T : BaseItem, new() { var path = getPathFn(name); - var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds; + var forceCaseInsensitiveId = _configurationManager.Configuration.EnableNormalizedItemByNameIds; return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); } @@ -994,7 +965,7 @@ namespace Emby.Server.Implementations.Library public Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) { // Ensure the location is available. - Directory.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); + Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath); return new PeopleValidator(this, _logger, _fileSystem).ValidatePeople(cancellationToken, progress); } @@ -1031,7 +1002,7 @@ namespace Emby.Server.Implementations.Library public async Task ValidateMediaLibraryInternal(IProgress progress, CancellationToken cancellationToken) { IsScanRunning = true; - _libraryMonitorFactory().Stop(); + LibraryMonitor.Stop(); try { @@ -1039,7 +1010,7 @@ namespace Emby.Server.Implementations.Library } finally { - _libraryMonitorFactory().Start(); + LibraryMonitor.Start(); IsScanRunning = false; } } @@ -1148,7 +1119,7 @@ namespace Emby.Server.Implementations.Library progress.Report(percent * 100); } - ItemRepository.UpdateInheritedValues(cancellationToken); + _itemRepository.UpdateInheritedValues(cancellationToken); progress.Report(100); } @@ -1168,9 +1139,9 @@ namespace Emby.Server.Implementations.Library var topLibraryFolders = GetUserRootFolder().Children.ToList(); _logger.LogDebug("Getting refreshQueue"); - var refreshQueue = includeRefreshState ? _providerManagerFactory().GetRefreshQueue() : null; + var refreshQueue = includeRefreshState ? ProviderManager.GetRefreshQueue() : null; - return _fileSystem.GetDirectoryPaths(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath) + return _fileSystem.GetDirectoryPaths(_configurationManager.ApplicationPaths.DefaultUserViewsPath) .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders, refreshQueue)) .ToList(); } @@ -1245,7 +1216,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentException("Guid can't be empty", nameof(id)); } - if (LibraryItemsCache.TryGetValue(id, out BaseItem item)) + if (_libraryItemsCache.TryGetValue(id, out BaseItem item)) { return item; } @@ -1276,7 +1247,7 @@ namespace Emby.Server.Implementations.Library AddUserToQuery(query, query.User, allowExternalContent); } - return ItemRepository.GetItemList(query); + return _itemRepository.GetItemList(query); } public List GetItemList(InternalItemsQuery query) @@ -1300,7 +1271,7 @@ namespace Emby.Server.Implementations.Library AddUserToQuery(query, query.User); } - return ItemRepository.GetCount(query); + return _itemRepository.GetCount(query); } public List GetItemList(InternalItemsQuery query, List parents) @@ -1315,7 +1286,7 @@ namespace Emby.Server.Implementations.Library } } - return ItemRepository.GetItemList(query); + return _itemRepository.GetItemList(query); } public QueryResult QueryItems(InternalItemsQuery query) @@ -1327,12 +1298,12 @@ namespace Emby.Server.Implementations.Library if (query.EnableTotalRecordCount) { - return ItemRepository.GetItems(query); + return _itemRepository.GetItems(query); } return new QueryResult { - Items = ItemRepository.GetItemList(query).ToArray() + Items = _itemRepository.GetItemList(query).ToArray() }; } @@ -1343,7 +1314,7 @@ namespace Emby.Server.Implementations.Library AddUserToQuery(query, query.User); } - return ItemRepository.GetItemIdsList(query); + return _itemRepository.GetItemIdsList(query); } public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query) @@ -1354,7 +1325,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetStudios(query); + return _itemRepository.GetStudios(query); } public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query) @@ -1365,7 +1336,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetGenres(query); + return _itemRepository.GetGenres(query); } public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query) @@ -1376,7 +1347,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetMusicGenres(query); + return _itemRepository.GetMusicGenres(query); } public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query) @@ -1387,7 +1358,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetAllArtists(query); + return _itemRepository.GetAllArtists(query); } public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query) @@ -1398,7 +1369,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetArtists(query); + return _itemRepository.GetArtists(query); } private void SetTopParentOrAncestorIds(InternalItemsQuery query) @@ -1439,7 +1410,7 @@ namespace Emby.Server.Implementations.Library } SetTopParentOrAncestorIds(query); - return ItemRepository.GetAlbumArtists(query); + return _itemRepository.GetAlbumArtists(query); } public QueryResult GetItemsResult(InternalItemsQuery query) @@ -1460,10 +1431,10 @@ namespace Emby.Server.Implementations.Library if (query.EnableTotalRecordCount) { - return ItemRepository.GetItems(query); + return _itemRepository.GetItems(query); } - var list = ItemRepository.GetItemList(query); + var list = _itemRepository.GetItemList(query); return new QueryResult { @@ -1509,7 +1480,7 @@ namespace Emby.Server.Implementations.Library string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) && query.ItemIds.Length == 0) { - var userViews = _userviewManager().GetUserViews(new UserViewQuery + var userViews = UserViewManager.GetUserViews(new UserViewQuery { UserId = user.Id, IncludeHidden = true, @@ -1809,7 +1780,7 @@ namespace Emby.Server.Implementations.Library // Don't iterate multiple times var itemsList = items.ToList(); - ItemRepository.SaveItems(itemsList, cancellationToken); + _itemRepository.SaveItems(itemsList, cancellationToken); foreach (var item in itemsList) { @@ -1846,7 +1817,7 @@ namespace Emby.Server.Implementations.Library public void UpdateImages(BaseItem item) { - ItemRepository.SaveImages(item); + _itemRepository.SaveImages(item); RegisterItem(item); } @@ -1863,7 +1834,7 @@ namespace Emby.Server.Implementations.Library { if (item.IsFileProtocol) { - _providerManagerFactory().SaveMetadata(item, updateReason); + ProviderManager.SaveMetadata(item, updateReason); } item.DateLastSaved = DateTime.UtcNow; @@ -1871,7 +1842,7 @@ namespace Emby.Server.Implementations.Library RegisterItem(item); } - ItemRepository.SaveItems(itemsList, cancellationToken); + _itemRepository.SaveItems(itemsList, cancellationToken); if (ItemUpdated != null) { @@ -1947,7 +1918,7 @@ namespace Emby.Server.Implementations.Library /// BaseItem. public BaseItem RetrieveItem(Guid id) { - return ItemRepository.RetrieveItem(id); + return _itemRepository.RetrieveItem(id); } public List GetCollectionFolders(BaseItem item) @@ -2066,7 +2037,7 @@ namespace Emby.Server.Implementations.Library private string GetContentTypeOverride(string path, bool inherit) { - var nameValuePair = ConfigurationManager.Configuration.ContentTypes + var nameValuePair = _configurationManager.Configuration.ContentTypes .FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrEmpty(i.Name) && _fileSystem.ContainsSubPath(i.Name, path))); @@ -2115,7 +2086,7 @@ namespace Emby.Server.Implementations.Library string sortName) { var path = Path.Combine( - ConfigurationManager.ApplicationPaths.InternalMetadataPath, + _configurationManager.ApplicationPaths.InternalMetadataPath, "views", _fileSystem.GetValidFilename(viewType)); @@ -2147,7 +2118,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); - _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); + ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); } return item; @@ -2165,7 +2136,7 @@ namespace Emby.Server.Implementations.Library var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); + var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; @@ -2202,7 +2173,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh( + ProviderManager.QueueRefresh( item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { @@ -2269,7 +2240,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh( + ProviderManager.QueueRefresh( item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { @@ -2303,7 +2274,7 @@ namespace Emby.Server.Implementations.Library var id = GetNewItemId(idValues, typeof(UserView)); - var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); + var path = Path.Combine(_configurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N", CultureInfo.InvariantCulture)); var item = GetItemById(id) as UserView; @@ -2346,7 +2317,7 @@ namespace Emby.Server.Implementations.Library if (refresh) { - _providerManagerFactory().QueueRefresh( + ProviderManager.QueueRefresh( item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { @@ -2677,8 +2648,8 @@ namespace Emby.Server.Implementations.Library } } - var metadataPath = ConfigurationManager.Configuration.MetadataPath; - var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath; + var metadataPath = _configurationManager.Configuration.MetadataPath; + var metadataNetworkPath = _configurationManager.Configuration.MetadataNetworkPath; if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) { @@ -2689,7 +2660,7 @@ namespace Emby.Server.Implementations.Library } } - foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) + foreach (var map in _configurationManager.Configuration.PathSubstitutions) { if (!string.IsNullOrWhiteSpace(map.From)) { @@ -2758,7 +2729,7 @@ namespace Emby.Server.Implementations.Library public List GetPeople(InternalPeopleQuery query) { - return ItemRepository.GetPeople(query); + return _itemRepository.GetPeople(query); } public List GetPeople(BaseItem item) @@ -2781,7 +2752,7 @@ namespace Emby.Server.Implementations.Library public List GetPeopleItems(InternalPeopleQuery query) { - return ItemRepository.GetPeopleNames(query).Select(i => + return _itemRepository.GetPeopleNames(query).Select(i => { try { @@ -2798,7 +2769,7 @@ namespace Emby.Server.Implementations.Library public List GetPeopleNames(InternalPeopleQuery query) { - return ItemRepository.GetPeopleNames(query); + return _itemRepository.GetPeopleNames(query); } public void UpdatePeople(BaseItem item, List people) @@ -2808,7 +2779,7 @@ namespace Emby.Server.Implementations.Library return; } - ItemRepository.UpdatePeople(item.Id, people); + _itemRepository.UpdatePeople(item.Id, people); } public async Task ConvertImageToLocal(BaseItem item, ItemImageInfo image, int imageIndex) @@ -2819,7 +2790,7 @@ namespace Emby.Server.Implementations.Library { _logger.LogDebug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url); - await _providerManagerFactory().SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); + await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); @@ -2852,7 +2823,7 @@ namespace Emby.Server.Implementations.Library name = _fileSystem.GetValidFilename(name); - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, name); while (Directory.Exists(virtualFolderPath)) @@ -2871,7 +2842,7 @@ namespace Emby.Server.Implementations.Library } } - _libraryMonitorFactory().Stop(); + LibraryMonitor.Stop(); try { @@ -2906,7 +2877,7 @@ namespace Emby.Server.Implementations.Library { // Need to add a delay here or directory watchers may still pick up the changes await Task.Delay(1000).ConfigureAwait(false); - _libraryMonitorFactory().Start(); + LibraryMonitor.Start(); } } } @@ -2966,7 +2937,7 @@ namespace Emby.Server.Implementations.Library throw new FileNotFoundException("The network path does not exist."); } - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var shortcutFilename = Path.GetFileNameWithoutExtension(path); @@ -3009,7 +2980,7 @@ namespace Emby.Server.Implementations.Library throw new FileNotFoundException("The network path does not exist."); } - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); @@ -3062,7 +3033,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(name)); } - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var path = Path.Combine(rootFolderPath, name); @@ -3071,7 +3042,7 @@ namespace Emby.Server.Implementations.Library throw new FileNotFoundException("The media folder does not exist"); } - _libraryMonitorFactory().Stop(); + LibraryMonitor.Stop(); try { @@ -3091,7 +3062,7 @@ namespace Emby.Server.Implementations.Library { // Need to add a delay here or directory watchers may still pick up the changes await Task.Delay(1000).ConfigureAwait(false); - _libraryMonitorFactory().Start(); + LibraryMonitor.Start(); } } } @@ -3105,7 +3076,7 @@ namespace Emby.Server.Implementations.Library var removeList = new List(); - foreach (var contentType in ConfigurationManager.Configuration.ContentTypes) + foreach (var contentType in _configurationManager.Configuration.ContentTypes) { if (string.IsNullOrWhiteSpace(contentType.Name)) { @@ -3120,11 +3091,11 @@ namespace Emby.Server.Implementations.Library if (removeList.Count > 0) { - ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes + _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes .Except(removeList) .ToArray(); - ConfigurationManager.SaveConfiguration(); + _configurationManager.SaveConfiguration(); } } @@ -3135,7 +3106,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(mediaPath)); } - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); if (!Directory.Exists(virtualFolderPath)) From 84b48eb69c1bd6c87dc33e359fe989cd88714f19 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 19:01:21 -0400 Subject: [PATCH 096/411] Convert MediaEncoder property to field --- Emby.Server.Implementations/ApplicationHost.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index aca1abf9fd..d3014a2d88 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -121,6 +121,7 @@ namespace Emby.Server.Implementations { private SqliteUserRepository _userRepository; private SqliteDisplayPreferencesRepository _displayPreferencesRepository; + private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private IHttpServer _httpServer; @@ -251,12 +252,6 @@ namespace Emby.Server.Implementations /// The user manager. public IUserManager UserManager { get; set; } - /// - /// Gets or sets the media encoder. - /// - /// The media encoder. - private IMediaEncoder MediaEncoder { get; set; } - public LocalizationManager LocalizationManager { get; set; } /// @@ -486,7 +481,7 @@ namespace Emby.Server.Implementations ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; - MediaEncoder.SetFFmpegPath(); + _mediaEncoder.SetFFmpegPath(); Logger.LogInformation("ServerId: {0}", SystemId); @@ -685,7 +680,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(UserManager); - MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( + // TODO: Add StartupOptions.FFmpegPath to IConfiguration so this doesn't need to be constructed manually + serviceCollection.AddSingleton(new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( LoggerFactory.CreateLogger(), ServerConfigurationManager, FileSystemManager, @@ -693,8 +689,7 @@ namespace Emby.Server.Implementations LocalizationManager, Resolve, startupConfig, - StartupOptions.FFmpegPath); - serviceCollection.AddSingleton(MediaEncoder); + StartupOptions.FFmpegPath)); // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); @@ -772,6 +767,7 @@ namespace Emby.Server.Implementations /// public void InitializeServices() { + _mediaEncoder = Resolve(); _sessionManager = Resolve(); _httpServer = Resolve(); @@ -1306,7 +1302,7 @@ namespace Emby.Server.Implementations ServerName = FriendlyName, LocalAddress = localAddress, SupportsLibraryMonitor = true, - EncoderLocation = MediaEncoder.EncoderLocation, + EncoderLocation = _mediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, SystemUpdateLevel = SystemUpdateLevel, PackageName = StartupOptions.PackageName From 4daa5436fc0d635fa8ae7b391efa7e1a8561d029 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 19:31:14 -0400 Subject: [PATCH 097/411] Register and construct IUserManager and IUserRepository correctly --- .../ApplicationHost.cs | 51 ++++--------------- .../Library/UserManager.cs | 26 ++++------ 2 files changed, 21 insertions(+), 56 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index d3014a2d88..9e570588a8 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -246,12 +246,6 @@ namespace Emby.Server.Implementations /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - /// - /// Gets or sets the user manager. - /// - /// The user manager. - public IUserManager UserManager { get; set; } - public LocalizationManager LocalizationManager { get; set; } /// @@ -650,7 +644,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); - UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, () => UserManager); + UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, Resolve); serviceCollection.AddSingleton(UserDataManager); _displayPreferencesRepository = new SqliteDisplayPreferencesRepository( @@ -664,21 +658,11 @@ namespace Emby.Server.Implementations AuthenticationRepository = GetAuthenticationRepository(); serviceCollection.AddSingleton(AuthenticationRepository); - _userRepository = GetUserRepository(); - - UserManager = new UserManager( - LoggerFactory.CreateLogger(), - _userRepository, - XmlSerializer, - NetworkManager, - Resolve, - Resolve, - this, - JsonSerializer, - FileSystemManager, - cryptoProvider); + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(UserManager); + // TODO: Refactor to eliminate the circular dependency here so that Lazy isn't required + serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddSingleton(); // TODO: Add StartupOptions.FFmpegPath to IConfiguration so this doesn't need to be constructed manually serviceCollection.AddSingleton(new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( @@ -771,6 +755,7 @@ namespace Emby.Server.Implementations _sessionManager = Resolve(); _httpServer = Resolve(); + ((SqliteUserRepository)Resolve()).Initialize(); ((ActivityRepository)Resolve()).Initialize(); _displayPreferencesRepository.Initialize(); @@ -778,11 +763,12 @@ namespace Emby.Server.Implementations SetStaticProperties(); - ((UserManager)UserManager).Initialize(); + var userManager = (UserManager)Resolve(); + userManager.Initialize(); ((UserDataManager)UserDataManager).Repository = userDataRepo; - ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, UserManager); + ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, userManager); FindParts(); } @@ -853,21 +839,6 @@ namespace Emby.Server.Implementations } } - /// - /// Gets the user repository. - /// - /// . - private SqliteUserRepository GetUserRepository() - { - var repo = new SqliteUserRepository( - LoggerFactory.CreateLogger(), - ApplicationPaths); - - repo.Initialize(); - - return repo; - } - private IAuthenticationRepository GetAuthenticationRepository() { var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager); @@ -889,7 +860,7 @@ namespace Emby.Server.Implementations BaseItem.ProviderManager = Resolve(); BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = Resolve(); - User.UserManager = UserManager; + User.UserManager = Resolve(); BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; BaseItem.ChannelManager = Resolve(); @@ -984,7 +955,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports(), GetExports()); - UserManager.AddParts(GetExports(), GetExports()); + Resolve().AddParts(GetExports(), GetExports()); IsoManager.AddParts(GetExports()); } diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 7b17cc913f..64193a8ec2 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -44,22 +44,14 @@ namespace Emby.Server.Implementations.Library { private readonly object _policySyncLock = new object(); private readonly object _configSyncLock = new object(); - /// - /// The logger. - /// - private readonly ILogger _logger; - /// - /// Gets the active user repository. - /// - /// The user repository. + private readonly ILogger _logger; private readonly IUserRepository _userRepository; private readonly IXmlSerializer _xmlSerializer; private readonly IJsonSerializer _jsonSerializer; private readonly INetworkManager _networkManager; - - private readonly Func _imageProcessorFactory; - private readonly Func _dtoServiceFactory; + private readonly IImageProcessor _imageProcessor; + private readonly Lazy _dtoServiceFactory; private readonly IServerApplicationHost _appHost; private readonly IFileSystem _fileSystem; private readonly ICryptoProvider _cryptoProvider; @@ -74,13 +66,15 @@ namespace Emby.Server.Implementations.Library private IPasswordResetProvider[] _passwordResetProviders; private DefaultPasswordResetProvider _defaultPasswordResetProvider; + private IDtoService DtoService => _dtoServiceFactory.Value; + public UserManager( ILogger logger, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, - Func imageProcessorFactory, - Func dtoServiceFactory, + IImageProcessor imageProcessor, + Lazy dtoServiceFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem, @@ -90,7 +84,7 @@ namespace Emby.Server.Implementations.Library _userRepository = userRepository; _xmlSerializer = xmlSerializer; _networkManager = networkManager; - _imageProcessorFactory = imageProcessorFactory; + _imageProcessor = imageProcessor; _dtoServiceFactory = dtoServiceFactory; _appHost = appHost; _jsonSerializer = jsonSerializer; @@ -600,7 +594,7 @@ namespace Emby.Server.Implementations.Library try { - _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user); + DtoService.AttachPrimaryImageAspectRatio(dto, user); } catch (Exception ex) { @@ -625,7 +619,7 @@ namespace Emby.Server.Implementations.Library { try { - return _imageProcessorFactory().GetImageCacheTag(item, image); + return _imageProcessor.GetImageCacheTag(item, image); } catch (Exception ex) { From a5234dfd886e9938ede838dcee79ff81d2139f57 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 19:36:27 -0400 Subject: [PATCH 098/411] Register and construct IAuthenticationRepository correctly --- Emby.Server.Implementations/ApplicationHost.cs | 15 ++------------- .../Security/AuthenticationRepository.cs | 4 ++-- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9e570588a8..86b267303e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -254,8 +254,6 @@ namespace Emby.Server.Implementations /// The user data repository. private IUserDataManager UserDataManager { get; set; } - private IAuthenticationRepository AuthenticationRepository { get; set; } - /// /// Gets the installation manager. /// @@ -655,8 +653,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - AuthenticationRepository = GetAuthenticationRepository(); - serviceCollection.AddSingleton(AuthenticationRepository); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -755,6 +752,7 @@ namespace Emby.Server.Implementations _sessionManager = Resolve(); _httpServer = Resolve(); + ((AuthenticationRepository)Resolve()).Initialize(); ((SqliteUserRepository)Resolve()).Initialize(); ((ActivityRepository)Resolve()).Initialize(); _displayPreferencesRepository.Initialize(); @@ -839,15 +837,6 @@ namespace Emby.Server.Implementations } } - private IAuthenticationRepository GetAuthenticationRepository() - { - var repo = new AuthenticationRepository(LoggerFactory, ServerConfigurationManager); - - repo.Initialize(); - - return repo; - } - /// /// Dirty hacks. /// diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 1ef5c4b996..4e4029f06f 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -15,8 +15,8 @@ namespace Emby.Server.Implementations.Security { public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository { - public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config) - : base(loggerFactory.CreateLogger(nameof(AuthenticationRepository))) + public AuthenticationRepository(ILogger logger, IServerConfigurationManager config) + : base(logger) { DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db"); } From 5827f0f5a96574f1c87904a866890b998cab48a7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 19:40:53 -0400 Subject: [PATCH 099/411] Register IDisplayPreferencesRepository correctly --- Emby.Server.Implementations/ApplicationHost.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 86b267303e..875ed62fe1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -120,7 +120,6 @@ namespace Emby.Server.Implementations public abstract class ApplicationHost : IServerApplicationHost, IDisposable { private SqliteUserRepository _userRepository; - private SqliteDisplayPreferencesRepository _displayPreferencesRepository; private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private IHttpServer _httpServer; @@ -645,11 +644,7 @@ namespace Emby.Server.Implementations UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, Resolve); serviceCollection.AddSingleton(UserDataManager); - _displayPreferencesRepository = new SqliteDisplayPreferencesRepository( - LoggerFactory.CreateLogger(), - ApplicationPaths, - FileSystemManager); - serviceCollection.AddSingleton(_displayPreferencesRepository); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -752,10 +747,10 @@ namespace Emby.Server.Implementations _sessionManager = Resolve(); _httpServer = Resolve(); + ((SqliteDisplayPreferencesRepository)Resolve()).Initialize(); ((AuthenticationRepository)Resolve()).Initialize(); ((SqliteUserRepository)Resolve()).Initialize(); ((ActivityRepository)Resolve()).Initialize(); - _displayPreferencesRepository.Initialize(); var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths); @@ -1628,11 +1623,9 @@ namespace Emby.Server.Implementations } _userRepository?.Dispose(); - _displayPreferencesRepository?.Dispose(); } _userRepository = null; - _displayPreferencesRepository = null; _disposed = true; } From 615717e562f98c5cbd4e9ad648380a555d44d774 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 19:57:26 -0400 Subject: [PATCH 100/411] Register and construct IUserDataManager and IUserDataRepository correctly --- .../ApplicationHost.cs | 17 ++------- .../Library/UserDataManager.cs | 37 +++++++++---------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 875ed62fe1..ea580bad85 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -247,12 +247,6 @@ namespace Emby.Server.Implementations public LocalizationManager LocalizationManager { get; set; } - /// - /// Gets or sets the user data repository. - /// - /// The user data repository. - private IUserDataManager UserDataManager { get; set; } - /// /// Gets the installation manager. /// @@ -641,8 +635,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); - UserDataManager = new UserDataManager(LoggerFactory, ServerConfigurationManager, Resolve); - serviceCollection.AddSingleton(UserDataManager); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -752,15 +746,12 @@ namespace Emby.Server.Implementations ((SqliteUserRepository)Resolve()).Initialize(); ((ActivityRepository)Resolve()).Initialize(); - var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger(), ApplicationPaths); - SetStaticProperties(); var userManager = (UserManager)Resolve(); userManager.Initialize(); - ((UserDataManager)UserDataManager).Repository = userDataRepo; - + var userDataRepo = (SqliteUserDataRepository)Resolve(); ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, userManager); FindParts(); @@ -846,7 +837,7 @@ namespace Emby.Server.Implementations BaseItem.ItemRepository = Resolve(); User.UserManager = Resolve(); BaseItem.FileSystem = FileSystemManager; - BaseItem.UserDataManager = UserDataManager; + BaseItem.UserDataManager = Resolve(); BaseItem.ChannelManager = Resolve(); Video.LiveTvManager = Resolve(); Folder.UserViewManager = Resolve(); diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 071681b08f..a9772a078d 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -28,25 +28,24 @@ namespace Emby.Server.Implementations.Library private readonly ILogger _logger; private readonly IServerConfigurationManager _config; - - private Func _userManager; - - public UserDataManager(ILoggerFactory loggerFactory, IServerConfigurationManager config, Func userManager) + private readonly IUserManager _userManager; + private readonly IUserDataRepository _repository; + + public UserDataManager( + ILogger logger, + IServerConfigurationManager config, + IUserManager userManager, + IUserDataRepository repository) { + _logger = logger; _config = config; - _logger = loggerFactory.CreateLogger(GetType().Name); _userManager = userManager; + _repository = repository; } - /// - /// Gets or sets the repository. - /// - /// The repository. - public IUserDataRepository Repository { get; set; } - public void SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken) { - var user = _userManager().GetUserById(userId); + var user = _userManager.GetUserById(userId); SaveUserData(user, item, userData, reason, cancellationToken); } @@ -71,7 +70,7 @@ namespace Emby.Server.Implementations.Library foreach (var key in keys) { - Repository.SaveUserData(userId, key, userData, cancellationToken); + _repository.SaveUserData(userId, key, userData, cancellationToken); } var cacheKey = GetCacheKey(userId, item.Id); @@ -96,9 +95,9 @@ namespace Emby.Server.Implementations.Library /// public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken) { - var user = _userManager().GetUserById(userId); + var user = _userManager.GetUserById(userId); - Repository.SaveAllUserData(user.InternalId, userData, cancellationToken); + _repository.SaveAllUserData(user.InternalId, userData, cancellationToken); } /// @@ -108,14 +107,14 @@ namespace Emby.Server.Implementations.Library /// public List GetAllUserData(Guid userId) { - var user = _userManager().GetUserById(userId); + var user = _userManager.GetUserById(userId); - return Repository.GetAllUserData(user.InternalId); + return _repository.GetAllUserData(user.InternalId); } public UserItemData GetUserData(Guid userId, Guid itemId, List keys) { - var user = _userManager().GetUserById(userId); + var user = _userManager.GetUserById(userId); return GetUserData(user, itemId, keys); } @@ -131,7 +130,7 @@ namespace Emby.Server.Implementations.Library private UserItemData GetUserDataInternal(long internalUserId, List keys) { - var userData = Repository.GetUserData(internalUserId, keys); + var userData = _repository.GetUserData(internalUserId, keys); if (userData != null) { From cbc0224aafa1e0a4c9d52f55e03ab944ff3b7132 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 20:00:55 -0400 Subject: [PATCH 101/411] Register IStreamHelper, IInstallationManager, IZipClient, IHttpResultFactory and IBlurayExaminer correctly --- Emby.Server.Implementations/ApplicationHost.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ea580bad85..e951ab1146 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -610,7 +610,7 @@ namespace Emby.Server.Implementations ProcessFactory = new ProcessFactory(); serviceCollection.AddSingleton(ProcessFactory); - serviceCollection.AddSingleton(typeof(IStreamHelper), typeof(StreamHelper)); + serviceCollection.AddSingleton(); var cryptoProvider = new CryptographyProvider(); serviceCollection.AddSingleton(cryptoProvider); @@ -618,11 +618,11 @@ namespace Emby.Server.Implementations SocketFactory = new SocketFactory(); serviceCollection.AddSingleton(SocketFactory); - serviceCollection.AddSingleton(typeof(IInstallationManager), typeof(InstallationManager)); + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(typeof(IZipClient), typeof(ZipClient)); + serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(typeof(IHttpResultFactory), typeof(HttpResultFactory)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(this); serviceCollection.AddSingleton(ApplicationPaths); @@ -633,7 +633,7 @@ namespace Emby.Server.Implementations await LocalizationManager.LoadAll().ConfigureAwait(false); serviceCollection.AddSingleton(LocalizationManager); - serviceCollection.AddSingleton(new BdInfoExaminer(FileSystemManager)); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); From 5d648bf54f3c0fe503f8fdebb58a72b8c5e64673 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 20:21:48 -0400 Subject: [PATCH 102/411] Register and construct ILocalizationManager correctly --- .../ApplicationHost.cs | 33 ++++++++++--------- .../Localization/LocalizationManager.cs | 3 -- Jellyfin.Server/Program.cs | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index e951ab1146..75a2b194a6 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -245,8 +245,6 @@ namespace Emby.Server.Implementations /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - public LocalizationManager LocalizationManager { get; set; } - /// /// Gets the installation manager. /// @@ -629,9 +627,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - LocalizationManager = new LocalizationManager(ServerConfigurationManager, JsonSerializer, LoggerFactory.CreateLogger()); - await LocalizationManager.LoadAll().ConfigureAwait(false); - serviceCollection.AddSingleton(LocalizationManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -651,15 +647,16 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); // TODO: Add StartupOptions.FFmpegPath to IConfiguration so this doesn't need to be constructed manually - serviceCollection.AddSingleton(new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( - LoggerFactory.CreateLogger(), - ServerConfigurationManager, - FileSystemManager, - ProcessFactory, - LocalizationManager, - Resolve, - startupConfig, - StartupOptions.FFmpegPath)); + serviceCollection.AddSingleton(provider => + new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( + provider.GetRequiredService>(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService(), + provider.GetRequiredService, + provider.GetRequiredService(), + StartupOptions.FFmpegPath)); // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); @@ -735,8 +732,12 @@ namespace Emby.Server.Implementations /// /// Create services registered with the service container that need to be initialized at application startup. /// - public void InitializeServices() + /// A task representing the service initialization operation. + public async Task InitializeServices() { + var localizationManager = (LocalizationManager)Resolve(); + await localizationManager.LoadAll().ConfigureAwait(false); + _mediaEncoder = Resolve(); _sessionManager = Resolve(); _httpServer = Resolve(); @@ -833,7 +834,7 @@ namespace Emby.Server.Implementations BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = Resolve(); BaseItem.ProviderManager = Resolve(); - BaseItem.LocalizationManager = LocalizationManager; + BaseItem.LocalizationManager = Resolve(); BaseItem.ItemRepository = Resolve(); User.UserManager = Resolve(); BaseItem.FileSystem = FileSystemManager; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index bda43e832a..e2a634e1a4 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -23,9 +23,6 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; - /// - /// The _configuration manager. - /// private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; private readonly ILogger _logger; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 5e9c15e20e..83170c6ae0 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -208,7 +208,7 @@ namespace Jellyfin.Server // 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; - appHost.InitializeServices(); + await appHost.InitializeServices().ConfigureAwait(false); Migrations.MigrationRunner.Run(appHost, _loggerFactory); try From aee6a1b4764869143edab160eef4d6fb111032a2 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 20:40:50 -0400 Subject: [PATCH 103/411] Remove unnecessary async and parameter from ApplicationHost initialization method --- Emby.Server.Implementations/ApplicationHost.cs | 6 +++--- Jellyfin.Server/Program.cs | 2 +- MediaBrowser.Common/IApplicationHost.cs | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 75a2b194a6..4597b65f3d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -500,7 +500,7 @@ namespace Emby.Server.Implementations } /// - public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig) + public void Init(IServiceCollection serviceCollection) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -533,7 +533,7 @@ namespace Emby.Server.Implementations DiscoverTypes(); - await RegisterServices(serviceCollection, startupConfig).ConfigureAwait(false); + RegisterServices(serviceCollection); } public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func next) @@ -566,7 +566,7 @@ namespace Emby.Server.Implementations /// /// Registers services/resources with the service collection that will be available via DI. /// - protected async Task RegisterServices(IServiceCollection serviceCollection, IConfiguration startupConfig) + protected void RegisterServices(IServiceCollection serviceCollection) { var imageEncoderType = SkiaEncoder.IsNativeLibAvailable() ? typeof(SkiaEncoder) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 83170c6ae0..56017cf897 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -202,7 +202,7 @@ namespace Jellyfin.Server } ServiceCollection serviceCollection = new ServiceCollection(); - await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false); + appHost.Init(serviceCollection); var webHost = CreateWebHostBuilder(appHost, serviceCollection, options, startupConfig, appPaths).Build(); diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 0e282cf538..904138381c 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -125,9 +125,7 @@ namespace MediaBrowser.Common /// Initializes this instance. /// /// The service collection. - /// The configuration to use for initialization. - /// A task. - Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig); + void Init(IServiceCollection serviceCollection); /// /// Creates the instance. From 3f2f95d8774df3dc27e9a24d89e7f7d6f57e2886 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 20:42:11 -0400 Subject: [PATCH 104/411] Register IProcessFactory, ICryptoProvider and ISocketFactory correctly --- Emby.Server.Implementations/ApplicationHost.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 4597b65f3d..6c1719ac10 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -253,12 +253,8 @@ namespace Emby.Server.Implementations public IStartupOptions StartupOptions { get; } - protected IProcessFactory ProcessFactory { get; private set; } - protected readonly IXmlSerializer XmlSerializer; - protected ISocketFactory SocketFactory { get; private set; } - protected ITaskManager TaskManager { get; private set; } public IHttpClient HttpClient { get; private set; } @@ -605,16 +601,13 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(XmlSerializer); - ProcessFactory = new ProcessFactory(); - serviceCollection.AddSingleton(ProcessFactory); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - var cryptoProvider = new CryptographyProvider(); - serviceCollection.AddSingleton(cryptoProvider); + serviceCollection.AddSingleton(); - SocketFactory = new SocketFactory(); - serviceCollection.AddSingleton(SocketFactory); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -1539,7 +1532,7 @@ namespace Emby.Server.Implementations throw new NotSupportedException(); } - var process = ProcessFactory.Create(new ProcessOptions + var process = Resolve().Create(new ProcessOptions { FileName = url, EnableRaisingEvents = true, From adf0e8d3fd8724a78e97fc0f4874beef67e5b891 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 21:00:11 -0400 Subject: [PATCH 105/411] Register and construct ITaskManager and IIsoManager correctly --- .../ApplicationHost.cs | 12 +++-------- .../ScheduledTasks/TaskManager.cs | 20 +++---------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 6c1719ac10..0fde273c59 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -255,16 +255,12 @@ namespace Emby.Server.Implementations protected readonly IXmlSerializer XmlSerializer; - protected ITaskManager TaskManager { get; private set; } - public IHttpClient HttpClient { get; private set; } protected INetworkManager NetworkManager { get; set; } public IJsonSerializer JsonSerializer { get; private set; } - protected IIsoManager IsoManager { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -593,11 +589,9 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(NetworkManager); - IsoManager = new IsoManager(); - serviceCollection.AddSingleton(IsoManager); + serviceCollection.AddSingleton(); - TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LoggerFactory, FileSystemManager); - serviceCollection.AddSingleton(TaskManager); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(XmlSerializer); @@ -926,7 +920,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports()); Resolve().AddParts(GetExports(), GetExports()); - IsoManager.AddParts(GetExports()); + Resolve().AddParts(GetExports()); } private IPlugin LoadPlugin(IPlugin plugin) diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index ecf58dbc0e..6ffa581a9c 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -32,22 +32,8 @@ namespace Emby.Server.Implementations.ScheduledTasks private readonly ConcurrentQueue> _taskQueue = new ConcurrentQueue>(); - /// - /// Gets or sets the json serializer. - /// - /// The json serializer. private readonly IJsonSerializer _jsonSerializer; - - /// - /// Gets or sets the application paths. - /// - /// The application paths. private readonly IApplicationPaths _applicationPaths; - - /// - /// Gets the logger. - /// - /// The logger. private readonly ILogger _logger; private readonly IFileSystem _fileSystem; @@ -56,17 +42,17 @@ namespace Emby.Server.Implementations.ScheduledTasks /// /// The application paths. /// The json serializer. - /// The logger factory. + /// The logger. /// The filesystem manager. public TaskManager( IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, - ILoggerFactory loggerFactory, + ILogger logger, IFileSystem fileSystem) { _applicationPaths = applicationPaths; _jsonSerializer = jsonSerializer; - _logger = loggerFactory.CreateLogger(nameof(TaskManager)); + _logger = logger; _fileSystem = fileSystem; ScheduledTasks = Array.Empty(); From e16c16dd51c62b1ad004979d729e4ef022359505 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 21:18:09 -0400 Subject: [PATCH 106/411] Register and construct IHttpClient correctly --- Emby.Server.Implementations/ApplicationHost.cs | 18 +++++------------- .../HttpClientManager/HttpClientManager.cs | 9 +++++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0fde273c59..5592d39197 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -123,6 +123,7 @@ namespace Emby.Server.Implementations private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private IHttpServer _httpServer; + private IHttpClient _httpClient; /// /// Gets a value indicating whether this instance can self restart. @@ -255,8 +256,6 @@ namespace Emby.Server.Implementations protected readonly IXmlSerializer XmlSerializer; - public IHttpClient HttpClient { get; private set; } - protected INetworkManager NetworkManager { get; set; } public IJsonSerializer JsonSerializer { get; private set; } @@ -363,10 +362,7 @@ namespace Emby.Server.Implementations } } - /// - /// Gets the name. - /// - /// The name. + /// public string Name => ApplicationProductName; /// @@ -580,12 +576,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); - HttpClient = new HttpClientManager.HttpClientManager( - ApplicationPaths, - LoggerFactory.CreateLogger(), - FileSystemManager, - () => ApplicationUserAgent); - serviceCollection.AddSingleton(HttpClient); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(NetworkManager); @@ -728,6 +719,7 @@ namespace Emby.Server.Implementations _mediaEncoder = Resolve(); _sessionManager = Resolve(); _httpServer = Resolve(); + _httpClient = Resolve(); ((SqliteDisplayPreferencesRepository)Resolve()).Initialize(); ((AuthenticationRepository)Resolve()).Initialize(); @@ -1423,7 +1415,7 @@ namespace Emby.Server.Implementations try { - using (var response = await HttpClient.SendAsync( + using (var response = await _httpClient.SendAsync( new HttpRequestOptions { Url = apiUrl, diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 882bfe2f6e..d66bb7638b 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.HttpClientManager private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; - private readonly Func _defaultUserAgentFn; + private readonly IApplicationHost _appHost; /// /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. @@ -40,12 +41,12 @@ namespace Emby.Server.Implementations.HttpClientManager IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, - Func defaultUserAgentFn) + IApplicationHost appHost) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _fileSystem = fileSystem; _appPaths = appPaths ?? throw new ArgumentNullException(nameof(appPaths)); - _defaultUserAgentFn = defaultUserAgentFn; + _appHost = appHost; } /// @@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (options.EnableDefaultUserAgent && !request.Headers.TryGetValues(HeaderNames.UserAgent, out _)) { - request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn()); + request.Headers.Add(HeaderNames.UserAgent, _appHost.ApplicationUserAgent); } switch (options.DecompressionMethod) From 710767fbf2c80491448e3c978eec6b2745c9e4db Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 21:27:48 -0400 Subject: [PATCH 107/411] Add deprecation warning message for injecting ILogger --- Emby.Server.Implementations/ApplicationHost.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5592d39197..b724dc3201 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -570,8 +570,12 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(JsonSerializer); - // TODO: Support for injecting ILogger should be deprecated in favour of ILogger and this removed - serviceCollection.AddSingleton(Logger); + // 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(); From 809cf3a0c288713fd0ed0cc0ace6f7bb90a90ca2 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 21:33:57 -0400 Subject: [PATCH 108/411] Register IJsonSerializer correctly --- Emby.Server.Implementations/ApplicationHost.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index b724dc3201..bae97c17ec 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -258,8 +258,6 @@ namespace Emby.Server.Implementations protected INetworkManager NetworkManager { get; set; } - public IJsonSerializer JsonSerializer { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -500,8 +498,6 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - JsonSerializer = new JsonSerializer(); - if (Plugins != null) { var pluginBuilder = new StringBuilder(); @@ -568,7 +564,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ApplicationPaths); - serviceCollection.AddSingleton(JsonSerializer); + serviceCollection.AddSingleton(); // TODO: Remove support for injecting ILogger completely serviceCollection.AddSingleton((provider) => @@ -813,7 +809,7 @@ namespace Emby.Server.Implementations private void SetStaticProperties() { // For now there's no real way to inject these properly - BaseItem.Logger = LoggerFactory.CreateLogger("BaseItem"); + BaseItem.Logger = Resolve>(); BaseItem.ConfigurationManager = ServerConfigurationManager; BaseItem.LibraryManager = Resolve(); BaseItem.ProviderManager = Resolve(); @@ -829,7 +825,7 @@ namespace Emby.Server.Implementations UserView.CollectionManager = Resolve(); BaseItem.MediaSourceManager = Resolve(); CollectionFolder.XmlSerializer = XmlSerializer; - CollectionFolder.JsonSerializer = JsonSerializer; + CollectionFolder.JsonSerializer = Resolve(); CollectionFolder.ApplicationHost = this; AuthenticatedAttribute.AuthService = Resolve(); } From 241d0ae65cca0ffdd92b7c366d692acaa71cd211 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 23:14:35 -0400 Subject: [PATCH 109/411] Inject IStartupOptions into StartupWizard --- Emby.Server.Implementations/ApplicationHost.cs | 2 ++ .../EntryPoints/StartupWizard.cs | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index bae97c17ec..0620469c0f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -557,6 +557,8 @@ namespace Emby.Server.Implementations : typeof(NullImageEncoder); serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType); + serviceCollection.AddSingleton(_startupOptions); + serviceCollection.AddMemoryCache(); serviceCollection.AddSingleton(ConfigurationManager); diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 8e97719311..af1604aa6e 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs @@ -16,17 +16,25 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IServerApplicationHost _appHost; private readonly IConfiguration _appConfig; private readonly IServerConfigurationManager _config; + private readonly IStartupOptions _startupOptions; /// /// Initializes a new instance of the class. /// /// The application host. + /// The application configuration. /// The configuration manager. - public StartupWizard(IServerApplicationHost appHost, IConfiguration appConfig, IServerConfigurationManager config) + /// The application startup options. + public StartupWizard( + IServerApplicationHost appHost, + IConfiguration appConfig, + IServerConfigurationManager config, + IStartupOptions startupOptions) { _appHost = appHost; _appConfig = appConfig; _config = config; + _startupOptions = startupOptions; } /// @@ -47,9 +55,7 @@ namespace Emby.Server.Implementations.EntryPoints } else if (_config.Configuration.AutoRunWebApp) { - var options = ((ApplicationHost)_appHost).StartupOptions; - - if (!options.NoAutoRunWebApp) + if (!_startupOptions.NoAutoRunWebApp) { BrowserLauncher.OpenWebApp(_appHost); } From 735d6c8ad531904c24650d88dd07ef3e57ded46a Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sat, 4 Apr 2020 23:18:11 -0400 Subject: [PATCH 110/411] Convert properties in ApplicationHost to private readonly fields, where possible --- .../ApplicationHost.cs | 67 +++++++------------ .../Data/SqliteItemRepository.cs | 2 +- Jellyfin.Server/CoreAppHost.cs | 3 - 3 files changed, 27 insertions(+), 45 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0620469c0f..06b6fec555 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -119,17 +119,21 @@ namespace Emby.Server.Implementations /// public abstract class ApplicationHost : IServerApplicationHost, IDisposable { - private SqliteUserRepository _userRepository; + private readonly IFileSystem _fileSystemManager; + private readonly INetworkManager _networkManager; + private readonly IXmlSerializer _xmlSerializer; + private readonly IStartupOptions _startupOptions; + private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private IHttpServer _httpServer; private IHttpClient _httpClient; + private IInstallationManager _installationManager; /// /// Gets a value indicating whether this instance can self restart. /// - /// true if this instance can self restart; otherwise, false. - public abstract bool CanSelfRestart { get; } + public bool CanSelfRestart => _startupOptions.RestartPath != null; public virtual bool CanLaunchWebBrowser { @@ -140,7 +144,7 @@ namespace Emby.Server.Implementations return false; } - if (StartupOptions.IsService) + if (_startupOptions.IsService) { return false; } @@ -210,8 +214,6 @@ namespace Emby.Server.Implementations /// The configuration manager. protected IConfigurationManager ConfigurationManager { get; set; } - public IFileSystem FileSystemManager { get; set; } - /// public PackageVersionClass SystemUpdateLevel { @@ -246,18 +248,6 @@ namespace Emby.Server.Implementations /// The server configuration manager. public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager; - /// - /// Gets the installation manager. - /// - /// The installation manager. - protected IInstallationManager InstallationManager { get; private set; } - - public IStartupOptions StartupOptions { get; } - - protected readonly IXmlSerializer XmlSerializer; - - protected INetworkManager NetworkManager { get; set; } - /// /// Initializes a new instance of the class. /// @@ -268,24 +258,24 @@ namespace Emby.Server.Implementations IFileSystem fileSystem, INetworkManager networkManager) { - XmlSerializer = new MyXmlSerializer(); + _xmlSerializer = new MyXmlSerializer(); - NetworkManager = networkManager; + _networkManager = networkManager; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; ApplicationPaths = applicationPaths; LoggerFactory = loggerFactory; - FileSystemManager = fileSystem; + _fileSystemManager = fileSystem; - ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, XmlSerializer, FileSystemManager); + ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); Logger = LoggerFactory.CreateLogger("App"); - StartupOptions = options; + _startupOptions = options; fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); - NetworkManager.NetworkChanged += OnNetworkChanged; + _networkManager.NetworkChanged += OnNetworkChanged; CertificateInfo = new CertificateInfo { @@ -575,18 +565,18 @@ namespace Emby.Server.Implementations return Logger; }); - serviceCollection.AddSingleton(FileSystemManager); + serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(NetworkManager); + serviceCollection.AddSingleton(_networkManager); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(XmlSerializer); + serviceCollection.AddSingleton(_xmlSerializer); serviceCollection.AddSingleton(); @@ -636,7 +626,7 @@ namespace Emby.Server.Implementations provider.GetRequiredService(), provider.GetRequiredService, provider.GetRequiredService(), - StartupOptions.FFmpegPath)); + _startupOptions.FFmpegPath)); // TODO: Refactor to eliminate the circular dependencies here so that Lazy isn't required serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); @@ -736,6 +726,8 @@ namespace Emby.Server.Implementations var userDataRepo = (SqliteUserDataRepository)Resolve(); ((SqliteItemRepository)Resolve()).Initialize(userDataRepo, userManager); + Resolve().PluginInstalled += PluginInstalled; + FindParts(); } @@ -818,7 +810,7 @@ namespace Emby.Server.Implementations BaseItem.LocalizationManager = Resolve(); BaseItem.ItemRepository = Resolve(); User.UserManager = Resolve(); - BaseItem.FileSystem = FileSystemManager; + BaseItem.FileSystem = _fileSystemManager; BaseItem.UserDataManager = Resolve(); BaseItem.ChannelManager = Resolve(); Video.LiveTvManager = Resolve(); @@ -826,7 +818,7 @@ namespace Emby.Server.Implementations UserView.TVSeriesManager = Resolve(); UserView.CollectionManager = Resolve(); BaseItem.MediaSourceManager = Resolve(); - CollectionFolder.XmlSerializer = XmlSerializer; + CollectionFolder.XmlSerializer = _xmlSerializer; CollectionFolder.JsonSerializer = Resolve(); CollectionFolder.ApplicationHost = this; AuthenticatedAttribute.AuthService = Resolve(); @@ -872,9 +864,6 @@ namespace Emby.Server.Implementations /// private void FindParts() { - InstallationManager = ServiceProvider.GetService(); - InstallationManager.PluginInstalled += PluginInstalled; - if (!ServerConfigurationManager.Configuration.IsPortAuthorized) { ServerConfigurationManager.Configuration.IsPortAuthorized = true; @@ -1210,7 +1199,7 @@ namespace Emby.Server.Implementations IsShuttingDown = IsShuttingDown, Version = ApplicationVersionString, WebSocketPortNumber = HttpPort, - CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(), + CompletedInstallations = _installationManager.CompletedInstallations.ToArray(), Id = SystemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, WebPath = ApplicationPaths.WebPath, @@ -1233,12 +1222,12 @@ namespace Emby.Server.Implementations EncoderLocation = _mediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, SystemUpdateLevel = SystemUpdateLevel, - PackageName = StartupOptions.PackageName + PackageName = _startupOptions.PackageName }; } public IEnumerable GetWakeOnLanInfo() - => NetworkManager.GetMacAddresses() + => _networkManager.GetMacAddresses() .Select(i => new WakeOnLanInfo(i)) .ToList(); @@ -1350,7 +1339,7 @@ namespace Emby.Server.Implementations if (addresses.Count == 0) { - addresses.AddRange(NetworkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); + addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces)); } var resultList = new List(); @@ -1594,12 +1583,8 @@ namespace Emby.Server.Implementations Logger.LogError(ex, "Error disposing {Type}", part.GetType().Name); } } - - _userRepository?.Dispose(); } - _userRepository = null; - _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 227f1a5e7e..8db4146cd3 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Data private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; private readonly ILocalizationManager _localization; - // TODO: Remove this dependency + // TODO: Remove this dependency. GetImageCacheTag() is the only method used and it can be converted to a static helper method private readonly IImageProcessor _imageProcessor; private readonly TypeMapper _typeMapper; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b35200e757..c3ac2ab411 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -36,9 +36,6 @@ namespace Jellyfin.Server { } - /// - public override bool CanSelfRestart => StartupOptions.RestartPath != null; - /// protected override void RestartInternal() => Program.Restart(); From f2760cb055ae6ee5971ca4a3ff342ecd7d829d99 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 5 Apr 2020 10:03:53 -0400 Subject: [PATCH 111/411] Register IImageEncoder in Jellyfin.Server instead of Emby.Server.Implementations --- Emby.Server.Implementations/ApplicationHost.cs | 8 +------- .../Emby.Server.Implementations.csproj | 1 - Jellyfin.Server/CoreAppHost.cs | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 06b6fec555..0bc47627a1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -48,7 +48,6 @@ using Emby.Server.Implementations.Session; using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; -using Jellyfin.Drawing.Skia; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -540,13 +539,8 @@ namespace Emby.Server.Implementations /// /// Registers services/resources with the service collection that will be available via DI. /// - protected void RegisterServices(IServiceCollection serviceCollection) + protected virtual void RegisterServices(IServiceCollection serviceCollection) { - var imageEncoderType = SkiaEncoder.IsNativeLibAvailable() - ? typeof(SkiaEncoder) - : typeof(NullImageEncoder); - serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType); - serviceCollection.AddSingleton(_startupOptions); serviceCollection.AddMemoryCache(); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6c20842c7f..d302d89843 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -4,7 +4,6 @@ - diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index c3ac2ab411..0769bf844e 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; using System.Reflection; +using Emby.Drawing; using Emby.Server.Implementations; +using Jellyfin.Drawing.Skia; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Drawing; using MediaBrowser.Model.IO; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Jellyfin.Server @@ -36,6 +39,17 @@ namespace Jellyfin.Server { } + /// + protected override void RegisterServices(IServiceCollection serviceCollection) + { + var imageEncoderType = SkiaEncoder.IsNativeLibAvailable() + ? typeof(SkiaEncoder) + : typeof(NullImageEncoder); + serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType); + + base.RegisterServices(serviceCollection); + } + /// protected override void RestartInternal() => Program.Restart(); From 2fcbc2a5b804e8d426dfd014560291d2399ab799 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 5 Apr 2020 21:19:04 +0200 Subject: [PATCH 112/411] Enable nullabe reference types for Emby.Drawing and Jellyfin.Drawing.Skia --- Emby.Dlna/ConfigurationExtension.cs | 1 + Emby.Drawing/Emby.Drawing.csproj | 1 + Emby.Drawing/ImageProcessor.cs | 22 +++---------- .../Data/SqliteItemRepository.cs | 9 +++++- .../Jellyfin.Drawing.Skia.csproj | 1 + .../PlayedIndicatorDrawer.cs | 13 +++----- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 32 +++++++++---------- Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 10 +++--- .../UnplayedCountIndicator.cs | 4 +-- .../Extensions/ShuffleExtensions.cs | 2 ++ 10 files changed, 45 insertions(+), 50 deletions(-) diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index e224d10bd3..dba9019677 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/Emby.Dlna/ConfigurationExtension.cs @@ -1,3 +1,4 @@ +#nullable enable #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index b7090b2629..b17fff751b 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -5,6 +5,7 @@ false true true + enable diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index eca4b56eb9..3d6bcc045d 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -121,11 +121,6 @@ namespace Emby.Drawing /// public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options) { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - var libraryManager = _libraryManager(); ItemImageInfo originalImage = options.Image; @@ -351,19 +346,12 @@ namespace Emby.Drawing /// public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) { - try + return GetImageCacheTag(item, new ItemImageInfo { - return GetImageCacheTag(item, new ItemImageInfo - { - Path = chapter.ImagePath, - Type = ImageType.Chapter, - DateModified = chapter.ImageDateModified - }); - } - catch - { - return null; - } + Path = chapter.ImagePath, + Type = ImageType.Chapter, + DateModified = chapter.ImageDateModified + }); } private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 46c6d55200..d3b3f7b7af 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1991,7 +1991,14 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrEmpty(chapter.ImagePath)) { - chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter); + try + { + chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter); + } + catch + { + + } } } diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index d0a99e1e28..e4b4c058ec 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -5,6 +5,7 @@ false true true + enable diff --git a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs index 5084fd211c..7eed5f4f79 100644 --- a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs +++ b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs @@ -26,7 +26,7 @@ namespace Jellyfin.Drawing.Skia { paint.Color = SKColor.Parse("#CC00A4DC"); paint.Style = SKPaintStyle.Fill; - canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint); + canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint); } using (var paint = new SKPaint()) @@ -39,16 +39,13 @@ namespace Jellyfin.Drawing.Skia // or: // var emojiChar = 0x1F680; - var text = "✔️"; - var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32); + const string Text = "✔️"; + var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32); // ask the font manager for a font with that character - var fontManager = SKFontManager.Default; - var emojiTypeface = fontManager.MatchCharacter(emojiChar); + paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar); - paint.Typeface = emojiTypeface; - - canvas.DrawText(text, (float)x - 20, OffsetFromTopRightCorner + 12, paint); + canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint); } } } diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index a67118f188..723c523405 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -205,11 +205,6 @@ namespace Jellyfin.Drawing.Skia /// The file at the specified path could not be used to generate a codec. public ImageDimensions GetImageSize(string path) { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - if (!File.Exists(path)) { throw new FileNotFoundException("File not found", path); @@ -297,7 +292,7 @@ namespace Jellyfin.Drawing.Skia /// The orientation of the image. /// The detected origin of the image. /// The resulting bitmap of the image. - internal SKBitmap Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) + internal SKBitmap? Decode(string path, bool forceCleanBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) { if (!File.Exists(path)) { @@ -348,12 +343,17 @@ namespace Jellyfin.Drawing.Skia return resultBitmap; } - private SKBitmap GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) + private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool forceAnalyzeBitmap, ImageOrientation? orientation, out SKEncodedOrigin origin) { if (cropWhitespace) { using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin)) { + if (bitmap == null) + { + return null; + } + return CropWhiteSpace(bitmap); } } @@ -361,13 +361,11 @@ namespace Jellyfin.Drawing.Skia return Decode(path, forceAnalyzeBitmap, orientation, out origin); } - private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) + private SKBitmap? GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation) { - SKEncodedOrigin origin; - if (autoOrient) { - var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out origin); + var bitmap = GetBitmap(path, cropWhitespace, true, orientation, out var origin); if (bitmap != null && origin != SKEncodedOrigin.TopLeft) { @@ -380,7 +378,7 @@ namespace Jellyfin.Drawing.Skia return bitmap; } - return GetBitmap(path, cropWhitespace, false, orientation, out origin); + return GetBitmap(path, cropWhitespace, false, orientation, out _); } private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin) @@ -517,14 +515,14 @@ namespace Jellyfin.Drawing.Skia /// public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) { - if (string.IsNullOrWhiteSpace(inputPath)) + if (inputPath.Length == 0) { - throw new ArgumentNullException(nameof(inputPath)); + throw new ArgumentException("String can't be empty.", nameof(inputPath)); } - if (string.IsNullOrWhiteSpace(inputPath)) + if (outputPath.Length == 0) { - throw new ArgumentNullException(nameof(outputPath)); + throw new ArgumentException("String can't be empty.", nameof(outputPath)); } var skiaOutputFormat = GetImageFormat(selectedOutputFormat); @@ -538,7 +536,7 @@ namespace Jellyfin.Drawing.Skia { if (bitmap == null) { - throw new ArgumentOutOfRangeException($"Skia unable to read image {inputPath}"); + throw new InvalidDataException($"Skia unable to read image {inputPath}"); } var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height); diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index 0735ef194a..61bef90ec5 100644 --- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -120,13 +120,13 @@ namespace Jellyfin.Drawing.Skia } // resize to the same aspect as the original - int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height); + int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height); using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType)) { currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High); // crop image - int ix = (int)Math.Abs((iWidth - iSlice) / 2); + int ix = Math.Abs((iWidth - iSlice) / 2); using (var image = SKImage.FromBitmap(resizeBitmap)) using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight))) { @@ -141,10 +141,10 @@ namespace Jellyfin.Drawing.Skia return bitmap; } - private SKBitmap GetNextValidImage(string[] paths, int currentIndex, out int newIndex) + private SKBitmap? GetNextValidImage(string[] paths, int currentIndex, out int newIndex) { var imagesTested = new Dictionary(); - SKBitmap bitmap = null; + SKBitmap? bitmap = null; while (imagesTested.Count < paths.Length) { @@ -153,7 +153,7 @@ namespace Jellyfin.Drawing.Skia currentIndex = 0; } - bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out var origin); + bitmap = _skiaEncoder.Decode(paths[currentIndex], false, null, out _); imagesTested[currentIndex] = 0; diff --git a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs index a10fff9dfe..cf3dbde2c0 100644 --- a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs +++ b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs @@ -32,7 +32,7 @@ namespace Jellyfin.Drawing.Skia { paint.Color = SKColor.Parse("#CC00A4DC"); paint.Style = SKPaintStyle.Fill; - canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint); + canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint); } using (var paint = new SKPaint()) @@ -61,7 +61,7 @@ namespace Jellyfin.Drawing.Skia paint.TextSize = 18; } - canvas.DrawText(text, (float)x, y, paint); + canvas.DrawText(text, x, y, paint); } } } diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs index 0432f36b57..459bec1105 100644 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Collections.Generic; From 410a322fe22302eb3c8a37ea38bbe1d7e9d12aff Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 5 Apr 2020 23:30:57 -0400 Subject: [PATCH 113/411] 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 62b0db59aa5a378b49044be5c3c02c03b0ed0de1 Mon Sep 17 00:00:00 2001 From: dafo90 <41897414+dafo90@users.noreply.github.com> Date: Mon, 6 Apr 2020 22:23:53 +0200 Subject: [PATCH 114/411] Fix Authentication request log --- Emby.Server.Implementations/Library/UserManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index d4d97345b9..9164135b49 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -264,6 +264,7 @@ namespace Emby.Server.Implementations.Library { if (string.IsNullOrWhiteSpace(username)) { + _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint); throw new ArgumentNullException(nameof(username)); } @@ -319,6 +320,7 @@ namespace Emby.Server.Implementations.Library if (user == null) { + _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", username, remoteEndPoint); throw new AuthenticationException("Invalid username or password entered."); } @@ -351,14 +353,14 @@ namespace Emby.Server.Implementations.Library } ResetInvalidLoginAttemptCount(user); + _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Name); } else { IncrementInvalidLoginAttemptCount(user); + _logger.LogInformation("Authentication request for {UserName} has been denied (IP: {IP}).", user.Name, remoteEndPoint); } - _logger.LogInformation("Authentication request for {0} {1} (IP: {2}).", user.Name, success ? "has succeeded" : "has been denied", remoteEndPoint); - return success ? user : null; } From 644ddfad00e2a4b42da89badca70474aef2464ff Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Mon, 6 Apr 2020 19:36:44 -0400 Subject: [PATCH 115/411] Apply code review changes --- README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index de5ea3f301..d74ec47669 100644 --- a/README.md +++ b/README.md @@ -66,17 +66,17 @@ Most of the translations can be found in the web client but we have several othe ## Jellyfin Server -This repository contains the code for Jellyfin's back-end server. Note that this is only one of many projects/repositories under the Jellyfin GitHub [organization](https://github.com/jellyfin/). If you want to contribute, can start by checking out our [documentation](https://jellyfin.org/docs/general/contributing/index.html) to see what to work on. +This repository contains the code for Jellyfin's backend server. Note that this is only one of many projects under the Jellyfin GitHub [organization](https://github.com/jellyfin/) on GitHub. If you want to contribute, you can start by checking out our [documentation](https://jellyfin.org/docs/general/contributing/index.html) to see what to work on. ## Server Development -These instructions will help you get set up with a local development environment in order to contribute to this repository. Before you start, please be sure to completely read our [guidelines on development contributions](https://jellyfin.org/docs/general/contributing/development.html). Note that this project is supported on all major operating systems (Windows, Mac and Linux). +These instructions will help you get set up with a local development environment in order to contribute to this repository. Before you start, please be sure to completely read our [guidelines on development contributions](https://jellyfin.org/docs/general/contributing/development.html). Note that this project is supported on all major operating systems except FreeBSD, which is still incompatible. ### Prerequisites Before the project can be built, you must first install the [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download) on your system. -Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but explicit instructions for [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download) are included here. +Instructions to run this project from the command line are included here, but you will also need to install an IDE if you want to debug the server while it is running. Any IDE that supports .NET Core development will work, but two options are recent versions of [Visual Studio](https://visualstudio.microsoft.com/downloads/) (at least 2017) and [Visual Studio Code](https://code.visualstudio.com/Download). ### Cloning the Repository @@ -88,16 +88,20 @@ git clone https://github.com/jellyfin/jellyfin.git ### Installing the Web Client -By default, the server is configured to host the static files required for the [web client](https://github.com/jellyfin/jellyfin-web) in addition to serving the backend API. before you can run the server, you will need to get a copy of the web client files since they are not included in this repository directly. +The server is configured to host the static files required for the [web client](https://github.com/jellyfin/jellyfin-web) in addition to serving the backend by default. Before you can run the server, you will need to get a copy of the web client since they are not included in this repository directly. Note that it is also possible to [host the web client separately](#hosting-the-web-client-separately) from the web server with some additional configuration, in which case you can skip this step. -There are two options to get the files for the web client: +There are three options to get the files for the web client. -1. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web) -2. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located here: `C:\Program Files\Jellyfin\Server\jellyfin-web` +1. Download one of the finished builds from the [Azure DevOps pipeline](https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=11). You can download the build for a specific release by looking at the [branches tab](https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=11&_a=summary&repositoryFilter=6&view=branches) of the pipelines page. +2. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web) +3. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located at `C:\Program Files\Jellyfin\Server\jellyfin-web` -Once you have a copy of the built web client files, you need to copy them into the build output directory of the web server project. For example: `\Jellyfin.Server\bin\Debug\netcoreapp3.1\jellyfin-web` +Once you have a copy of the built web client files, you need to copy them into the build output directory of the web server project. + +* `/jellyfin-web` +* `/Jellyfin.Server/bin/Debug/netcoreapp3.1/jellyfin-web` ### Running The Server @@ -138,7 +142,7 @@ A second option is to build the project and then run the resulting executable fi ### Running The Tests -This repository also includes several unit test projects that are used to validate functionality as part of a CI process. These are several ways to run these tests: +This repository also includes unit tests that are used to validate functionality as part of a CI pipeline on Azure. There are several ways to run these tests. 1. Run tests from the command line using `dotnet test` 2. Run tests in Visual Studio using the [Test Explorer](https://docs.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer) @@ -150,7 +154,7 @@ The following sections describe some more advanced scenarios for running the ser #### Hosting The Web Client Separately -It is not necessary to host the frontend web client as part of the backend server. Hosting these two components separately may be useful for front-end developers who would prefer to host the client in a separate webpack development server for a tighter development loop (see the [jellyfin-web](https://github.com/jellyfin/jellyfin-web#getting-started) repo for instructions on how to do this). +It is not necessary to host the frontend web client as part of the backend server. Hosting these two components separately may be useful for frontend developers who would prefer to host the client in a separate webpack development server for a tighter development loop. See the [jellyfin-web](https://github.com/jellyfin/jellyfin-web#getting-started) repo for instructions on how to do this. To instruct the server not to host the web content, there is a `nowebcontent` configuration flag that must be set. This can specified using the command line switch `--nowebcontent` or the environment variable `JELLYFIN_NOWEBCONTENT=true`. From 0a9b7c868e798769196e2c93e4359f176f2ceb12 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 7 Apr 2020 10:42:16 -0400 Subject: [PATCH 116/411] Specify the directory for jellyfin-web correctly --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d74ec47669..6007aa3cb9 100644 --- a/README.md +++ b/README.md @@ -98,10 +98,11 @@ There are three options to get the files for the web client. 2. Build them from source following the instructions on the [jellyfin-web repository](https://github.com/jellyfin/jellyfin-web) 3. Get the pre-built files from an existing installation of the server. For example, with a Windows server installation the client files are located at `C:\Program Files\Jellyfin\Server\jellyfin-web` -Once you have a copy of the built web client files, you need to copy them into the build output directory of the web server project. +Once you have a copy of the built web client files, you need to copy them into a specific directory. -* `/jellyfin-web` -* `/Jellyfin.Server/bin/Debug/netcoreapp3.1/jellyfin-web` +> `/Mediabrowser.WebDashboard/jellyfin-web` + +As part of the build process, this folder will be copied to the build output directory, where it can be accessed by the server. ### Running The Server From e85f9f5613c009a47c9b59ac59cd5930fc45d96a Mon Sep 17 00:00:00 2001 From: Vasily Date: Tue, 7 Apr 2020 18:41:15 +0300 Subject: [PATCH 117/411] Make localhost LiveTV restreams always use plain HTTP port --- Emby.Server.Implementations/ApplicationHost.cs | 17 +++++++++-------- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 2 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 2 +- .../IServerApplicationHost.cs | 10 +++++++--- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index cb32b8c01b..9af89112c5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1419,7 +1419,7 @@ namespace Emby.Server.Implementations public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; - public async Task GetLocalApiUrl(CancellationToken cancellationToken) + public async Task GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp=false) { try { @@ -1428,7 +1428,7 @@ namespace Emby.Server.Implementations foreach (var address in addresses) { - return GetLocalApiUrl(address); + return GetLocalApiUrl(address, forceHttp); } return null; @@ -1458,7 +1458,7 @@ namespace Emby.Server.Implementations } /// - public string GetLocalApiUrl(IPAddress ipAddress) + public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp=false) { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { @@ -1468,20 +1468,21 @@ namespace Emby.Server.Implementations str.CopyTo(span.Slice(1)); span[^1] = ']'; - return GetLocalApiUrl(span); + return GetLocalApiUrl(span, forceHttp); } - return GetLocalApiUrl(ipAddress.ToString()); + return GetLocalApiUrl(ipAddress.ToString(), forceHttp); } /// - public string GetLocalApiUrl(ReadOnlySpan host) + public string GetLocalApiUrl(ReadOnlySpan host, bool forceHttp=false) { var url = new StringBuilder(64); - url.Append(EnableHttps ? "https://" : "http://") + bool useHttps = EnableHttps && !forceHttp; + url.Append(useHttps ? "https://" : "http://") .Append(host) .Append(':') - .Append(EnableHttps ? HttpsPort : HttpPort); + .Append(useHttps ? HttpsPort : HttpPort); string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; if (baseUrl.Length != 0) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 139aa19a4b..409917245f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1062,7 +1062,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.GetLocalApiUrl("127.0.0.1", true) + "/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..d89a816b3b 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.GetLocalApiUrl("127.0.0.1", true) + "/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..0e600202aa 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.GetLocalApiUrl("127.0.0.1", true) + "/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 608ffc61c2..09f6cb0431 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -65,22 +65,26 @@ namespace MediaBrowser.Controller /// /// Gets the local API URL. /// + /// Token to cancel the request if needed. + /// Whether to force usage of plain HTTP protocol. /// The local API URL. - Task GetLocalApiUrl(CancellationToken cancellationToken); + Task GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp=false); /// /// Gets the local API URL. /// /// The hostname. + /// Whether to force usage of plain HTTP protocol. /// The local API URL. - string GetLocalApiUrl(ReadOnlySpan hostname); + string GetLocalApiUrl(ReadOnlySpan hostname, bool forceHttp=false); /// /// Gets the local API URL. /// /// The IP address. + /// Whether to force usage of plain HTTP protocol. /// The local API URL. - string GetLocalApiUrl(IPAddress address); + string GetLocalApiUrl(IPAddress address, bool forceHttp=false); /// /// Open a URL in an external browser window. From 626d4dab1062050cca8b4755c79da498f4fed0b7 Mon Sep 17 00:00:00 2001 From: Vasily Date: Wed, 8 Apr 2020 13:41:11 +0300 Subject: [PATCH 118/411] Make sure Jellyfin listens on localhost no matter what This is needed by LiveTV --- Jellyfin.Server/Program.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index e55b0d4ed9..efb049a5bb 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -267,9 +267,15 @@ namespace Jellyfin.Server .LocalNetworkAddresses .Select(appHost.NormalizeConfiguredLocalAddress) .Where(i => i != null) - .ToList(); - if (addresses.Any()) + .ToHashSet(); + if (addresses.Any() && !addresses.Contains(IPAddress.Any)) { + if (!addresses.Contains(IPAddress.Loopback)) + { + // we must listen on loopback for LiveTV to function regardless of the settings + addresses.Add(IPAddress.Loopback); + } + foreach (var address in addresses) { _logger.LogInformation("Kestrel listening on {IpAddress}", address); From dd128b5e304db8c0707b14819afeac32dd15d476 Mon Sep 17 00:00:00 2001 From: dafo90 <41897414+dafo90@users.noreply.github.com> Date: Wed, 8 Apr 2020 17:02:32 +0200 Subject: [PATCH 119/411] Log message for each exception during login --- Emby.Server.Implementations/Library/UserManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 9164135b49..15076a194d 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -326,6 +326,7 @@ namespace Emby.Server.Implementations.Library if (user.Policy.IsDisabled) { + _logger.LogInformation("Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", username, remoteEndPoint); throw new AuthenticationException( string.Format( CultureInfo.InvariantCulture, @@ -335,11 +336,13 @@ namespace Emby.Server.Implementations.Library if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) { + _logger.LogInformation("Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", username, remoteEndPoint); throw new AuthenticationException("Forbidden."); } if (!user.IsParentalScheduleAllowed()) { + _logger.LogInformation("Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", username, remoteEndPoint); throw new AuthenticationException("User is not allowed access at this time."); } From 7b2fca96e34339e028350181a51f249b91f71fba Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 9 Apr 2020 15:43:47 +0800 Subject: [PATCH 120/411] use pre-compiled deb to avoid non-free drivers --- Dockerfile | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index caac7500a5..e3a5e41b38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ ARG DOTNET_VERSION=3.1 -ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master @@ -17,7 +16,6 @@ ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 # see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM debian:buster-slim # https://askubuntu.com/questions/972516/debian-frontend-environment-variable @@ -27,32 +25,26 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" -COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web # Install dependencies: -# libfontconfig1: needed for Skia -# libgomp1: needed for ffmpeg -# libva-drm2: needed for ffmpeg -# mesa-va-drivers: needed for VAAPI +# mesa-va-drivers: needed for AMD VAAPI RUN apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \ + && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | sudo apt-key add - \ + && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | sudo tee /etc/apt/sources.list.d/jellyfin.list \ + && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ - libfontconfig1 \ - libgomp1 \ - libva-drm2 \ mesa-va-drivers \ + jellyfin-ffmpeg \ openssl \ - ca-certificates \ - vainfo \ - i965-va-driver \ locales \ - && apt-get clean autoclean -y\ - && apt-get autoremove -y\ + && apt-get remove gnupg wget apt-transport-https -y \ + && apt-get clean autoclean -y \ + && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media \ - && ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \ - && ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 @@ -65,4 +57,4 @@ VOLUME /cache /config /media ENTRYPOINT ["./jellyfin/jellyfin", \ "--datadir", "/config", \ "--cachedir", "/cache", \ - "--ffmpeg", "/usr/local/bin/ffmpeg"] + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"] From 8105494cb5bbab9f8930d7aecc1b7a2a3082c810 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 9 Apr 2020 17:37:57 +0800 Subject: [PATCH 121/411] minor changes --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e3a5e41b38..6e834d4e0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,8 +31,8 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web # mesa-va-drivers: needed for AMD VAAPI RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \ - && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | sudo apt-key add - \ - && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | sudo tee /etc/apt/sources.list.d/jellyfin.list \ + && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \ + && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \ && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ mesa-va-drivers \ From 9eab678487e107939e206ec66b0f573463486082 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:17:38 -0400 Subject: [PATCH 122/411] Improve Fedora spec and add metapackage --- fedora/jellyfin.spec | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index c15b4ee957..4071babcdc 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -6,10 +6,10 @@ %global dotnet_runtime centos-x64 %endif -Name: jellyfin-server +Name: jellyfin Version: 10.6.0 Release: 1%{?dist} -Summary: The Free Software Media System Server backend and API +Summary: The Free Software Media System License: GPLv3 URL: https://jellyfin.media # Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` @@ -23,22 +23,27 @@ Source16: jellyfin-firewalld.xml %{?systemd_requires} BuildRequires: systemd -Requires(pre): shadow-utils BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel -Requires: libcurl, fontconfig, freetype, openssl, glibc libicu # Requirements not packaged in main repos # COPR @dotnet-sig/dotnet or # https://packages.microsoft.com/rhel/7/prod/ BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 -# RPMfusion free -Requires: ffmpeg - +Requires: %{name}-server = %{version}-%{release}, %{name}-web >= 1, %{name}-web < 2 # Disable Automatic Dependency Processing AutoReqProv: no %description Jellyfin is a free software media system that puts you in control of managing and streaming your media. +%package server +# RPMfusion free +Summary: The Free Software Media System Server backend +Requires(pre): shadow-utils +Requires: ffmpeg +Requires: libcurl, fontconfig, freetype, openssl, glibc libicu + +%description server +The Jellyfin media server backend. %prep %autosetup -n jellyfin-server-%{version} -b 0 @@ -69,7 +74,7 @@ EOF %{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh %{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml -%files +%files server %attr(755,root,root) %{_bindir}/jellyfin %{_libdir}/jellyfin/*.json %{_libdir}/jellyfin/*.dll @@ -92,14 +97,14 @@ EOF %attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin %{_datadir}/licenses/jellyfin/LICENSE -%pre +%pre server getent group jellyfin >/dev/null || groupadd -r jellyfin getent passwd jellyfin >/dev/null || \ useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \ -c "Jellyfin default user" jellyfin exit 0 -%post +%post server # Move existing configuration cache and logs to their new locations and symlink them. if [ $1 -gt 1 ] ; then service_state=$(systemctl is-active jellyfin.service) @@ -127,10 +132,10 @@ if [ $1 -gt 1 ] ; then fi %systemd_post jellyfin.service -%preun +%preun server %systemd_preun jellyfin.service -%postun +%postun server %systemd_postun_with_restart jellyfin.service %changelog From c10df2fe85e06efb509a889bd7aae8ab5dc3a512 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:33:22 -0400 Subject: [PATCH 123/411] Improve dpkg handling in build.sh --- build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 86c8447933..b4792a25ed 100755 --- a/build.sh +++ b/build.sh @@ -34,7 +34,7 @@ list_platforms() { } do_build_native() { - if [[ $( dpkg --print-architecture | head -1 ) != "${PLATFORM##*.}" ]]; then + if [[ -f $( which dpkg ) && $( dpkg --print-architecture | head -1 ) != "${PLATFORM##*.}" ]]; then echo "Cross-building is not supported for native builds, use 'docker' builds on amd64 for cross-building." exit 1 fi @@ -43,7 +43,7 @@ do_build_native() { } do_build_docker() { - if ! dpkg --print-architecture | grep -q 'amd64'; then + if [[ -f $( which dpkg ) && $( dpkg --print-architecture | head -1 ) != "amd64" ]]; then echo "Docker-based builds only support amd64-based cross-building; use a 'native' build instead." exit 1 fi From 529a9ae544dfd804466d852d3c9babf1a660a1eb Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:37:00 -0400 Subject: [PATCH 124/411] Don't remove already-moved files --- bump_version | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bump_version b/bump_version index c868c541e6..46b7f86e05 100755 --- a/bump_version +++ b/bump_version @@ -67,8 +67,6 @@ echo -e "jellyfin (${new_version_deb}) unstable; urgency=medium cat ${debian_changelog_file} >> ${debian_changelog_temp} # Move into place mv ${debian_changelog_temp} ${debian_changelog_file} -# Clean up -rm -f ${debian_changelog_temp} # Write out a temporary Yum changelog with our new stuff prepended and some templated formatting fedora_spec_file="fedora/jellyfin.spec" @@ -95,7 +93,7 @@ popd # Move into place mv ${fedora_spec_temp} ${fedora_spec_file} # Clean up -rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir} +rm -rf ${fedora_spec_temp_dir} # Stage the changed files for commit git add ${shared_version_file} ${build_file} ${debian_equivs_file} ${debian_changelog_file} ${fedora_spec_file} From b0e80b486b5a5047f78afd1f680b354daed94542 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:40:04 -0400 Subject: [PATCH 125/411] Use jellyfin.org everywhere --- Emby.Notifications/NotificationEntryPoint.cs | 2 +- debian/control | 2 +- fedora/jellyfin.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index befecc570b..869b7407e2 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -143,7 +143,7 @@ namespace Emby.Notifications var notification = new NotificationRequest { - Description = "Please see jellyfin.media for details.", + Description = "Please see jellyfin.org for details.", NotificationType = type, Name = _localization.GetLocalizedString("NewVersionIsAvailable") }; diff --git a/debian/control b/debian/control index 648e28ae81..5559700cf5 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Build-Depends: debhelper (>= 9), libfreetype6-dev, libssl-dev Standards-Version: 3.9.4 -Homepage: https://jellyfin.media/ +Homepage: https://jellyfin.org/ Vcs-Git: https://github.org/jellyfin/jellyfin.git Vcs-Browser: https://github.org/jellyfin/jellyfin diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 4071babcdc..4e1045d740 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -11,7 +11,7 @@ Version: 10.6.0 Release: 1%{?dist} Summary: The Free Software Media System License: GPLv3 -URL: https://jellyfin.media +URL: https://jellyfin.org # Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` Source0: jellyfin-server-%{version}.tar.gz Source11: jellyfin.service From 406d087a465dd62ad376124fcb53692b1f666aef Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:46:16 -0400 Subject: [PATCH 126/411] Correct ARCH var in Ubuntu Dockerfiles --- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index e34ef7edd1..f91b91cd46 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -7,7 +7,7 @@ ARG SDK_VERSION=3.1 ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=arm64 +ENV ARCH=amd64 ENV IS_DOCKER=YES # Prepare Debian build environment diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 6f92c81ab1..85414614c0 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -7,7 +7,7 @@ ARG SDK_VERSION=3.1 ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=armhf +ENV ARCH=amd64 ENV IS_DOCKER=YES # Prepare Debian build environment From ed735522cfd4ab8edfd7be2e4f6ce52856eb43cd Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:49:14 -0400 Subject: [PATCH 127/411] Revert "Remove old stuff" This reverts commit b9fdd96ece39a6ff0f4ff37ecba36d7a0f65fcba. --- deployment/old/README.md | 62 ++++++ deployment/old/centos-package-x64/Dockerfile | 39 ++++ deployment/old/centos-package-x64/clean.sh | 32 ++++ .../old/centos-package-x64/dependencies.txt | 1 + .../old/centos-package-x64/docker-build.sh | 18 ++ deployment/old/centos-package-x64/package.sh | 34 ++++ deployment/old/centos-package-x64/pkg-src | 1 + .../old/debian-package-arm64/Dockerfile.amd64 | 43 +++++ .../old/debian-package-arm64/Dockerfile.arm64 | 34 ++++ deployment/old/debian-package-arm64/clean.sh | 27 +++ .../old/debian-package-arm64/dependencies.txt | 1 + .../old/debian-package-arm64/docker-build.sh | 21 ++ .../old/debian-package-arm64/package.sh | 45 +++++ deployment/old/debian-package-arm64/pkg-src | 1 + .../old/debian-package-armhf/Dockerfile.amd64 | 42 ++++ .../old/debian-package-armhf/Dockerfile.armhf | 34 ++++ deployment/old/debian-package-armhf/clean.sh | 27 +++ .../old/debian-package-armhf/dependencies.txt | 1 + .../old/debian-package-armhf/docker-build.sh | 21 ++ .../old/debian-package-armhf/package.sh | 45 +++++ deployment/old/debian-package-armhf/pkg-src | 1 + deployment/old/debian-package-x64/Dockerfile | 34 ++++ deployment/old/debian-package-x64/clean.sh | 27 +++ .../old/debian-package-x64/dependencies.txt | 1 + .../old/debian-package-x64/docker-build.sh | 20 ++ deployment/old/debian-package-x64/package.sh | 34 ++++ .../old/debian-package-x64/pkg-src/changelog | 59 ++++++ .../old/debian-package-x64/pkg-src/compat | 1 + .../debian-package-x64/pkg-src/conf/jellyfin | 40 ++++ .../pkg-src/conf/jellyfin-sudoers | 37 ++++ .../pkg-src/conf/jellyfin.service.conf | 7 + .../pkg-src/conf/logging.json | 30 +++ .../old/debian-package-x64/pkg-src/control | 31 +++ .../old/debian-package-x64/pkg-src/copyright | 29 +++ .../old/debian-package-x64/pkg-src/gbp.conf | 6 + .../old/debian-package-x64/pkg-src/install | 6 + .../debian-package-x64/pkg-src/jellyfin.init | 61 ++++++ .../pkg-src/jellyfin.service | 14 ++ .../pkg-src/jellyfin.upstart | 20 ++ .../debian-package-x64/pkg-src/po/POTFILES.in | 1 + .../pkg-src/po/templates.pot | 57 ++++++ .../old/debian-package-x64/pkg-src/postinst | 92 +++++++++ .../old/debian-package-x64/pkg-src/postrm | 81 ++++++++ .../old/debian-package-x64/pkg-src/preinst | 78 ++++++++ .../old/debian-package-x64/pkg-src/prerm | 61 ++++++ .../old/debian-package-x64/pkg-src/rules | 66 +++++++ .../pkg-src/source.lintian-overrides | 3 + .../debian-package-x64/pkg-src/source/format | 1 + .../debian-package-x64/pkg-src/source/options | 11 ++ deployment/old/fedora-package-x64/Dockerfile | 33 ++++ deployment/old/fedora-package-x64/clean.sh | 32 ++++ .../old/fedora-package-x64/dependencies.txt | 1 + .../old/fedora-package-x64/docker-build.sh | 18 ++ deployment/old/fedora-package-x64/package.sh | 34 ++++ .../old/fedora-package-x64/pkg-src/.gitignore | 3 + .../old/fedora-package-x64/pkg-src/README.md | 43 +++++ .../pkg-src/jellyfin-firewalld.xml | 9 + .../fedora-package-x64/pkg-src/jellyfin.env | 34 ++++ .../pkg-src/jellyfin.override.conf | 7 + .../pkg-src/jellyfin.service | 15 ++ .../fedora-package-x64/pkg-src/jellyfin.spec | 181 ++++++++++++++++++ .../pkg-src/jellyfin.sudoers | 19 ++ .../old/fedora-package-x64/pkg-src/restart.sh | 36 ++++ deployment/old/linux-x64/Dockerfile | 37 ++++ deployment/old/linux-x64/clean.sh | 27 +++ deployment/old/linux-x64/dependencies.txt | 1 + deployment/old/linux-x64/docker-build.sh | 36 ++++ deployment/old/linux-x64/package.sh | 34 ++++ deployment/old/macos/Dockerfile | 37 ++++ deployment/old/macos/clean.sh | 27 +++ deployment/old/macos/dependencies.txt | 1 + deployment/old/macos/docker-build.sh | 36 ++++ deployment/old/macos/package.sh | 34 ++++ deployment/old/portable/Dockerfile | 37 ++++ deployment/old/portable/clean.sh | 27 +++ deployment/old/portable/dependencies.txt | 1 + deployment/old/portable/docker-build.sh | 36 ++++ deployment/old/portable/package.sh | 34 ++++ .../old/ubuntu-package-arm64/Dockerfile.amd64 | 59 ++++++ .../old/ubuntu-package-arm64/Dockerfile.arm64 | 40 ++++ deployment/old/ubuntu-package-arm64/clean.sh | 27 +++ .../old/ubuntu-package-arm64/dependencies.txt | 1 + .../old/ubuntu-package-arm64/docker-build.sh | 21 ++ .../old/ubuntu-package-arm64/package.sh | 45 +++++ deployment/old/ubuntu-package-arm64/pkg-src | 1 + .../old/ubuntu-package-armhf/Dockerfile.amd64 | 59 ++++++ .../old/ubuntu-package-armhf/Dockerfile.armhf | 40 ++++ deployment/old/ubuntu-package-armhf/clean.sh | 27 +++ .../old/ubuntu-package-armhf/dependencies.txt | 1 + .../old/ubuntu-package-armhf/docker-build.sh | 21 ++ .../old/ubuntu-package-armhf/package.sh | 45 +++++ deployment/old/ubuntu-package-armhf/pkg-src | 1 + deployment/old/ubuntu-package-x64/Dockerfile | 36 ++++ deployment/old/ubuntu-package-x64/clean.sh | 27 +++ .../old/ubuntu-package-x64/dependencies.txt | 1 + .../old/ubuntu-package-x64/docker-build.sh | 20 ++ deployment/old/ubuntu-package-x64/package.sh | 34 ++++ deployment/old/ubuntu-package-x64/pkg-src | 1 + .../old/unraid/docker-templates/README.md | 15 ++ .../old/unraid/docker-templates/jellyfin.xml | 57 ++++++ deployment/old/win-x64/Dockerfile | 37 ++++ deployment/old/win-x64/clean.sh | 27 +++ deployment/old/win-x64/dependencies.txt | 1 + deployment/old/win-x64/docker-build.sh | 61 ++++++ deployment/old/win-x64/package.sh | 34 ++++ deployment/old/win-x86/Dockerfile | 37 ++++ deployment/old/win-x86/clean.sh | 27 +++ deployment/old/win-x86/dependencies.txt | 1 + deployment/old/win-x86/docker-build.sh | 61 ++++++ deployment/old/win-x86/package.sh | 34 ++++ 110 files changed, 3207 insertions(+) create mode 100644 deployment/old/README.md create mode 100644 deployment/old/centos-package-x64/Dockerfile create mode 100755 deployment/old/centos-package-x64/clean.sh create mode 100644 deployment/old/centos-package-x64/dependencies.txt create mode 100755 deployment/old/centos-package-x64/docker-build.sh create mode 100755 deployment/old/centos-package-x64/package.sh create mode 120000 deployment/old/centos-package-x64/pkg-src create mode 100644 deployment/old/debian-package-arm64/Dockerfile.amd64 create mode 100644 deployment/old/debian-package-arm64/Dockerfile.arm64 create mode 100755 deployment/old/debian-package-arm64/clean.sh create mode 100644 deployment/old/debian-package-arm64/dependencies.txt create mode 100755 deployment/old/debian-package-arm64/docker-build.sh create mode 100755 deployment/old/debian-package-arm64/package.sh create mode 120000 deployment/old/debian-package-arm64/pkg-src create mode 100644 deployment/old/debian-package-armhf/Dockerfile.amd64 create mode 100644 deployment/old/debian-package-armhf/Dockerfile.armhf create mode 100755 deployment/old/debian-package-armhf/clean.sh create mode 100644 deployment/old/debian-package-armhf/dependencies.txt create mode 100755 deployment/old/debian-package-armhf/docker-build.sh create mode 100755 deployment/old/debian-package-armhf/package.sh create mode 120000 deployment/old/debian-package-armhf/pkg-src create mode 100644 deployment/old/debian-package-x64/Dockerfile create mode 100755 deployment/old/debian-package-x64/clean.sh create mode 100644 deployment/old/debian-package-x64/dependencies.txt create mode 100755 deployment/old/debian-package-x64/docker-build.sh create mode 100755 deployment/old/debian-package-x64/package.sh create mode 100644 deployment/old/debian-package-x64/pkg-src/changelog create mode 100644 deployment/old/debian-package-x64/pkg-src/compat create mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin create mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers create mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf create mode 100644 deployment/old/debian-package-x64/pkg-src/conf/logging.json create mode 100644 deployment/old/debian-package-x64/pkg-src/control create mode 100644 deployment/old/debian-package-x64/pkg-src/copyright create mode 100644 deployment/old/debian-package-x64/pkg-src/gbp.conf create mode 100644 deployment/old/debian-package-x64/pkg-src/install create mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.init create mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.service create mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.upstart create mode 100644 deployment/old/debian-package-x64/pkg-src/po/POTFILES.in create mode 100644 deployment/old/debian-package-x64/pkg-src/po/templates.pot create mode 100644 deployment/old/debian-package-x64/pkg-src/postinst create mode 100644 deployment/old/debian-package-x64/pkg-src/postrm create mode 100644 deployment/old/debian-package-x64/pkg-src/preinst create mode 100644 deployment/old/debian-package-x64/pkg-src/prerm create mode 100755 deployment/old/debian-package-x64/pkg-src/rules create mode 100644 deployment/old/debian-package-x64/pkg-src/source.lintian-overrides create mode 100644 deployment/old/debian-package-x64/pkg-src/source/format create mode 100644 deployment/old/debian-package-x64/pkg-src/source/options create mode 100644 deployment/old/fedora-package-x64/Dockerfile create mode 100755 deployment/old/fedora-package-x64/clean.sh create mode 100644 deployment/old/fedora-package-x64/dependencies.txt create mode 100755 deployment/old/fedora-package-x64/docker-build.sh create mode 100755 deployment/old/fedora-package-x64/package.sh create mode 100644 deployment/old/fedora-package-x64/pkg-src/.gitignore create mode 100644 deployment/old/fedora-package-x64/pkg-src/README.md create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.env create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.service create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.spec create mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers create mode 100755 deployment/old/fedora-package-x64/pkg-src/restart.sh create mode 100644 deployment/old/linux-x64/Dockerfile create mode 100755 deployment/old/linux-x64/clean.sh create mode 100644 deployment/old/linux-x64/dependencies.txt create mode 100755 deployment/old/linux-x64/docker-build.sh create mode 100755 deployment/old/linux-x64/package.sh create mode 100644 deployment/old/macos/Dockerfile create mode 100755 deployment/old/macos/clean.sh create mode 100644 deployment/old/macos/dependencies.txt create mode 100755 deployment/old/macos/docker-build.sh create mode 100755 deployment/old/macos/package.sh create mode 100644 deployment/old/portable/Dockerfile create mode 100755 deployment/old/portable/clean.sh create mode 100644 deployment/old/portable/dependencies.txt create mode 100755 deployment/old/portable/docker-build.sh create mode 100755 deployment/old/portable/package.sh create mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.amd64 create mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.arm64 create mode 100755 deployment/old/ubuntu-package-arm64/clean.sh create mode 100644 deployment/old/ubuntu-package-arm64/dependencies.txt create mode 100755 deployment/old/ubuntu-package-arm64/docker-build.sh create mode 100755 deployment/old/ubuntu-package-arm64/package.sh create mode 120000 deployment/old/ubuntu-package-arm64/pkg-src create mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.amd64 create mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.armhf create mode 100755 deployment/old/ubuntu-package-armhf/clean.sh create mode 100644 deployment/old/ubuntu-package-armhf/dependencies.txt create mode 100755 deployment/old/ubuntu-package-armhf/docker-build.sh create mode 100755 deployment/old/ubuntu-package-armhf/package.sh create mode 120000 deployment/old/ubuntu-package-armhf/pkg-src create mode 100644 deployment/old/ubuntu-package-x64/Dockerfile create mode 100755 deployment/old/ubuntu-package-x64/clean.sh create mode 100644 deployment/old/ubuntu-package-x64/dependencies.txt create mode 100755 deployment/old/ubuntu-package-x64/docker-build.sh create mode 100755 deployment/old/ubuntu-package-x64/package.sh create mode 120000 deployment/old/ubuntu-package-x64/pkg-src create mode 100644 deployment/old/unraid/docker-templates/README.md create mode 100644 deployment/old/unraid/docker-templates/jellyfin.xml create mode 100644 deployment/old/win-x64/Dockerfile create mode 100755 deployment/old/win-x64/clean.sh create mode 100644 deployment/old/win-x64/dependencies.txt create mode 100755 deployment/old/win-x64/docker-build.sh create mode 100755 deployment/old/win-x64/package.sh create mode 100644 deployment/old/win-x86/Dockerfile create mode 100755 deployment/old/win-x86/clean.sh create mode 100644 deployment/old/win-x86/dependencies.txt create mode 100755 deployment/old/win-x86/docker-build.sh create mode 100755 deployment/old/win-x86/package.sh diff --git a/deployment/old/README.md b/deployment/old/README.md new file mode 100644 index 0000000000..a805f59ca3 --- /dev/null +++ b/deployment/old/README.md @@ -0,0 +1,62 @@ +# Jellyfin Packaging + +This directory contains the packaging configuration of Jellyfin for multiple platforms. The specification is below; all package platforms must follow the specification to be compatable with the central `build` script. + +## Package List + +### Operating System Packages + +* `debian-package-x64`: Package for Debian and Ubuntu amd64 systems. +* `fedora-package-x64`: Package for Fedora, CentOS, and Red Hat Enterprise Linux amd64 systems. + +### Portable Builds (archives) + +* `linux-x64`: Portable binary archive for generic Linux amd64 systems. +* `macos`: Portable binary archive for MacOS amd64 systems. +* `win-x64`: Portable binary archive for Windows amd64 systems. +* `win-x86`: Portable binary archive for Windows i386 systems. + +### Other Builds + +These builds are not necessarily run from the `build` script, but are present for other platforms. + +* `portable`: Compiled `.dll` for use with .NET Core runtime on any system. +* `docker`: Docker manifests for auto-publishing. +* `unraid`: unRaid Docker template; not built by `build` but imported into unRaid directly. +* `windows`: Support files and scripts for Windows CI build. + +## Package Specification + +### Dependencies + +* If a platform requires additional build dependencies, the required binary names, i.e. to validate `which `, should be specified in a `dependencies.txt` file inside the platform directory. + +* Each dependency should be present on its own line. + +### Action Scripts + +* Actions are defined in BASH scripts with the name `.sh` within the platform directory. + +* The list of valid actions are: + + 1. `build`: Builds a set of binaries. + 2. `package`: Assembles the compiled binaries into a package. + 3. `sign`: Performs signing actions on a package. + 4. `publish`: Performs a publishing action for a package. + 5. `clean`: Cleans up any artifacts from the previous actions. + +* All package actions are optional, however at least one should generate output files, and any that do should contain a `clean` action. + +* Actions are executed in the order specified above, and later actions may depend on former actions. + +* Actions except for `clean` should `set -o errexit` to terminate on failed actions. + +* The `clean` action should always `exit 0` even if no work is done or it fails. + +* The `clean` action can be passed a variable as argument 1, named `keep_artifacts`, containing either the value `y` or `n`. It is indended to handle situations when the user runs `build --keep-artifacts` and should be handled intelligently. Usually, this is used to preserve Docker images while still removing temporary directories. + +### Output Files + +* Upon completion of the defined actions, at least one output file must be created in the `/pkg-dist` directory. + +* Output files will be moved to the directory `jellyfin-build/` one directory above the repository root upon completion. diff --git a/deployment/old/centos-package-x64/Dockerfile b/deployment/old/centos-package-x64/Dockerfile new file mode 100644 index 0000000000..08219a2e4a --- /dev/null +++ b/deployment/old/centos-package-x64/Dockerfile @@ -0,0 +1,39 @@ +FROM centos:7 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/centos-package-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist + +# Prepare CentOS environment +RUN yum update -y \ + && yum install -y epel-release + +# Install build dependencies +RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git + +# Install recent NodeJS and Yarn +RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \ + && rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \ + && yum install -y yarn + +# Install DotNET SDK +RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \ + && rpmdev-setuptree \ + && yum install -y dotnet-sdk-${SDK_VERSION} + +# Create symlinks and directories +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/centos-package-x64/clean.sh b/deployment/old/centos-package-x64/clean.sh new file mode 100755 index 0000000000..31455de0d4 --- /dev/null +++ b/deployment/old/centos-package-x64/clean.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" +VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +package_source_dir="${WORKDIR}/pkg-src" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-centos-build" + +rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ + || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/centos-package-x64/dependencies.txt b/deployment/old/centos-package-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/centos-package-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/centos-package-x64/docker-build.sh b/deployment/old/centos-package-x64/docker-build.sh new file mode 100755 index 0000000000..62dd144e50 --- /dev/null +++ b/deployment/old/centos-package-x64/docker-build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Builds the RPM inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Build RPM +make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/rpm +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/centos-package-x64/package.sh b/deployment/old/centos-package-x64/package.sh new file mode 100755 index 0000000000..1b983f49d9 --- /dev/null +++ b/deployment/old/centos-package-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-centos-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the RPMs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the RPMs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/centos-package-x64/pkg-src b/deployment/old/centos-package-x64/pkg-src new file mode 120000 index 0000000000..3ff4d3cbf5 --- /dev/null +++ b/deployment/old/centos-package-x64/pkg-src @@ -0,0 +1 @@ +../fedora-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-arm64/Dockerfile.amd64 b/deployment/old/debian-package-arm64/Dockerfile.amd64 new file mode 100644 index 0000000000..b63e08b7dd --- /dev/null +++ b/deployment/old/debian-package-arm64/Dockerfile.amd64 @@ -0,0 +1,43 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN dpkg --add-architecture arm64 \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 8 \ + && cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \ + && apt-get install -y gcc-8-source libstdc++-8-dev-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64 + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] +#ENTRYPOINT ["/bin/bash"] diff --git a/deployment/old/debian-package-arm64/Dockerfile.arm64 b/deployment/old/debian-package-arm64/Dockerfile.arm64 new file mode 100644 index 0000000000..9ca4868441 --- /dev/null +++ b/deployment/old/debian-package-arm64/Dockerfile.arm64 @@ -0,0 +1,34 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=arm64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-arm64/clean.sh b/deployment/old/debian-package-arm64/clean.sh new file mode 100755 index 0000000000..e7bfdf8b4b --- /dev/null +++ b/deployment/old/debian-package-arm64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian_arm64-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/debian-package-arm64/dependencies.txt b/deployment/old/debian-package-arm64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/debian-package-arm64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/debian-package-arm64/docker-build.sh b/deployment/old/debian-package-arm64/docker-build.sh new file mode 100755 index 0000000000..67ab6bd74b --- /dev/null +++ b/deployment/old/debian-package-arm64/docker-build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -aarm64 + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-arm64/package.sh b/deployment/old/debian-package-arm64/package.sh new file mode 100755 index 0000000000..2091982187 --- /dev/null +++ b/deployment/old/debian-package-arm64/package.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +ARCH="$( arch )" +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian_arm64-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Determine which Dockerfile to use +case $ARCH in + 'x86_64') + DOCKERFILE="Dockerfile.amd64" + ;; + 'armv7l') + DOCKERFILE="Dockerfile.arm64" + ;; +esac + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-arm64/pkg-src b/deployment/old/debian-package-arm64/pkg-src new file mode 120000 index 0000000000..4c695fea17 --- /dev/null +++ b/deployment/old/debian-package-arm64/pkg-src @@ -0,0 +1 @@ +../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/debian-package-armhf/Dockerfile.amd64 b/deployment/old/debian-package-armhf/Dockerfile.amd64 new file mode 100644 index 0000000000..1b64b53148 --- /dev/null +++ b/deployment/old/debian-package-armhf/Dockerfile.amd64 @@ -0,0 +1,42 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Prepare the cross-toolchain +RUN dpkg --add-architecture armhf \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 8 \ + && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \ + && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip binutils-arm-linux-gnueabihf libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/Dockerfile.armhf b/deployment/old/debian-package-armhf/Dockerfile.armhf new file mode 100644 index 0000000000..dd398b5aa5 --- /dev/null +++ b/deployment/old/debian-package-armhf/Dockerfile.armhf @@ -0,0 +1,34 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=armhf + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/clean.sh b/deployment/old/debian-package-armhf/clean.sh new file mode 100755 index 0000000000..35a3d3e9ad --- /dev/null +++ b/deployment/old/debian-package-armhf/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian_armhf-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/debian-package-armhf/dependencies.txt b/deployment/old/debian-package-armhf/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/debian-package-armhf/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/debian-package-armhf/docker-build.sh b/deployment/old/debian-package-armhf/docker-build.sh new file mode 100755 index 0000000000..1bd7fb2911 --- /dev/null +++ b/deployment/old/debian-package-armhf/docker-build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -aarmhf + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-armhf/package.sh b/deployment/old/debian-package-armhf/package.sh new file mode 100755 index 0000000000..4a27dd8283 --- /dev/null +++ b/deployment/old/debian-package-armhf/package.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +ARCH="$( arch )" +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian_armhf-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Determine which Dockerfile to use +case $ARCH in + 'x86_64') + DOCKERFILE="Dockerfile.amd64" + ;; + 'armv7l') + DOCKERFILE="Dockerfile.armhf" + ;; +esac + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-armhf/pkg-src b/deployment/old/debian-package-armhf/pkg-src new file mode 120000 index 0000000000..0bb6d55249 --- /dev/null +++ b/deployment/old/debian-package-armhf/pkg-src @@ -0,0 +1 @@ +../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-x64/Dockerfile b/deployment/old/debian-package-x64/Dockerfile new file mode 100644 index 0000000000..e863d1edf9 --- /dev/null +++ b/deployment/old/debian-package-x64/Dockerfile @@ -0,0 +1,34 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-x64/clean.sh b/deployment/old/debian-package-x64/clean.sh new file mode 100755 index 0000000000..4e507bcb27 --- /dev/null +++ b/deployment/old/debian-package-x64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/debian-package-x64/dependencies.txt b/deployment/old/debian-package-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/debian-package-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/debian-package-x64/docker-build.sh b/deployment/old/debian-package-x64/docker-build.sh new file mode 100755 index 0000000000..962a522ebc --- /dev/null +++ b/deployment/old/debian-package-x64/docker-build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +dpkg-buildpackage -us -uc + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-x64/package.sh b/deployment/old/debian-package-x64/package.sh new file mode 100755 index 0000000000..5a416959ab --- /dev/null +++ b/deployment/old/debian-package-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-debian-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-x64/pkg-src/changelog b/deployment/old/debian-package-x64/pkg-src/changelog new file mode 100644 index 0000000000..51c4822370 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/changelog @@ -0,0 +1,59 @@ +jellyfin (10.5.0-1) unstable; urgency=medium + + * New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 + + -- Jellyfin Packaging Team Fri, 11 Oct 2019 20:12:38 -0400 + +jellyfin (10.4.0-1) unstable; urgency=medium + + * New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 + + -- Jellyfin Packaging Team Sat, 31 Aug 2019 21:38:56 -0400 + +jellyfin (10.3.7-1) unstable; urgency=medium + + * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 + + -- Jellyfin Packaging Team Wed, 24 Jul 2019 10:48:28 -0400 + +jellyfin (10.3.6-1) unstable; urgency=medium + + * New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 + + -- Jellyfin Packaging Team Sat, 06 Jul 2019 13:34:19 -0400 + +jellyfin (10.3.5-1) unstable; urgency=medium + + * New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 + + -- Jellyfin Packaging Team Sun, 09 Jun 2019 21:47:35 -0400 + +jellyfin (10.3.4-1) unstable; urgency=medium + + * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 + + -- Jellyfin Packaging Team Thu, 06 Jun 2019 22:45:31 -0400 + +jellyfin (10.3.3-1) unstable; urgency=medium + + * New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 + + -- Jellyfin Packaging Team Fri, 17 May 2019 23:12:08 -0400 + +jellyfin (10.3.2-1) unstable; urgency=medium + + * New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 + + -- Jellyfin Packaging Team Tue, 30 Apr 2019 20:18:44 -0400 + +jellyfin (10.3.1-1) unstable; urgency=medium + + * New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 + + -- Jellyfin Packaging Team Sat, 20 Apr 2019 14:24:07 -0400 + +jellyfin (10.3.0-1) unstable; urgency=medium + + * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 + + -- Jellyfin Packaging Team Fri, 19 Apr 2019 14:24:29 -0400 diff --git a/deployment/old/debian-package-x64/pkg-src/compat b/deployment/old/debian-package-x64/pkg-src/compat new file mode 100644 index 0000000000..45a4fb75db --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/compat @@ -0,0 +1 @@ +8 diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin new file mode 100644 index 0000000000..c6e595f15a --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin @@ -0,0 +1,40 @@ +# Jellyfin default configuration options +# This is a POSIX shell fragment + +# Use this file to override the default configurations; add additional +# options with JELLYFIN_ADD_OPTS. + +# Under systemd, use +# /etc/systemd/system/jellyfin.service.d/jellyfin.service.conf +# to override the user or this config file's location. + +# +# General options +# + +# Program directories +JELLYFIN_DATA_DIR="/var/lib/jellyfin" +JELLYFIN_CONFIG_DIR="/etc/jellyfin" +JELLYFIN_LOG_DIR="/var/log/jellyfin" +JELLYFIN_CACHE_DIR="/var/cache/jellyfin" + +# Restart script for in-app server control +JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh" + +# ffmpeg binary paths, overriding the system values +JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" + +# [OPTIONAL] run Jellyfin as a headless service +#JELLYFIN_SERVICE_OPT="--service" + +# [OPTIONAL] run Jellyfin without the web app +#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" + +# +# SysV init/Upstart options +# + +# Application username +JELLYFIN_USER="jellyfin" +# Full application command +JELLYFIN_ARGS="$JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers new file mode 100644 index 0000000000..b481ba4ad4 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers @@ -0,0 +1,37 @@ +#Allow jellyfin group to start, stop and restart itself +Cmnd_Alias RESTARTSERVER_SYSV = /sbin/service jellyfin restart, /usr/sbin/service jellyfin restart +Cmnd_Alias STARTSERVER_SYSV = /sbin/service jellyfin start, /usr/sbin/service jellyfin start +Cmnd_Alias STOPSERVER_SYSV = /sbin/service jellyfin stop, /usr/sbin/service jellyfin stop +Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin +Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin +Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin +Cmnd_Alias RESTARTSERVER_INITD = /etc/init.d/jellyfin restart +Cmnd_Alias STARTSERVER_INITD = /etc/init.d/jellyfin start +Cmnd_Alias STOPSERVER_INITD = /etc/init.d/jellyfin stop + + +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSV +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_INITD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_INITD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_INITD + +Defaults!RESTARTSERVER_SYSV !requiretty +Defaults!STARTSERVER_SYSV !requiretty +Defaults!STOPSERVER_SYSV !requiretty +Defaults!RESTARTSERVER_SYSTEMD !requiretty +Defaults!STARTSERVER_SYSTEMD !requiretty +Defaults!STOPSERVER_SYSTEMD !requiretty +Defaults!RESTARTSERVER_INITD !requiretty +Defaults!STARTSERVER_INITD !requiretty +Defaults!STOPSERVER_INITD !requiretty + +#Allow the server to mount iso images +jellyfin ALL=(ALL) NOPASSWD: /bin/mount +jellyfin ALL=(ALL) NOPASSWD: /bin/umount + +Defaults:jellyfin !requiretty diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf new file mode 100644 index 0000000000..1b69dd74ef --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf @@ -0,0 +1,7 @@ +# Jellyfin systemd configuration options + +# Use this file to override the user or environment file location. + +[Service] +#User = jellyfin +#EnvironmentFile = /etc/default/jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/conf/logging.json b/deployment/old/debian-package-x64/pkg-src/conf/logging.json new file mode 100644 index 0000000000..f32b2089eb --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/conf/logging.json @@ -0,0 +1,30 @@ +{ + "Serilog": { + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "File", + "Args": { + "path": "%JELLYFIN_LOG_DIR%//jellyfin.log", + "fileSizeLimitBytes": 10485700, + "rollOnFileSizeLimit": true, + "retainedFileCountLimit": 10, + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" + } + } + ] + } + } + ] + } +} diff --git a/deployment/old/debian-package-x64/pkg-src/control b/deployment/old/debian-package-x64/pkg-src/control new file mode 100644 index 0000000000..13fd3ecabb --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/control @@ -0,0 +1,31 @@ +Source: jellyfin +Section: misc +Priority: optional +Maintainer: Jellyfin Team +Build-Depends: debhelper (>= 9), + dotnet-sdk-3.1, + libc6-dev, + libcurl4-openssl-dev, + libfontconfig1-dev, + libfreetype6-dev, + libssl-dev, + wget, + npm | nodejs +Standards-Version: 3.9.4 +Homepage: https://jellyfin.media/ +Vcs-Git: https://github.org/jellyfin/jellyfin.git +Vcs-Browser: https://github.org/jellyfin/jellyfin + +Package: jellyfin +Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server +Architecture: any +Depends: at, + libsqlite3-0, + jellyfin-ffmpeg, + libfontconfig1, + libfreetype6, + libssl1.1 +Description: Jellyfin is a home media server. + It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. diff --git a/deployment/old/debian-package-x64/pkg-src/copyright b/deployment/old/debian-package-x64/pkg-src/copyright new file mode 100644 index 0000000000..0d7a2a6007 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/copyright @@ -0,0 +1,29 @@ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: jellyfin +Source: https://github.com/jellyfin/jellyfin + +Files: * +Copyright: 2018 Jellyfin Team +License: GPL-2.0+ + +Files: debian/* +Copyright: 2018 Joshua Boniface +Copyright: 2014 Carlos Hernandez +License: GPL-2.0+ + +License: GPL-2.0+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/deployment/old/debian-package-x64/pkg-src/gbp.conf b/deployment/old/debian-package-x64/pkg-src/gbp.conf new file mode 100644 index 0000000000..60b3d28723 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/gbp.conf @@ -0,0 +1,6 @@ +[DEFAULT] +pristine-tar = False +cleaner = fakeroot debian/rules clean + +[import-orig] +filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/deployment/old/debian-package-x64/pkg-src/install b/deployment/old/debian-package-x64/pkg-src/install new file mode 100644 index 0000000000..994322d141 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/install @@ -0,0 +1,6 @@ +usr/lib/jellyfin usr/lib/ +debian/conf/jellyfin etc/default/ +debian/conf/logging.json etc/jellyfin/ +debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/ +debian/conf/jellyfin-sudoers etc/sudoers.d/ +debian/bin/restart.sh usr/lib/jellyfin/ diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.init b/deployment/old/debian-package-x64/pkg-src/jellyfin.init new file mode 100644 index 0000000000..7f5642bac1 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/jellyfin.init @@ -0,0 +1,61 @@ +### BEGIN INIT INFO +# Provides: Jellyfin Media Server +# Required-Start: $local_fs $network +# Required-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Jellyfin Media Server +# Description: Runs Jellyfin Server +### END INIT INFO + +set -e + +# Carry out specific functions when asked to by the system + +if test -f /etc/default/jellyfin; then + . /etc/default/jellyfin +fi + +. /lib/lsb/init-functions + +PIDFILE="/run/jellyfin.pid" + +case "$1" in + start) + log_daemon_msg "Starting Jellyfin Media Server" "jellyfin" || true + + if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + stop) + log_daemon_msg "Stopping Jellyfin Media Server" "jellyfin" || true + if start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + restart) + log_daemon_msg "Restarting Jellyfin Media Server" "jellyfin" || true + start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE --remove-pidfile + if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then + log_end_msg 0 || true + else + log_end_msg 1 || true + fi + ;; + + status) + status_of_proc -p $PIDFILE /usr/bin/jellyfin jellyfin && exit 0 || exit $? + ;; + + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.service b/deployment/old/debian-package-x64/pkg-src/jellyfin.service new file mode 100644 index 0000000000..1305e238b0 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/jellyfin.service @@ -0,0 +1,14 @@ +[Unit] +Description = Jellyfin Media Server +After = network.target + +[Service] +Type = simple +EnvironmentFile = /etc/default/jellyfin +User = jellyfin +ExecStart = /usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +Restart = on-failure +TimeoutSec = 15 + +[Install] +WantedBy = multi-user.target diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart b/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart new file mode 100644 index 0000000000..ef5bc9bcaf --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart @@ -0,0 +1,20 @@ +description "jellyfin daemon" + +start on (local-filesystems and net-device-up IFACE!=lo) +stop on runlevel [!2345] + +console log +respawn +respawn limit 10 5 + +kill timeout 20 + +script + set -x + echo "Starting $UPSTART_JOB" + + # Log file + logger -t "$0" "DEBUG: `set`" + . /etc/default/jellyfin + exec su -u $JELLYFIN_USER -c /usr/bin/jellyfin $JELLYFIN_ARGS +end script diff --git a/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in b/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in new file mode 100644 index 0000000000..cef83a3407 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] templates diff --git a/deployment/old/debian-package-x64/pkg-src/po/templates.pot b/deployment/old/debian-package-x64/pkg-src/po/templates.pot new file mode 100644 index 0000000000..2cdcae4173 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/po/templates.pot @@ -0,0 +1,57 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: jellyfin-server\n" +"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n" +"POT-Creation-Date: 2015-06-12 20:51-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "Jellyfin permission info:" +msgstr "" + +#. Type: note +#. Description +#: ../templates:1001 +msgid "" +"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the " +"user jellyfin has read and write access to any folders you wish to add to your " +"library. Otherwise please run jellyfin under a different user." +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "Username to run Jellyfin as:" +msgstr "" + +#. Type: string +#. Description +#: ../templates:2001 +msgid "The user that jellyfin will run as." +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin still running" +msgstr "" + +#. Type: note +#. Description +#: ../templates:3001 +msgid "Jellyfin is currently running. Please close it and try again." +msgstr "" diff --git a/deployment/old/debian-package-x64/pkg-src/postinst b/deployment/old/debian-package-x64/pkg-src/postinst new file mode 100644 index 0000000000..860222e051 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/postinst @@ -0,0 +1,92 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +case "$1" in + configure) + # create jellyfin group if it does not exist + if [[ -z "$(getent group jellyfin)" ]]; then + addgroup --quiet --system jellyfin > /dev/null 2>&1 + fi + # create jellyfin user if it does not exist + if [[ -z "$(getent passwd jellyfin)" ]]; then + adduser --system --ingroup jellyfin --shell /bin/false jellyfin --no-create-home --home ${PROGRAMDATA} \ + --gecos "Jellyfin default user" > /dev/null 2>&1 + fi + # ensure $PROGRAMDATA exists + if [[ ! -d $PROGRAMDATA ]]; then + mkdir $PROGRAMDATA + fi + # ensure $CONFIGDATA exists + if [[ ! -d $CONFIGDATA ]]; then + mkdir $CONFIGDATA + fi + # ensure $LOGDATA exists + if [[ ! -d $LOGDATA ]]; then + mkdir $LOGDATA + fi + # ensure $CACHEDATA exists + if [[ ! -d $CACHEDATA ]]; then + mkdir $CACHEDATA + fi + # Ensure permissions are correct on all config directories + chown -R jellyfin $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA + + chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true + + # Install jellyfin symlink into /usr/bin + ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin + + ;; + abort-upgrade|abort-remove|abort-deconfigure) + ;; + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER + +if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + # Manual init script handling + deb-systemd-helper unmask jellyfin.service >/dev/null || true + # was-enabled defaults to true, so new installations run enable. + if deb-systemd-helper --quiet was-enabled jellyfin.service; then + # Enables the unit on first installation, creates new + # symlinks on upgrades if the unit file has changed. + deb-systemd-helper enable jellyfin.service >/dev/null || true + else + # Update the statefile to add new symlinks (if any), which need to be + # cleaned up on purge. Also remove old symlinks. + deb-systemd-helper update-state jellyfin.service >/dev/null || true + fi +fi + +# End automatically added section +# Automatically added by dh_installinit +if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then + if [[ -d "/run/systemd/systemd" ]]; then + systemctl --system daemon-reload >/dev/null || true + deb-systemd-invoke start jellyfin >/dev/null || true + elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then + update-rc.d jellyfin defaults >/dev/null + invoke-rc.d jellyfin start || exit $? + fi +fi +exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/postrm b/deployment/old/debian-package-x64/pkg-src/postrm new file mode 100644 index 0000000000..1d00a984ec --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/postrm @@ -0,0 +1,81 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [[ -d /run/systemd/system ]] ; then + systemctl --system daemon-reload >/dev/null || true +fi + +case "$1" in + purge) + echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true + + if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.connf" ]]; then + update-rc.d jellyfin remove >/dev/null 2>&1 || true + fi + + if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + deb-systemd-helper purge jellyfin.service >/dev/null + deb-systemd-helper unmask jellyfin.service >/dev/null + fi + + # Remove user and group + userdel jellyfin > /dev/null 2>&1 || true + delgroup --quiet jellyfin > /dev/null 2>&1 || true + # Remove config dir + if [[ -d $CONFIGDATA ]]; then + rm -rf $CONFIGDATA + fi + # Remove log dir + if [[ -d $LOGDATA ]]; then + rm -rf $LOGDATA + fi + # Remove cache dir + if [[ -d $CACHEDATA ]]; then + rm -rf $CACHEDATA + fi + # Remove program data dir + if [[ -d $PROGRAMDATA ]]; then + rm -rf $PROGRAMDATA + fi + # Remove binary symlink + [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin + # Remove sudoers config + [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers + # Remove anything at the default locations; catches situations where the user moved the defaults + [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin + [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin + [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin + [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin + ;; + remove) + if [[ -x "/usr/bin/deb-systemd-helper" ]]; then + deb-systemd-helper mask jellyfin.service >/dev/null + fi + ;; + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/preinst b/deployment/old/debian-package-x64/pkg-src/preinst new file mode 100644 index 0000000000..2713fb9b80 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/preinst @@ -0,0 +1,78 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +# In case this system is running systemd, we make systemd reload the unit files +# to pick up changes. +if [[ -d /run/systemd/system ]] ; then + systemctl --system daemon-reload >/dev/null || true +fi + +case "$1" in + install|upgrade) + # try graceful termination; + if [[ -d /run/systemd/system ]]; then + deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true + elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then + invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true + fi + # try and figure out if jellyfin is running + PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) + [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) + # if its running, let's stop it + if [[ -n "$JELLYFIN_PID" ]]; then + echo "Stopping Jellyfin!" + # if jellyfin is still running, kill it + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + CPIDS=$(pgrep -P $JELLYFIN_PID) + sleep 2 && kill -KILL $CPIDS + kill -TERM $CPIDS > /dev/null 2>&1 + fi + sleep 1 + # if it's still running, show error + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + echo "Could not successfully stop JellyfinServer, please do so before uninstalling." + exit 1 + else + [[ -f $PIDFILE ]] && rm $PIDFILE + fi + fi + + # Clean up old Emby cruft that can break the user's system + [[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby + + # If we have existing config, log, or cache dirs in /var/lib/jellyfin, move them into the right place + if [[ -d $PROGRAMDATA/config ]]; then + mv $PROGRAMDATA/config $CONFIGDATA + fi + if [[ -d $PROGRAMDATA/logs ]]; then + mv $PROGRAMDATA/logs $LOGDATA + fi + if [[ -d $PROGRAMDATA/logs ]]; then + mv $PROGRAMDATA/cache $CACHEDATA + fi + + ;; + abort-upgrade) + ;; + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac +#DEBHELPER# + +exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/prerm b/deployment/old/debian-package-x64/pkg-src/prerm new file mode 100644 index 0000000000..e965cb7d71 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/prerm @@ -0,0 +1,61 @@ +#!/bin/bash +set -e + +NAME=jellyfin +DEFAULT_FILE=/etc/default/${NAME} + +# Source Jellyfin default configuration +if [[ -f $DEFAULT_FILE ]]; then + . $DEFAULT_FILE +fi + +# Data directories for program data (cache, db), configs, and logs +PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} +CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} +LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} +CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} + +case "$1" in + remove|upgrade|deconfigure) + echo "Stopping Jellyfin!" + # try graceful termination; + if [[ -d /run/systemd/system ]]; then + deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true + elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then + invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true + fi + # Ensure that it is shutdown + PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) + [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) + # if its running, let's stop it + if [[ -n "$JELLYFIN_PID" ]]; then + # if jellyfin is still running, kill it + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + CPIDS=$(pgrep -P $JELLYFIN_PID) + sleep 2 && kill -KILL $CPIDS + kill -TERM $CPIDS > /dev/null 2>&1 + fi + sleep 1 + # if it's still running, show error + if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then + echo "Could not successfully stop Jellyfin, please do so before uninstalling." + exit 1 + else + [[ -f $PIDFILE ]] && rm $PIDFILE + fi + fi + if [[ -f /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so ]]; then + rm /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so + fi + ;; + failed-upgrade) + ;; + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/rules b/deployment/old/debian-package-x64/pkg-src/rules new file mode 100755 index 0000000000..c2d57dfb22 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/rules @@ -0,0 +1,66 @@ +#! /usr/bin/make -f +CONFIG := Release +TERM := xterm +SHELL := /bin/bash +WEB_TARGET := $(CURDIR)/MediaBrowser.WebDashboard/jellyfin-web +WEB_VERSION := $(shell sed -n -e 's/^version: "\(.*\)"/\1/p' $(CURDIR)/build.yaml) + +HOST_ARCH := $(shell arch) +BUILD_ARCH := ${DEB_HOST_MULTIARCH} +ifeq ($(HOST_ARCH),x86_64) + # Building AMD64 + DOTNETRUNTIME := debian-x64 + ifeq ($(BUILD_ARCH),arm-linux-gnueabihf) + # Cross-building ARM on AMD64 + DOTNETRUNTIME := debian-arm + endif + ifeq ($(BUILD_ARCH),aarch64-linux-gnu) + # Cross-building ARM on AMD64 + DOTNETRUNTIME := debian-arm64 + endif +endif +ifeq ($(HOST_ARCH),armv7l) + # Building ARM + DOTNETRUNTIME := debian-arm +endif +ifeq ($(HOST_ARCH),arm64) + # Building ARM + DOTNETRUNTIME := debian-arm64 +endif + +export DH_VERBOSE=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 + +%: + dh $@ + +# disable "make check" +override_dh_auto_test: + +# disable stripping debugging symbols +override_dh_clistrip: + +override_dh_auto_build: + echo $(WEB_VERSION) + # Clone down and build Web frontend + mkdir -p $(WEB_TARGET) + wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/v$(WEB_VERSION).tar.gz || wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz + mkdir -p $(CURDIR)/web + tar -xzf web-src.tgz -C $(CURDIR)/web/ --strip 1 + cd $(CURDIR)/web/ && npm install yarn + cd $(CURDIR)/web/ && node_modules/yarn/bin/yarn install + mv $(CURDIR)/web/dist/* $(WEB_TARGET)/ + # Build the application + dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ + "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server + +override_dh_auto_clean: + dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true + rm -f '$(CURDIR)/web-src.tgz' + rm -rf '$(CURDIR)/usr' + rm -rf '$(CURDIR)/web' + rm -rf '$(WEB_TARGET)' + +# Force the service name to jellyfin even if we're building jellyfin-nightly +override_dh_installinit: + dh_installinit --name=jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides b/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides new file mode 100644 index 0000000000..aeb332f13a --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides @@ -0,0 +1,3 @@ +# This is an override for the following lintian errors: +jellyfin source: license-problem-md5sum-non-free-file Emby.Drawing/ImageMagick/fonts/webdings.ttf* +jellyfin source: source-is-missing diff --git a/deployment/old/debian-package-x64/pkg-src/source/format b/deployment/old/debian-package-x64/pkg-src/source/format new file mode 100644 index 0000000000..d3827e75a5 --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/source/format @@ -0,0 +1 @@ +1.0 diff --git a/deployment/old/debian-package-x64/pkg-src/source/options b/deployment/old/debian-package-x64/pkg-src/source/options new file mode 100644 index 0000000000..17b5373d5e --- /dev/null +++ b/deployment/old/debian-package-x64/pkg-src/source/options @@ -0,0 +1,11 @@ +tar-ignore='.git*' +tar-ignore='**/.git' +tar-ignore='**/.hg' +tar-ignore='**/.vs' +tar-ignore='**/.vscode' +tar-ignore='deployment' +tar-ignore='**/bin' +tar-ignore='**/obj' +tar-ignore='**/.nuget' +tar-ignore='*.deb' +tar-ignore='ThirdParty' diff --git a/deployment/old/fedora-package-x64/Dockerfile b/deployment/old/fedora-package-x64/Dockerfile new file mode 100644 index 0000000000..87120f3a05 --- /dev/null +++ b/deployment/old/fedora-package-x64/Dockerfile @@ -0,0 +1,33 @@ +FROM fedora:31 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist + +# Prepare Fedora environment +RUN dnf update -y + +# Install build dependencies +RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn + +# Install DotNET SDK +RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ + && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ + && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} + +# Create symlinks and directories +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/fedora-package-x64/clean.sh b/deployment/old/fedora-package-x64/clean.sh new file mode 100755 index 0000000000..700c8f1bb3 --- /dev/null +++ b/deployment/old/fedora-package-x64/clean.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" +VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +package_source_dir="${WORKDIR}/pkg-src" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-fedora-build" + +rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ + || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/fedora-package-x64/dependencies.txt b/deployment/old/fedora-package-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/fedora-package-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/fedora-package-x64/docker-build.sh b/deployment/old/fedora-package-x64/docker-build.sh new file mode 100755 index 0000000000..740e8d35ca --- /dev/null +++ b/deployment/old/fedora-package-x64/docker-build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Builds the RPM inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Build RPM +make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS +rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/rpm +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/fedora-package-x64/package.sh b/deployment/old/fedora-package-x64/package.sh new file mode 100755 index 0000000000..ae6962dd1f --- /dev/null +++ b/deployment/old/fedora-package-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-fedora-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the RPMs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the RPMs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/fedora-package-x64/pkg-src/.gitignore b/deployment/old/fedora-package-x64/pkg-src/.gitignore new file mode 100644 index 0000000000..6019b98c22 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/.gitignore @@ -0,0 +1,3 @@ +*.rpm +*.zip +*.tar.gz \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/README.md b/deployment/old/fedora-package-x64/pkg-src/README.md new file mode 100644 index 0000000000..7ed6f7efc6 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/README.md @@ -0,0 +1,43 @@ +# Jellyfin RPM + +## Build Fedora Package with docker + +Change into this directory `cd rpm-package` +Run the build script `./build-fedora-rpm.sh`. +Resulting RPM and src.rpm will be in `../../jellyfin-*.rpm` + +## ffmpeg + +The RPM package for Fedora/CentOS requires some additional repositories as ffmpeg is not in the main repositories. + +```shell +# ffmpeg from RPMfusion free +# Fedora +$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm +# CentOS 7 +$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm +``` + +## ISO mounting + +To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers` +``` +# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount +# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount +``` + +## Building with dotnet + +Jellyfin is build with `--self-contained` so no dotnet required for runtime. + +```shell +# dotnet required for building the RPM +# Fedora +$ sudo dnf copr enable @dotnet-sig/dotnet +# CentOS +$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm +``` + +## TODO + +- [ ] OpenSUSE \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml b/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml new file mode 100644 index 0000000000..538c5d65f8 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml @@ -0,0 +1,9 @@ + + + Jellyfin + The Free Software Media System. + + + + + diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.env b/deployment/old/fedora-package-x64/pkg-src/jellyfin.env new file mode 100644 index 0000000000..de48f13af5 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin.env @@ -0,0 +1,34 @@ +# Jellyfin default configuration options + +# Use this file to override the default configurations; add additional +# options with JELLYFIN_ADD_OPTS. + +# To override the user or this config file's location, use +# /etc/systemd/system/jellyfin.service.d/override.conf + +# +# This is a POSIX shell fragment +# + +# +# General options +# + +# Program directories +JELLYFIN_DATA_DIR="/var/lib/jellyfin" +JELLYFIN_CONFIG_DIR="/etc/jellyfin" +JELLYFIN_LOG_DIR="/var/log/jellyfin" +JELLYFIN_CACHE_DIR="/var/cache/jellyfin" + +# In-App service control +JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" + +# [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values +#JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg" + +# [OPTIONAL] run Jellyfin as a headless service +#JELLYFIN_SERVICE_OPT="--service" + +# [OPTIONAL] run Jellyfin without the web app +#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" + diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf b/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf new file mode 100644 index 0000000000..8652450bb4 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf @@ -0,0 +1,7 @@ +# Jellyfin systemd configuration options + +# Use this file to override the user or environment file location. + +[Service] +#User = jellyfin +#EnvironmentFile = /etc/sysconfig/jellyfin diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.service b/deployment/old/fedora-package-x64/pkg-src/jellyfin.service new file mode 100644 index 0000000000..f3dc594b1c --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin.service @@ -0,0 +1,15 @@ +[Unit] +After=network.target +Description=Jellyfin is a free software media system that puts you in control of managing and streaming your media. + +[Service] +EnvironmentFile=/etc/sysconfig/jellyfin +WorkingDirectory=/var/lib/jellyfin +ExecStart=/usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +TimeoutSec=15 +Restart=on-failure +User=jellyfin +Group=jellyfin + +[Install] +WantedBy=multi-user.target diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec new file mode 100644 index 0000000000..33c6f6f648 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec @@ -0,0 +1,181 @@ +%global debug_package %{nil} +# Set the dotnet runtime +%if 0%{?fedora} +%global dotnet_runtime fedora-x64 +%else +%global dotnet_runtime centos-x64 +%endif + +Name: jellyfin +Version: 10.5.0 +Release: 1%{?dist} +Summary: The Free Software Media Browser +License: GPLv2 +URL: https://jellyfin.media +# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` +Source0: https://github.com/%{name}/%{name}/archive/%{name}-%{version}.tar.gz +# Jellyfin Webinterface downloaded by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` +Source1: https://github.com/%{name}/%{name}-web/archive/%{name}-web-%{version}.tar.gz +Source11: jellyfin.service +Source12: jellyfin.env +Source13: jellyfin.sudoers +Source14: restart.sh +Source15: jellyfin.override.conf +Source16: jellyfin-firewalld.xml + +%{?systemd_requires} +BuildRequires: systemd +Requires(pre): shadow-utils +BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel, git +%if 0%{?fedora} +BuildRequires: nodejs-yarn, git +%else +# Requirements not packaged in main repos +# From https://rpm.nodesource.com/pub_10.x/el/7/x86_64/ +BuildRequires: nodejs >= 10 yarn +%endif +Requires: libcurl, fontconfig, freetype, openssl, glibc libicu +# Requirements not packaged in main repos +# COPR @dotnet-sig/dotnet or +# https://packages.microsoft.com/rhel/7/prod/ +BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 +# RPMfusion free +Requires: ffmpeg + +# Disable Automatic Dependency Processing +AutoReqProv: no + +%description +Jellyfin is a free software media system that puts you in control of managing and streaming your media. + + +%prep +%autosetup -n %{name}-%{version} -b 0 -b 1 +web_build_dir="$(mktemp -d)" +web_target="$PWD/MediaBrowser.WebDashboard/jellyfin-web" +pushd ../jellyfin-web-%{version} || pushd ../jellyfin-web-master +%if 0%{?fedora} +nodejs-yarn install +%else +yarn install +%endif +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd + +%build + +%install +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \ + "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server +%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE +%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/%{name}.service.d/override.conf +%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/%{name}/logging.json +%{__mkdir} -p %{buildroot}%{_bindir} +tee %{buildroot}%{_bindir}/jellyfin << EOF +#!/bin/sh +exec %{_libdir}/%{name}/%{name} \${@} +EOF +%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin +%{__mkdir} -p %{buildroot}%{_sysconfdir}/%{name} +%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin +%{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin + +%{__install} -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service +%{__install} -D -m 0644 %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/%{name} +%{__install} -D -m 0600 %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers +%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/%{name}/restart.sh +%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/%{name}.xml + +%files +%{_libdir}/%{name}/jellyfin-web/* +%attr(755,root,root) %{_bindir}/%{name} +%{_libdir}/%{name}/*.json +%{_libdir}/%{name}/*.dll +%{_libdir}/%{name}/*.so +%{_libdir}/%{name}/*.a +%{_libdir}/%{name}/createdump +# Needs 755 else only root can run it since binary build by dotnet is 722 +%attr(755,root,root) %{_libdir}/%{name}/jellyfin +%{_libdir}/%{name}/SOS_README.md +%{_unitdir}/%{name}.service +%{_libexecdir}/%{name}/restart.sh +%{_prefix}/lib/firewalld/services/%{name}.xml +%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name} +%config %{_sysconfdir}/sysconfig/%{name} +%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers +%config(noreplace) %{_sysconfdir}/systemd/system/%{name}.service.d/override.conf +%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/%{name}/logging.json +%attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin +%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin +%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin +%if 0%{?fedora} +%license LICENSE +%else +%{_datadir}/licenses/%{name}/LICENSE +%endif + +%pre +getent group jellyfin >/dev/null || groupadd -r jellyfin +getent passwd jellyfin >/dev/null || \ + useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \ + -c "Jellyfin default user" jellyfin +exit 0 + +%post +# Move existing configuration cache and logs to their new locations and symlink them. +if [ $1 -gt 1 ] ; then + service_state=$(systemctl is-active jellyfin.service) + if [ "${service_state}" = "active" ]; then + systemctl stop jellyfin.service + fi + if [ ! -L %{_sharedstatedir}/%{name}/config ]; then + mv %{_sharedstatedir}/%{name}/config/* %{_sysconfdir}/%{name}/ + rmdir %{_sharedstatedir}/%{name}/config + ln -sf %{_sysconfdir}/%{name} %{_sharedstatedir}/%{name}/config + fi + if [ ! -L %{_sharedstatedir}/%{name}/logs ]; then + mv %{_sharedstatedir}/%{name}/logs/* %{_var}/log/jellyfin + rmdir %{_sharedstatedir}/%{name}/logs + ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/%{name}/logs + fi + if [ ! -L %{_sharedstatedir}/%{name}/cache ]; then + mv %{_sharedstatedir}/%{name}/cache/* %{_var}/cache/jellyfin + rmdir %{_sharedstatedir}/%{name}/cache + ln -sf %{_var}/cache/jellyfin %{_sharedstatedir}/%{name}/cache + fi + if [ "${service_state}" = "active" ]; then + systemctl start jellyfin.service + fi +fi +%systemd_post jellyfin.service + +%preun +%systemd_preun jellyfin.service + +%postun +%systemd_postun_with_restart jellyfin.service + +%changelog +* Fri Oct 11 2019 Jellyfin Packaging Team +- New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 +* Sat Aug 31 2019 Jellyfin Packaging Team +- New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 +* Wed Jul 24 2019 Jellyfin Packaging Team +- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 +* Sat Jul 06 2019 Jellyfin Packaging Team +- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 +* Sun Jun 09 2019 Jellyfin Packaging Team +- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 +* Thu Jun 06 2019 Jellyfin Packaging Team +- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 +* Fri May 17 2019 Jellyfin Packaging Team +- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 +* Tue Apr 30 2019 Jellyfin Packaging Team +- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 +* Sat Apr 20 2019 Jellyfin Packaging Team +- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 +* Fri Apr 19 2019 Jellyfin Packaging Team +- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers b/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers new file mode 100644 index 0000000000..dd245af4b8 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers @@ -0,0 +1,19 @@ +# Allow jellyfin group to start, stop and restart itself +Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin +Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin +Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin + + +jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD +jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD + +Defaults!RESTARTSERVER_SYSTEMD !requiretty +Defaults!STARTSERVER_SYSTEMD !requiretty +Defaults!STOPSERVER_SYSTEMD !requiretty + +# Allow the server to mount iso images +jellyfin ALL=(ALL) NOPASSWD: /bin/mount +jellyfin ALL=(ALL) NOPASSWD: /bin/umount + +Defaults:jellyfin !requiretty diff --git a/deployment/old/fedora-package-x64/pkg-src/restart.sh b/deployment/old/fedora-package-x64/pkg-src/restart.sh new file mode 100755 index 0000000000..9b64b6d728 --- /dev/null +++ b/deployment/old/fedora-package-x64/pkg-src/restart.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# restart.sh - Jellyfin server restart script +# Part of the Jellyfin project (https://github.com/jellyfin) +# +# This script restarts the Jellyfin daemon on Linux when using +# the Restart button on the admin dashboard. It supports the +# systemctl, service, and traditional /etc/init.d (sysv) restart +# methods, chosen automatically by which one is found first (in +# that order). +# +# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. + +get_service_command() { + for command in systemctl service; do + if which $command &>/dev/null; then + echo $command && return + fi + done + echo "sysv" +} + +cmd="$( get_service_command )" +echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." +case $cmd in + 'systemctl') + echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now + ;; + 'service') + echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now + ;; + 'sysv') + echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now + ;; +esac +exit 0 diff --git a/deployment/old/linux-x64/Dockerfile b/deployment/old/linux-x64/Dockerfile new file mode 100644 index 0000000000..c47057546d --- /dev/null +++ b/deployment/old/linux-x64/Dockerfile @@ -0,0 +1,37 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/linux-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install yarn package manager +RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && apt update \ + && apt install -y yarn + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/linux-x64/clean.sh b/deployment/old/linux-x64/clean.sh new file mode 100755 index 0000000000..c07501a7bb --- /dev/null +++ b/deployment/old/linux-x64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-linux-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/linux-x64/dependencies.txt b/deployment/old/linux-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/linux-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/linux-x64/docker-build.sh b/deployment/old/linux-x64/docker-build.sh new file mode 100755 index 0000000000..e33328a36a --- /dev/null +++ b/deployment/old/linux-x64/docker-build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Builds the TAR archive inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Clone down and build Web frontend +web_build_dir="$( mktemp -d )" +web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" +git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ +pushd ${web_build_dir} +if [[ -n ${web_branch} ]]; then + checkout -b origin/${web_branch} +fi +yarn install +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd +rm -rf ${web_build_dir} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} +rm -rf /dist/jellyfin_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/linux-x64/package.sh b/deployment/old/linux-x64/package.sh new file mode 100755 index 0000000000..dfe8a9aa4a --- /dev/null +++ b/deployment/old/linux-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-linux-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/macos/Dockerfile b/deployment/old/macos/Dockerfile new file mode 100644 index 0000000000..b522df8848 --- /dev/null +++ b/deployment/old/macos/Dockerfile @@ -0,0 +1,37 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/macos +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install yarn package manager +RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && apt update \ + && apt install -y yarn + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/macos/clean.sh b/deployment/old/macos/clean.sh new file mode 100755 index 0000000000..c07501a7bb --- /dev/null +++ b/deployment/old/macos/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-linux-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/macos/dependencies.txt b/deployment/old/macos/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/macos/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/macos/docker-build.sh b/deployment/old/macos/docker-build.sh new file mode 100755 index 0000000000..f9417388d7 --- /dev/null +++ b/deployment/old/macos/docker-build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Builds the TAR archive inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Clone down and build Web frontend +web_build_dir="$( mktemp -d )" +web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" +git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ +pushd ${web_build_dir} +if [[ -n ${web_branch} ]]; then + checkout -b origin/${web_branch} +fi +yarn install +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd +rm -rf ${web_build_dir} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} +rm -rf /dist/jellyfin_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/macos/package.sh b/deployment/old/macos/package.sh new file mode 100755 index 0000000000..464c0d382f --- /dev/null +++ b/deployment/old/macos/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-macos-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/portable/Dockerfile b/deployment/old/portable/Dockerfile new file mode 100644 index 0000000000..965eb82b86 --- /dev/null +++ b/deployment/old/portable/Dockerfile @@ -0,0 +1,37 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/portable +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install yarn package manager +RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && apt update \ + && apt install -y yarn + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/portable/clean.sh b/deployment/old/portable/clean.sh new file mode 100755 index 0000000000..c07501a7bb --- /dev/null +++ b/deployment/old/portable/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-linux-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/portable/dependencies.txt b/deployment/old/portable/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/portable/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/portable/docker-build.sh b/deployment/old/portable/docker-build.sh new file mode 100755 index 0000000000..094190bbf6 --- /dev/null +++ b/deployment/old/portable/docker-build.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Builds the TAR archive inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Clone down and build Web frontend +web_build_dir="$( mktemp -d )" +web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" +git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ +pushd ${web_build_dir} +if [[ -n ${web_branch} ]]; then + checkout -b origin/${web_branch} +fi +yarn install +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd +rm -rf ${web_build_dir} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build archives +dotnet publish Jellyfin.Server --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" +tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} +rm -rf /dist/jellyfin_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/portable/package.sh b/deployment/old/portable/package.sh new file mode 100755 index 0000000000..0ceb54dda1 --- /dev/null +++ b/deployment/old/portable/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-portable-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 new file mode 100644 index 0000000000..b11994a18a --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 @@ -0,0 +1,59 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install npm package manager +RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ + && apt update \ + && apt install -y nodejs + +# Prepare the cross-toolchain +RUN rm /etc/apt/sources.list \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && dpkg --add-architecture arm64 \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 6 \ + && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64 + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 new file mode 100644 index 0000000000..8f004b2f1a --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 @@ -0,0 +1,40 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=arm64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install npm package manager +RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ + && apt update \ + && apt install -y nodejs + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/clean.sh b/deployment/old/ubuntu-package-arm64/clean.sh new file mode 100755 index 0000000000..82d427f9e5 --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/ubuntu-package-arm64/dependencies.txt b/deployment/old/ubuntu-package-arm64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/ubuntu-package-arm64/docker-build.sh b/deployment/old/ubuntu-package-arm64/docker-build.sh new file mode 100755 index 0000000000..67ab6bd74b --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/docker-build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -aarm64 + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-arm64/package.sh b/deployment/old/ubuntu-package-arm64/package.sh new file mode 100755 index 0000000000..d1140a7274 --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/package.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +ARCH="$( arch )" +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu_arm64-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Determine which Dockerfile to use +case $ARCH in + 'x86_64') + DOCKERFILE="Dockerfile.amd64" + ;; + 'armv7l') + DOCKERFILE="Dockerfile.arm64" + ;; +esac + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/pkg-src b/deployment/old/ubuntu-package-arm64/pkg-src new file mode 120000 index 0000000000..4c695fea17 --- /dev/null +++ b/deployment/old/ubuntu-package-arm64/pkg-src @@ -0,0 +1 @@ +../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 new file mode 100644 index 0000000000..e475b14389 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 @@ -0,0 +1,59 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install npm package manager +RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ + && apt update \ + && apt install -y nodejs + +# Prepare the cross-toolchain +RUN rm /etc/apt/sources.list \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && dpkg --add-architecture armhf \ + && apt-get update \ + && apt-get install -y cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 6 \ + && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.armhf b/deployment/old/ubuntu-package-armhf/Dockerfile.armhf new file mode 100644 index 0000000000..0e71fa6938 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/Dockerfile.armhf @@ -0,0 +1,40 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=armhf + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install npm package manager +RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ + && apt update \ + && apt install -y nodejs + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +# Link to Debian source dir; mkdir needed or it fails, can't force dest +RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/clean.sh b/deployment/old/ubuntu-package-armhf/clean.sh new file mode 100755 index 0000000000..82d427f9e5 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/ubuntu-package-armhf/dependencies.txt b/deployment/old/ubuntu-package-armhf/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/ubuntu-package-armhf/docker-build.sh b/deployment/old/ubuntu-package-armhf/docker-build.sh new file mode 100755 index 0000000000..1bd7fb2911 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/docker-build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} +dpkg-buildpackage -us -uc -aarmhf + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-armhf/package.sh b/deployment/old/ubuntu-package-armhf/package.sh new file mode 100755 index 0000000000..2ceb3e8165 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/package.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +ARCH="$( arch )" +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu_armhf-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Determine which Dockerfile to use +case $ARCH in + 'x86_64') + DOCKERFILE="Dockerfile.amd64" + ;; + 'armv7l') + DOCKERFILE="Dockerfile.armhf" + ;; +esac + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-armhf/pkg-src b/deployment/old/ubuntu-package-armhf/pkg-src new file mode 120000 index 0000000000..4c695fea17 --- /dev/null +++ b/deployment/old/ubuntu-package-armhf/pkg-src @@ -0,0 +1 @@ +../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-x64/Dockerfile b/deployment/old/ubuntu-package-x64/Dockerfile new file mode 100644 index 0000000000..e2dda6392c --- /dev/null +++ b/deployment/old/ubuntu-package-x64/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:bionic +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Ubuntu build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 \ + && ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ + && mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install npm package manager +RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ + && apt update \ + && apt install -y nodejs + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-x64/clean.sh b/deployment/old/ubuntu-package-x64/clean.sh new file mode 100755 index 0000000000..82d427f9e5 --- /dev/null +++ b/deployment/old/ubuntu-package-x64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/ubuntu-package-x64/dependencies.txt b/deployment/old/ubuntu-package-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/ubuntu-package-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/ubuntu-package-x64/docker-build.sh b/deployment/old/ubuntu-package-x64/docker-build.sh new file mode 100755 index 0000000000..962a522ebc --- /dev/null +++ b/deployment/old/ubuntu-package-x64/docker-build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Builds the DEB inside the Docker container + +set -o errexit +set -o xtrace + +# Move to source directory +pushd ${SOURCE_DIR} + +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control + +# Build DEB +dpkg-buildpackage -us -uc + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/deb +mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-x64/package.sh b/deployment/old/ubuntu-package-x64/package.sh new file mode 100755 index 0000000000..08c003778b --- /dev/null +++ b/deployment/old/ubuntu-package-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-ubuntu-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-x64/pkg-src b/deployment/old/ubuntu-package-x64/pkg-src new file mode 120000 index 0000000000..0bb6d55249 --- /dev/null +++ b/deployment/old/ubuntu-package-x64/pkg-src @@ -0,0 +1 @@ +../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/unraid/docker-templates/README.md b/deployment/old/unraid/docker-templates/README.md new file mode 100644 index 0000000000..2c268e8b3e --- /dev/null +++ b/deployment/old/unraid/docker-templates/README.md @@ -0,0 +1,15 @@ +# docker-templates + +### Installation: + +Open unRaid GUI (at least unRaid 6.5) + +Click on the Docker tab + +Add the following line under "Template Repositories" + +https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates + +Click save than click on Add Container and select jellyfin. + +Adjust to your paths to your liking and off you go! diff --git a/deployment/old/unraid/docker-templates/jellyfin.xml b/deployment/old/unraid/docker-templates/jellyfin.xml new file mode 100644 index 0000000000..57b4cc5ae1 --- /dev/null +++ b/deployment/old/unraid/docker-templates/jellyfin.xml @@ -0,0 +1,57 @@ + + + https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml + False + MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos + Jellyfin + + Jellyfin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br] + You can add as many mount points as needed for recordings, movies ,etc. [br][br] + [b][span style='color: #E80000;']Directions:[/span][/b][br] + [b]/config[/b] : This is where Jellyfin will store it's databases and configuration.[br][br] + [b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br] + [b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point[br][br] + [b]Cache[/b] : This is where Jellyfin will store and manage cached files like images to serve to clients. This is not where all images are stored.[br][br] + [b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it. + + + Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free! + + https://www.reddit.com/r/jellyfin/ + https://hub.docker.com/r/jellyfin/jellyfin/ + https://github.com/jellyfin/jellyfin/> + jellyfin/jellyfin + https://jellyfin.media/ + true + false + + host + + + 8096 + 8096 + tcp + + + + + + /mnt/user/appdata/Jellyfin + /config + rw + + + /mnt/user + /media + rw + + + /mnt/user/appdata/Jellyfin/cache/ + /cache + rw + + + http://[IP]:[PORT:8096]/ + https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/jellyfin-icon.png + + diff --git a/deployment/old/win-x64/Dockerfile b/deployment/old/win-x64/Dockerfile new file mode 100644 index 0000000000..8a33749541 --- /dev/null +++ b/deployment/old/win-x64/Dockerfile @@ -0,0 +1,37 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/win-x64 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install yarn package manager +RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && apt update \ + && apt install -y yarn + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x64/clean.sh b/deployment/old/win-x64/clean.sh new file mode 100755 index 0000000000..6c183f3371 --- /dev/null +++ b/deployment/old/win-x64/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-windows-x64-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/win-x64/dependencies.txt b/deployment/old/win-x64/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/win-x64/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/win-x64/docker-build.sh b/deployment/old/win-x64/docker-build.sh new file mode 100755 index 0000000000..79e5fb0bcd --- /dev/null +++ b/deployment/old/win-x64/docker-build.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Builds the ZIP archive inside the Docker container + +set -o errexit +set -o xtrace + +# Version variables +NSSM_VERSION="nssm-2.24-101-g897c7ad" +NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" +FFMPEG_VERSION="ffmpeg-4.2.1-win64-static" +FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip" + +# Move to source directory +pushd ${SOURCE_DIR} + +# Clone down and build Web frontend +web_build_dir="$( mktemp -d )" +web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" +git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ +pushd ${web_build_dir} +if [[ -n ${web_branch} ]]; then + checkout -b origin/${web_branch} +fi +yarn install +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd +rm -rf ${web_build_dir} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build binary +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" + +# Prepare addins +addin_build_dir="$( mktemp -d )" +wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip +wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip +unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe +unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe +rm -rf ${addin_build_dir} + +# Prepare scripts +cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 +cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat + +# Create zip package +pushd /dist +zip -r /jellyfin_${version}.portable.zip jellyfin_${version} +popd +rm -rf /dist/jellyfin_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x64/package.sh b/deployment/old/win-x64/package.sh new file mode 100755 index 0000000000..a8ab190fa5 --- /dev/null +++ b/deployment/old/win-x64/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-windows-x64-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/win-x86/Dockerfile b/deployment/old/win-x86/Dockerfile new file mode 100644 index 0000000000..f8dc5be83d --- /dev/null +++ b/deployment/old/win-x86/Dockerfile @@ -0,0 +1,37 @@ +FROM debian:10 +# Docker build arguments +ARG SOURCE_DIR=/jellyfin +ARG PLATFORM_DIR=/jellyfin/deployment/win-x86 +ARG ARTIFACT_DIR=/dist +ARG SDK_VERSION=3.1 +# Docker run environment +ENV SOURCE_DIR=/jellyfin +ENV ARTIFACT_DIR=/dist +ENV DEB_BUILD_OPTIONS=noddebs +ENV ARCH=amd64 + +# Prepare Debian build environment +RUN apt-get update \ + && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip + +# Install dotnet repository +# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + +# Install yarn package manager +RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ + && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && apt update \ + && apt install -y yarn + +# Link to docker-build script +RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh + +VOLUME ${ARTIFACT_DIR}/ + +COPY . ${SOURCE_DIR}/ + +ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x86/clean.sh b/deployment/old/win-x86/clean.sh new file mode 100755 index 0000000000..8b78c5e4b6 --- /dev/null +++ b/deployment/old/win-x86/clean.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +keep_artifacts="${1}" + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-windows-x86-build" + +rm -rf "${package_temporary_dir}" &>/dev/null \ + || sudo rm -rf "${package_temporary_dir}" &>/dev/null + +rm -rf "${output_dir}" &>/dev/null \ + || sudo rm -rf "${output_dir}" &>/dev/null + +if [[ ${keep_artifacts} == 'n' ]]; then + docker_sudo="" + if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo=sudo + fi + ${docker_sudo} docker image rm ${image_name} --force +fi diff --git a/deployment/old/win-x86/dependencies.txt b/deployment/old/win-x86/dependencies.txt new file mode 100644 index 0000000000..bdb9670965 --- /dev/null +++ b/deployment/old/win-x86/dependencies.txt @@ -0,0 +1 @@ +docker diff --git a/deployment/old/win-x86/docker-build.sh b/deployment/old/win-x86/docker-build.sh new file mode 100755 index 0000000000..977dcf78fa --- /dev/null +++ b/deployment/old/win-x86/docker-build.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Builds the ZIP archive inside the Docker container + +set -o errexit +set -o xtrace + +# Version variables +NSSM_VERSION="nssm-2.24-101-g897c7ad" +NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" +FFMPEG_VERSION="ffmpeg-4.2.1-win32-static" +FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip" + +# Move to source directory +pushd ${SOURCE_DIR} + +# Clone down and build Web frontend +web_build_dir="$( mktemp -d )" +web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" +git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ +pushd ${web_build_dir} +if [[ -n ${web_branch} ]]; then + checkout -b origin/${web_branch} +fi +yarn install +mkdir -p ${web_target} +mv dist/* ${web_target}/ +popd +rm -rf ${web_build_dir} + +# Get version +version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" + +# Build binary +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" + +# Prepare addins +addin_build_dir="$( mktemp -d )" +wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip +wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip +unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe +unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe +cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe +rm -rf ${addin_build_dir} + +# Prepare scripts +cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 +cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat + +# Create zip package +pushd /dist +zip -r /jellyfin_${version}.portable.zip jellyfin_${version} +popd +rm -rf /dist/jellyfin_${version} + +# Move the artifacts out +mkdir -p ${ARTIFACT_DIR}/ +mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ +chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x86/package.sh b/deployment/old/win-x86/package.sh new file mode 100755 index 0000000000..65e7e2928c --- /dev/null +++ b/deployment/old/win-x86/package.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +args="${@}" +declare -a docker_envvars +for arg in ${args}; do + docker_envvars+=("-e ${arg}") +done + +WORKDIR="$( pwd )" + +package_temporary_dir="${WORKDIR}/pkg-dist-tmp" +output_dir="${WORKDIR}/pkg-dist" +current_user="$( whoami )" +image_name="jellyfin-windows-x86-build" + +# Determine if sudo should be used for Docker +if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ + && [[ ! ${EUID:-1000} -eq 0 ]] \ + && [[ ! ${USER} == "root" ]] \ + && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then + docker_sudo="sudo" +else + docker_sudo="" +fi + +# Prepare temporary package dir +mkdir -p "${package_temporary_dir}" +# Set up the build environment Docker image +${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile +# Build the DEBs and copy out to ${package_temporary_dir} +${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} +# Move the DEBs to the output directory +mkdir -p "${output_dir}" +mv "${package_temporary_dir}"/* "${output_dir}" From 42813ef06973126151e2c91ecb7aa6836e0d472c Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:50:32 -0400 Subject: [PATCH 128/411] Preserve Unraid configuration --- deployment/unraid/docker-templates/README.md | 15 +++++ .../unraid/docker-templates/jellyfin.xml | 57 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 deployment/unraid/docker-templates/README.md create mode 100644 deployment/unraid/docker-templates/jellyfin.xml diff --git a/deployment/unraid/docker-templates/README.md b/deployment/unraid/docker-templates/README.md new file mode 100644 index 0000000000..2c268e8b3e --- /dev/null +++ b/deployment/unraid/docker-templates/README.md @@ -0,0 +1,15 @@ +# docker-templates + +### Installation: + +Open unRaid GUI (at least unRaid 6.5) + +Click on the Docker tab + +Add the following line under "Template Repositories" + +https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates + +Click save than click on Add Container and select jellyfin. + +Adjust to your paths to your liking and off you go! diff --git a/deployment/unraid/docker-templates/jellyfin.xml b/deployment/unraid/docker-templates/jellyfin.xml new file mode 100644 index 0000000000..57b4cc5ae1 --- /dev/null +++ b/deployment/unraid/docker-templates/jellyfin.xml @@ -0,0 +1,57 @@ + + + https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml + False + MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos + Jellyfin + + Jellyfin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br] + You can add as many mount points as needed for recordings, movies ,etc. [br][br] + [b][span style='color: #E80000;']Directions:[/span][/b][br] + [b]/config[/b] : This is where Jellyfin will store it's databases and configuration.[br][br] + [b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br] + [b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point[br][br] + [b]Cache[/b] : This is where Jellyfin will store and manage cached files like images to serve to clients. This is not where all images are stored.[br][br] + [b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it. + + + Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free! + + https://www.reddit.com/r/jellyfin/ + https://hub.docker.com/r/jellyfin/jellyfin/ + https://github.com/jellyfin/jellyfin/> + jellyfin/jellyfin + https://jellyfin.media/ + true + false + + host + + + 8096 + 8096 + tcp + + + + + + /mnt/user/appdata/Jellyfin + /config + rw + + + /mnt/user + /media + rw + + + /mnt/user/appdata/Jellyfin/cache/ + /cache + rw + + + http://[IP]:[PORT:8096]/ + https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/jellyfin-icon.png + + From fbad4f00b4a95b889708d430ad445a58dad2f964 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:50:46 -0400 Subject: [PATCH 129/411] Remove old build infra (again) --- deployment/old/README.md | 62 ------ deployment/old/centos-package-x64/Dockerfile | 39 ---- deployment/old/centos-package-x64/clean.sh | 32 ---- .../old/centos-package-x64/dependencies.txt | 1 - .../old/centos-package-x64/docker-build.sh | 18 -- deployment/old/centos-package-x64/package.sh | 34 ---- deployment/old/centos-package-x64/pkg-src | 1 - .../old/debian-package-arm64/Dockerfile.amd64 | 43 ----- .../old/debian-package-arm64/Dockerfile.arm64 | 34 ---- deployment/old/debian-package-arm64/clean.sh | 27 --- .../old/debian-package-arm64/dependencies.txt | 1 - .../old/debian-package-arm64/docker-build.sh | 21 -- .../old/debian-package-arm64/package.sh | 45 ----- deployment/old/debian-package-arm64/pkg-src | 1 - .../old/debian-package-armhf/Dockerfile.amd64 | 42 ---- .../old/debian-package-armhf/Dockerfile.armhf | 34 ---- deployment/old/debian-package-armhf/clean.sh | 27 --- .../old/debian-package-armhf/dependencies.txt | 1 - .../old/debian-package-armhf/docker-build.sh | 21 -- .../old/debian-package-armhf/package.sh | 45 ----- deployment/old/debian-package-armhf/pkg-src | 1 - deployment/old/debian-package-x64/Dockerfile | 34 ---- deployment/old/debian-package-x64/clean.sh | 27 --- .../old/debian-package-x64/dependencies.txt | 1 - .../old/debian-package-x64/docker-build.sh | 20 -- deployment/old/debian-package-x64/package.sh | 34 ---- .../old/debian-package-x64/pkg-src/changelog | 59 ------ .../old/debian-package-x64/pkg-src/compat | 1 - .../debian-package-x64/pkg-src/conf/jellyfin | 40 ---- .../pkg-src/conf/jellyfin-sudoers | 37 ---- .../pkg-src/conf/jellyfin.service.conf | 7 - .../pkg-src/conf/logging.json | 30 --- .../old/debian-package-x64/pkg-src/control | 31 --- .../old/debian-package-x64/pkg-src/copyright | 29 --- .../old/debian-package-x64/pkg-src/gbp.conf | 6 - .../old/debian-package-x64/pkg-src/install | 6 - .../debian-package-x64/pkg-src/jellyfin.init | 61 ------ .../pkg-src/jellyfin.service | 14 -- .../pkg-src/jellyfin.upstart | 20 -- .../debian-package-x64/pkg-src/po/POTFILES.in | 1 - .../pkg-src/po/templates.pot | 57 ------ .../old/debian-package-x64/pkg-src/postinst | 92 --------- .../old/debian-package-x64/pkg-src/postrm | 81 -------- .../old/debian-package-x64/pkg-src/preinst | 78 -------- .../old/debian-package-x64/pkg-src/prerm | 61 ------ .../old/debian-package-x64/pkg-src/rules | 66 ------- .../pkg-src/source.lintian-overrides | 3 - .../debian-package-x64/pkg-src/source/format | 1 - .../debian-package-x64/pkg-src/source/options | 11 -- deployment/old/fedora-package-x64/Dockerfile | 33 ---- deployment/old/fedora-package-x64/clean.sh | 32 ---- .../old/fedora-package-x64/dependencies.txt | 1 - .../old/fedora-package-x64/docker-build.sh | 18 -- deployment/old/fedora-package-x64/package.sh | 34 ---- .../old/fedora-package-x64/pkg-src/.gitignore | 3 - .../old/fedora-package-x64/pkg-src/README.md | 43 ----- .../pkg-src/jellyfin-firewalld.xml | 9 - .../fedora-package-x64/pkg-src/jellyfin.env | 34 ---- .../pkg-src/jellyfin.override.conf | 7 - .../pkg-src/jellyfin.service | 15 -- .../fedora-package-x64/pkg-src/jellyfin.spec | 181 ------------------ .../pkg-src/jellyfin.sudoers | 19 -- .../old/fedora-package-x64/pkg-src/restart.sh | 36 ---- deployment/old/linux-x64/Dockerfile | 37 ---- deployment/old/linux-x64/clean.sh | 27 --- deployment/old/linux-x64/dependencies.txt | 1 - deployment/old/linux-x64/docker-build.sh | 36 ---- deployment/old/linux-x64/package.sh | 34 ---- deployment/old/macos/Dockerfile | 37 ---- deployment/old/macos/clean.sh | 27 --- deployment/old/macos/dependencies.txt | 1 - deployment/old/macos/docker-build.sh | 36 ---- deployment/old/macos/package.sh | 34 ---- deployment/old/portable/Dockerfile | 37 ---- deployment/old/portable/clean.sh | 27 --- deployment/old/portable/dependencies.txt | 1 - deployment/old/portable/docker-build.sh | 36 ---- deployment/old/portable/package.sh | 34 ---- .../old/ubuntu-package-arm64/Dockerfile.amd64 | 59 ------ .../old/ubuntu-package-arm64/Dockerfile.arm64 | 40 ---- deployment/old/ubuntu-package-arm64/clean.sh | 27 --- .../old/ubuntu-package-arm64/dependencies.txt | 1 - .../old/ubuntu-package-arm64/docker-build.sh | 21 -- .../old/ubuntu-package-arm64/package.sh | 45 ----- deployment/old/ubuntu-package-arm64/pkg-src | 1 - .../old/ubuntu-package-armhf/Dockerfile.amd64 | 59 ------ .../old/ubuntu-package-armhf/Dockerfile.armhf | 40 ---- deployment/old/ubuntu-package-armhf/clean.sh | 27 --- .../old/ubuntu-package-armhf/dependencies.txt | 1 - .../old/ubuntu-package-armhf/docker-build.sh | 21 -- .../old/ubuntu-package-armhf/package.sh | 45 ----- deployment/old/ubuntu-package-armhf/pkg-src | 1 - deployment/old/ubuntu-package-x64/Dockerfile | 36 ---- deployment/old/ubuntu-package-x64/clean.sh | 27 --- .../old/ubuntu-package-x64/dependencies.txt | 1 - .../old/ubuntu-package-x64/docker-build.sh | 20 -- deployment/old/ubuntu-package-x64/package.sh | 34 ---- deployment/old/ubuntu-package-x64/pkg-src | 1 - .../old/unraid/docker-templates/README.md | 15 -- .../old/unraid/docker-templates/jellyfin.xml | 57 ------ deployment/old/win-x64/Dockerfile | 37 ---- deployment/old/win-x64/clean.sh | 27 --- deployment/old/win-x64/dependencies.txt | 1 - deployment/old/win-x64/docker-build.sh | 61 ------ deployment/old/win-x64/package.sh | 34 ---- deployment/old/win-x86/Dockerfile | 37 ---- deployment/old/win-x86/clean.sh | 27 --- deployment/old/win-x86/dependencies.txt | 1 - deployment/old/win-x86/docker-build.sh | 61 ------ deployment/old/win-x86/package.sh | 34 ---- 110 files changed, 3207 deletions(-) delete mode 100644 deployment/old/README.md delete mode 100644 deployment/old/centos-package-x64/Dockerfile delete mode 100755 deployment/old/centos-package-x64/clean.sh delete mode 100644 deployment/old/centos-package-x64/dependencies.txt delete mode 100755 deployment/old/centos-package-x64/docker-build.sh delete mode 100755 deployment/old/centos-package-x64/package.sh delete mode 120000 deployment/old/centos-package-x64/pkg-src delete mode 100644 deployment/old/debian-package-arm64/Dockerfile.amd64 delete mode 100644 deployment/old/debian-package-arm64/Dockerfile.arm64 delete mode 100755 deployment/old/debian-package-arm64/clean.sh delete mode 100644 deployment/old/debian-package-arm64/dependencies.txt delete mode 100755 deployment/old/debian-package-arm64/docker-build.sh delete mode 100755 deployment/old/debian-package-arm64/package.sh delete mode 120000 deployment/old/debian-package-arm64/pkg-src delete mode 100644 deployment/old/debian-package-armhf/Dockerfile.amd64 delete mode 100644 deployment/old/debian-package-armhf/Dockerfile.armhf delete mode 100755 deployment/old/debian-package-armhf/clean.sh delete mode 100644 deployment/old/debian-package-armhf/dependencies.txt delete mode 100755 deployment/old/debian-package-armhf/docker-build.sh delete mode 100755 deployment/old/debian-package-armhf/package.sh delete mode 120000 deployment/old/debian-package-armhf/pkg-src delete mode 100644 deployment/old/debian-package-x64/Dockerfile delete mode 100755 deployment/old/debian-package-x64/clean.sh delete mode 100644 deployment/old/debian-package-x64/dependencies.txt delete mode 100755 deployment/old/debian-package-x64/docker-build.sh delete mode 100755 deployment/old/debian-package-x64/package.sh delete mode 100644 deployment/old/debian-package-x64/pkg-src/changelog delete mode 100644 deployment/old/debian-package-x64/pkg-src/compat delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf delete mode 100644 deployment/old/debian-package-x64/pkg-src/conf/logging.json delete mode 100644 deployment/old/debian-package-x64/pkg-src/control delete mode 100644 deployment/old/debian-package-x64/pkg-src/copyright delete mode 100644 deployment/old/debian-package-x64/pkg-src/gbp.conf delete mode 100644 deployment/old/debian-package-x64/pkg-src/install delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.init delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.service delete mode 100644 deployment/old/debian-package-x64/pkg-src/jellyfin.upstart delete mode 100644 deployment/old/debian-package-x64/pkg-src/po/POTFILES.in delete mode 100644 deployment/old/debian-package-x64/pkg-src/po/templates.pot delete mode 100644 deployment/old/debian-package-x64/pkg-src/postinst delete mode 100644 deployment/old/debian-package-x64/pkg-src/postrm delete mode 100644 deployment/old/debian-package-x64/pkg-src/preinst delete mode 100644 deployment/old/debian-package-x64/pkg-src/prerm delete mode 100755 deployment/old/debian-package-x64/pkg-src/rules delete mode 100644 deployment/old/debian-package-x64/pkg-src/source.lintian-overrides delete mode 100644 deployment/old/debian-package-x64/pkg-src/source/format delete mode 100644 deployment/old/debian-package-x64/pkg-src/source/options delete mode 100644 deployment/old/fedora-package-x64/Dockerfile delete mode 100755 deployment/old/fedora-package-x64/clean.sh delete mode 100644 deployment/old/fedora-package-x64/dependencies.txt delete mode 100755 deployment/old/fedora-package-x64/docker-build.sh delete mode 100755 deployment/old/fedora-package-x64/package.sh delete mode 100644 deployment/old/fedora-package-x64/pkg-src/.gitignore delete mode 100644 deployment/old/fedora-package-x64/pkg-src/README.md delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.env delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.service delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.spec delete mode 100644 deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers delete mode 100755 deployment/old/fedora-package-x64/pkg-src/restart.sh delete mode 100644 deployment/old/linux-x64/Dockerfile delete mode 100755 deployment/old/linux-x64/clean.sh delete mode 100644 deployment/old/linux-x64/dependencies.txt delete mode 100755 deployment/old/linux-x64/docker-build.sh delete mode 100755 deployment/old/linux-x64/package.sh delete mode 100644 deployment/old/macos/Dockerfile delete mode 100755 deployment/old/macos/clean.sh delete mode 100644 deployment/old/macos/dependencies.txt delete mode 100755 deployment/old/macos/docker-build.sh delete mode 100755 deployment/old/macos/package.sh delete mode 100644 deployment/old/portable/Dockerfile delete mode 100755 deployment/old/portable/clean.sh delete mode 100644 deployment/old/portable/dependencies.txt delete mode 100755 deployment/old/portable/docker-build.sh delete mode 100755 deployment/old/portable/package.sh delete mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.amd64 delete mode 100644 deployment/old/ubuntu-package-arm64/Dockerfile.arm64 delete mode 100755 deployment/old/ubuntu-package-arm64/clean.sh delete mode 100644 deployment/old/ubuntu-package-arm64/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-arm64/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-arm64/package.sh delete mode 120000 deployment/old/ubuntu-package-arm64/pkg-src delete mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.amd64 delete mode 100644 deployment/old/ubuntu-package-armhf/Dockerfile.armhf delete mode 100755 deployment/old/ubuntu-package-armhf/clean.sh delete mode 100644 deployment/old/ubuntu-package-armhf/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-armhf/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-armhf/package.sh delete mode 120000 deployment/old/ubuntu-package-armhf/pkg-src delete mode 100644 deployment/old/ubuntu-package-x64/Dockerfile delete mode 100755 deployment/old/ubuntu-package-x64/clean.sh delete mode 100644 deployment/old/ubuntu-package-x64/dependencies.txt delete mode 100755 deployment/old/ubuntu-package-x64/docker-build.sh delete mode 100755 deployment/old/ubuntu-package-x64/package.sh delete mode 120000 deployment/old/ubuntu-package-x64/pkg-src delete mode 100644 deployment/old/unraid/docker-templates/README.md delete mode 100644 deployment/old/unraid/docker-templates/jellyfin.xml delete mode 100644 deployment/old/win-x64/Dockerfile delete mode 100755 deployment/old/win-x64/clean.sh delete mode 100644 deployment/old/win-x64/dependencies.txt delete mode 100755 deployment/old/win-x64/docker-build.sh delete mode 100755 deployment/old/win-x64/package.sh delete mode 100644 deployment/old/win-x86/Dockerfile delete mode 100755 deployment/old/win-x86/clean.sh delete mode 100644 deployment/old/win-x86/dependencies.txt delete mode 100755 deployment/old/win-x86/docker-build.sh delete mode 100755 deployment/old/win-x86/package.sh diff --git a/deployment/old/README.md b/deployment/old/README.md deleted file mode 100644 index a805f59ca3..0000000000 --- a/deployment/old/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Jellyfin Packaging - -This directory contains the packaging configuration of Jellyfin for multiple platforms. The specification is below; all package platforms must follow the specification to be compatable with the central `build` script. - -## Package List - -### Operating System Packages - -* `debian-package-x64`: Package for Debian and Ubuntu amd64 systems. -* `fedora-package-x64`: Package for Fedora, CentOS, and Red Hat Enterprise Linux amd64 systems. - -### Portable Builds (archives) - -* `linux-x64`: Portable binary archive for generic Linux amd64 systems. -* `macos`: Portable binary archive for MacOS amd64 systems. -* `win-x64`: Portable binary archive for Windows amd64 systems. -* `win-x86`: Portable binary archive for Windows i386 systems. - -### Other Builds - -These builds are not necessarily run from the `build` script, but are present for other platforms. - -* `portable`: Compiled `.dll` for use with .NET Core runtime on any system. -* `docker`: Docker manifests for auto-publishing. -* `unraid`: unRaid Docker template; not built by `build` but imported into unRaid directly. -* `windows`: Support files and scripts for Windows CI build. - -## Package Specification - -### Dependencies - -* If a platform requires additional build dependencies, the required binary names, i.e. to validate `which `, should be specified in a `dependencies.txt` file inside the platform directory. - -* Each dependency should be present on its own line. - -### Action Scripts - -* Actions are defined in BASH scripts with the name `.sh` within the platform directory. - -* The list of valid actions are: - - 1. `build`: Builds a set of binaries. - 2. `package`: Assembles the compiled binaries into a package. - 3. `sign`: Performs signing actions on a package. - 4. `publish`: Performs a publishing action for a package. - 5. `clean`: Cleans up any artifacts from the previous actions. - -* All package actions are optional, however at least one should generate output files, and any that do should contain a `clean` action. - -* Actions are executed in the order specified above, and later actions may depend on former actions. - -* Actions except for `clean` should `set -o errexit` to terminate on failed actions. - -* The `clean` action should always `exit 0` even if no work is done or it fails. - -* The `clean` action can be passed a variable as argument 1, named `keep_artifacts`, containing either the value `y` or `n`. It is indended to handle situations when the user runs `build --keep-artifacts` and should be handled intelligently. Usually, this is used to preserve Docker images while still removing temporary directories. - -### Output Files - -* Upon completion of the defined actions, at least one output file must be created in the `/pkg-dist` directory. - -* Output files will be moved to the directory `jellyfin-build/` one directory above the repository root upon completion. diff --git a/deployment/old/centos-package-x64/Dockerfile b/deployment/old/centos-package-x64/Dockerfile deleted file mode 100644 index 08219a2e4a..0000000000 --- a/deployment/old/centos-package-x64/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM centos:7 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/centos-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist - -# Prepare CentOS environment -RUN yum update -y \ - && yum install -y epel-release - -# Install build dependencies -RUN yum install -y @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git - -# Install recent NodeJS and Yarn -RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \ - && rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \ - && yum install -y yarn - -# Install DotNET SDK -RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm \ - && rpmdev-setuptree \ - && yum install -y dotnet-sdk-${SDK_VERSION} - -# Create symlinks and directories -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/centos-package-x64/clean.sh b/deployment/old/centos-package-x64/clean.sh deleted file mode 100755 index 31455de0d4..0000000000 --- a/deployment/old/centos-package-x64/clean.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" -VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -package_source_dir="${WORKDIR}/pkg-src" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-centos-build" - -rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ - || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/centos-package-x64/dependencies.txt b/deployment/old/centos-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/centos-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/centos-package-x64/docker-build.sh b/deployment/old/centos-package-x64/docker-build.sh deleted file mode 100755 index 62dd144e50..0000000000 --- a/deployment/old/centos-package-x64/docker-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Builds the RPM inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Build RPM -make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS -rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/rpm -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/centos-package-x64/package.sh b/deployment/old/centos-package-x64/package.sh deleted file mode 100755 index 1b983f49d9..0000000000 --- a/deployment/old/centos-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-centos-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the RPMs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the RPMs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/centos-package-x64/pkg-src b/deployment/old/centos-package-x64/pkg-src deleted file mode 120000 index 3ff4d3cbf5..0000000000 --- a/deployment/old/centos-package-x64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../fedora-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-arm64/Dockerfile.amd64 b/deployment/old/debian-package-arm64/Dockerfile.amd64 deleted file mode 100644 index b63e08b7dd..0000000000 --- a/deployment/old/debian-package-arm64/Dockerfile.amd64 +++ /dev/null @@ -1,43 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Prepare the cross-toolchain -RUN dpkg --add-architecture arm64 \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 8 \ - && cd cross-gcc-packages-amd64/cross-gcc-8-arm64 \ - && apt-get install -y gcc-8-source libstdc++-8-dev-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-8-dev:arm64 - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] -#ENTRYPOINT ["/bin/bash"] diff --git a/deployment/old/debian-package-arm64/Dockerfile.arm64 b/deployment/old/debian-package-arm64/Dockerfile.arm64 deleted file mode 100644 index 9ca4868441..0000000000 --- a/deployment/old/debian-package-arm64/Dockerfile.arm64 +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=arm64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-arm64/clean.sh b/deployment/old/debian-package-arm64/clean.sh deleted file mode 100755 index e7bfdf8b4b..0000000000 --- a/deployment/old/debian-package-arm64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_arm64-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-arm64/dependencies.txt b/deployment/old/debian-package-arm64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-arm64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-arm64/docker-build.sh b/deployment/old/debian-package-arm64/docker-build.sh deleted file mode 100755 index 67ab6bd74b..0000000000 --- a/deployment/old/debian-package-arm64/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarm64 - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-arm64/package.sh b/deployment/old/debian-package-arm64/package.sh deleted file mode 100755 index 2091982187..0000000000 --- a/deployment/old/debian-package-arm64/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_arm64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.arm64" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-arm64/pkg-src b/deployment/old/debian-package-arm64/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/debian-package-arm64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/debian-package-armhf/Dockerfile.amd64 b/deployment/old/debian-package-armhf/Dockerfile.amd64 deleted file mode 100644 index 1b64b53148..0000000000 --- a/deployment/old/debian-package-armhf/Dockerfile.amd64 +++ /dev/null @@ -1,42 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Prepare the cross-toolchain -RUN dpkg --add-architecture armhf \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 8 \ - && cd cross-gcc-packages-amd64/cross-gcc-8-armhf \ - && apt-get install -y gcc-8-source libstdc++-8-dev-armhf-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip binutils-arm-linux-gnueabihf libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf liblttng-ust0:armhf libstdc++-8-dev:armhf - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/Dockerfile.armhf b/deployment/old/debian-package-armhf/Dockerfile.armhf deleted file mode 100644 index dd398b5aa5..0000000000 --- a/deployment/old/debian-package-armhf/Dockerfile.armhf +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=armhf - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-armhf/clean.sh b/deployment/old/debian-package-armhf/clean.sh deleted file mode 100755 index 35a3d3e9ad..0000000000 --- a/deployment/old/debian-package-armhf/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_armhf-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-armhf/dependencies.txt b/deployment/old/debian-package-armhf/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-armhf/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-armhf/docker-build.sh b/deployment/old/debian-package-armhf/docker-build.sh deleted file mode 100755 index 1bd7fb2911..0000000000 --- a/deployment/old/debian-package-armhf/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarmhf - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-armhf/package.sh b/deployment/old/debian-package-armhf/package.sh deleted file mode 100755 index 4a27dd8283..0000000000 --- a/deployment/old/debian-package-armhf/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian_armhf-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.armhf" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-armhf/pkg-src b/deployment/old/debian-package-armhf/pkg-src deleted file mode 120000 index 0bb6d55249..0000000000 --- a/deployment/old/debian-package-armhf/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/debian-package-x64/Dockerfile b/deployment/old/debian-package-x64/Dockerfile deleted file mode 100644 index e863d1edf9..0000000000 --- a/deployment/old/debian-package-x64/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget npm devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/debian-package-x64/clean.sh b/deployment/old/debian-package-x64/clean.sh deleted file mode 100755 index 4e507bcb27..0000000000 --- a/deployment/old/debian-package-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/debian-package-x64/dependencies.txt b/deployment/old/debian-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/debian-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/debian-package-x64/docker-build.sh b/deployment/old/debian-package-x64/docker-build.sh deleted file mode 100755 index 962a522ebc..0000000000 --- a/deployment/old/debian-package-x64/docker-build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -dpkg-buildpackage -us -uc - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/debian-package-x64/package.sh b/deployment/old/debian-package-x64/package.sh deleted file mode 100755 index 5a416959ab..0000000000 --- a/deployment/old/debian-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-debian-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/debian-package-x64/pkg-src/changelog b/deployment/old/debian-package-x64/pkg-src/changelog deleted file mode 100644 index 51c4822370..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/changelog +++ /dev/null @@ -1,59 +0,0 @@ -jellyfin (10.5.0-1) unstable; urgency=medium - - * New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 - - -- Jellyfin Packaging Team Fri, 11 Oct 2019 20:12:38 -0400 - -jellyfin (10.4.0-1) unstable; urgency=medium - - * New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 - - -- Jellyfin Packaging Team Sat, 31 Aug 2019 21:38:56 -0400 - -jellyfin (10.3.7-1) unstable; urgency=medium - - * New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 - - -- Jellyfin Packaging Team Wed, 24 Jul 2019 10:48:28 -0400 - -jellyfin (10.3.6-1) unstable; urgency=medium - - * New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 - - -- Jellyfin Packaging Team Sat, 06 Jul 2019 13:34:19 -0400 - -jellyfin (10.3.5-1) unstable; urgency=medium - - * New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 - - -- Jellyfin Packaging Team Sun, 09 Jun 2019 21:47:35 -0400 - -jellyfin (10.3.4-1) unstable; urgency=medium - - * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 - - -- Jellyfin Packaging Team Thu, 06 Jun 2019 22:45:31 -0400 - -jellyfin (10.3.3-1) unstable; urgency=medium - - * New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 - - -- Jellyfin Packaging Team Fri, 17 May 2019 23:12:08 -0400 - -jellyfin (10.3.2-1) unstable; urgency=medium - - * New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 - - -- Jellyfin Packaging Team Tue, 30 Apr 2019 20:18:44 -0400 - -jellyfin (10.3.1-1) unstable; urgency=medium - - * New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 - - -- Jellyfin Packaging Team Sat, 20 Apr 2019 14:24:07 -0400 - -jellyfin (10.3.0-1) unstable; urgency=medium - - * New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 - - -- Jellyfin Packaging Team Fri, 19 Apr 2019 14:24:29 -0400 diff --git a/deployment/old/debian-package-x64/pkg-src/compat b/deployment/old/debian-package-x64/pkg-src/compat deleted file mode 100644 index 45a4fb75db..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/compat +++ /dev/null @@ -1 +0,0 @@ -8 diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin deleted file mode 100644 index c6e595f15a..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin +++ /dev/null @@ -1,40 +0,0 @@ -# Jellyfin default configuration options -# This is a POSIX shell fragment - -# Use this file to override the default configurations; add additional -# options with JELLYFIN_ADD_OPTS. - -# Under systemd, use -# /etc/systemd/system/jellyfin.service.d/jellyfin.service.conf -# to override the user or this config file's location. - -# -# General options -# - -# Program directories -JELLYFIN_DATA_DIR="/var/lib/jellyfin" -JELLYFIN_CONFIG_DIR="/etc/jellyfin" -JELLYFIN_LOG_DIR="/var/log/jellyfin" -JELLYFIN_CACHE_DIR="/var/cache/jellyfin" - -# Restart script for in-app server control -JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh" - -# ffmpeg binary paths, overriding the system values -JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" - -# [OPTIONAL] run Jellyfin as a headless service -#JELLYFIN_SERVICE_OPT="--service" - -# [OPTIONAL] run Jellyfin without the web app -#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" - -# -# SysV init/Upstart options -# - -# Application username -JELLYFIN_USER="jellyfin" -# Full application command -JELLYFIN_ARGS="$JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers deleted file mode 100644 index b481ba4ad4..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin-sudoers +++ /dev/null @@ -1,37 +0,0 @@ -#Allow jellyfin group to start, stop and restart itself -Cmnd_Alias RESTARTSERVER_SYSV = /sbin/service jellyfin restart, /usr/sbin/service jellyfin restart -Cmnd_Alias STARTSERVER_SYSV = /sbin/service jellyfin start, /usr/sbin/service jellyfin start -Cmnd_Alias STOPSERVER_SYSV = /sbin/service jellyfin stop, /usr/sbin/service jellyfin stop -Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin -Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin -Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin -Cmnd_Alias RESTARTSERVER_INITD = /etc/init.d/jellyfin restart -Cmnd_Alias STARTSERVER_INITD = /etc/init.d/jellyfin start -Cmnd_Alias STOPSERVER_INITD = /etc/init.d/jellyfin stop - - -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSV -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_INITD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_INITD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_INITD - -Defaults!RESTARTSERVER_SYSV !requiretty -Defaults!STARTSERVER_SYSV !requiretty -Defaults!STOPSERVER_SYSV !requiretty -Defaults!RESTARTSERVER_SYSTEMD !requiretty -Defaults!STARTSERVER_SYSTEMD !requiretty -Defaults!STOPSERVER_SYSTEMD !requiretty -Defaults!RESTARTSERVER_INITD !requiretty -Defaults!STARTSERVER_INITD !requiretty -Defaults!STOPSERVER_INITD !requiretty - -#Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - -Defaults:jellyfin !requiretty diff --git a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf b/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf deleted file mode 100644 index 1b69dd74ef..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/jellyfin.service.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Jellyfin systemd configuration options - -# Use this file to override the user or environment file location. - -[Service] -#User = jellyfin -#EnvironmentFile = /etc/default/jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/conf/logging.json b/deployment/old/debian-package-x64/pkg-src/conf/logging.json deleted file mode 100644 index f32b2089eb..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/conf/logging.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Serilog": { - "MinimumLevel": "Information", - "WriteTo": [ - { - "Name": "Console", - "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" - } - }, - { - "Name": "Async", - "Args": { - "configure": [ - { - "Name": "File", - "Args": { - "path": "%JELLYFIN_LOG_DIR%//jellyfin.log", - "fileSizeLimitBytes": 10485700, - "rollOnFileSizeLimit": true, - "retainedFileCountLimit": 10, - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" - } - } - ] - } - } - ] - } -} diff --git a/deployment/old/debian-package-x64/pkg-src/control b/deployment/old/debian-package-x64/pkg-src/control deleted file mode 100644 index 13fd3ecabb..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/control +++ /dev/null @@ -1,31 +0,0 @@ -Source: jellyfin -Section: misc -Priority: optional -Maintainer: Jellyfin Team -Build-Depends: debhelper (>= 9), - dotnet-sdk-3.1, - libc6-dev, - libcurl4-openssl-dev, - libfontconfig1-dev, - libfreetype6-dev, - libssl-dev, - wget, - npm | nodejs -Standards-Version: 3.9.4 -Homepage: https://jellyfin.media/ -Vcs-Git: https://github.org/jellyfin/jellyfin.git -Vcs-Browser: https://github.org/jellyfin/jellyfin - -Package: jellyfin -Replaces: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Breaks: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server -Architecture: any -Depends: at, - libsqlite3-0, - jellyfin-ffmpeg, - libfontconfig1, - libfreetype6, - libssl1.1 -Description: Jellyfin is a home media server. - It is built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono. It features a REST-based api with built-in documentation to facilitate client development. We also have client libraries for our api to enable rapid development. diff --git a/deployment/old/debian-package-x64/pkg-src/copyright b/deployment/old/debian-package-x64/pkg-src/copyright deleted file mode 100644 index 0d7a2a6007..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/copyright +++ /dev/null @@ -1,29 +0,0 @@ -Format: http://dep.debian.net/deps/dep5 -Upstream-Name: jellyfin -Source: https://github.com/jellyfin/jellyfin - -Files: * -Copyright: 2018 Jellyfin Team -License: GPL-2.0+ - -Files: debian/* -Copyright: 2018 Joshua Boniface -Copyright: 2014 Carlos Hernandez -License: GPL-2.0+ - -License: GPL-2.0+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/deployment/old/debian-package-x64/pkg-src/gbp.conf b/deployment/old/debian-package-x64/pkg-src/gbp.conf deleted file mode 100644 index 60b3d28723..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/gbp.conf +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -pristine-tar = False -cleaner = fakeroot debian/rules clean - -[import-orig] -filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ] diff --git a/deployment/old/debian-package-x64/pkg-src/install b/deployment/old/debian-package-x64/pkg-src/install deleted file mode 100644 index 994322d141..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/install +++ /dev/null @@ -1,6 +0,0 @@ -usr/lib/jellyfin usr/lib/ -debian/conf/jellyfin etc/default/ -debian/conf/logging.json etc/jellyfin/ -debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/ -debian/conf/jellyfin-sudoers etc/sudoers.d/ -debian/bin/restart.sh usr/lib/jellyfin/ diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.init b/deployment/old/debian-package-x64/pkg-src/jellyfin.init deleted file mode 100644 index 7f5642bac1..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.init +++ /dev/null @@ -1,61 +0,0 @@ -### BEGIN INIT INFO -# Provides: Jellyfin Media Server -# Required-Start: $local_fs $network -# Required-Stop: $local_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Jellyfin Media Server -# Description: Runs Jellyfin Server -### END INIT INFO - -set -e - -# Carry out specific functions when asked to by the system - -if test -f /etc/default/jellyfin; then - . /etc/default/jellyfin -fi - -. /lib/lsb/init-functions - -PIDFILE="/run/jellyfin.pid" - -case "$1" in - start) - log_daemon_msg "Starting Jellyfin Media Server" "jellyfin" || true - - if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - stop) - log_daemon_msg "Stopping Jellyfin Media Server" "jellyfin" || true - if start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE --remove-pidfile; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - restart) - log_daemon_msg "Restarting Jellyfin Media Server" "jellyfin" || true - start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile $PIDFILE --remove-pidfile - if start-stop-daemon --start --quiet --oknodo --background --pidfile $PIDFILE --make-pidfile --user $JELLYFIN_USER --chuid $JELLYFIN_USER --exec /usr/bin/jellyfin -- $JELLYFIN_ARGS; then - log_end_msg 0 || true - else - log_end_msg 1 || true - fi - ;; - - status) - status_of_proc -p $PIDFILE /usr/bin/jellyfin jellyfin && exit 0 || exit $? - ;; - - *) - echo "Usage: $0 {start|stop|restart|status}" - exit 1 - ;; -esac diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.service b/deployment/old/debian-package-x64/pkg-src/jellyfin.service deleted file mode 100644 index 1305e238b0..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description = Jellyfin Media Server -After = network.target - -[Service] -Type = simple -EnvironmentFile = /etc/default/jellyfin -User = jellyfin -ExecStart = /usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} -Restart = on-failure -TimeoutSec = 15 - -[Install] -WantedBy = multi-user.target diff --git a/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart b/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart deleted file mode 100644 index ef5bc9bcaf..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/jellyfin.upstart +++ /dev/null @@ -1,20 +0,0 @@ -description "jellyfin daemon" - -start on (local-filesystems and net-device-up IFACE!=lo) -stop on runlevel [!2345] - -console log -respawn -respawn limit 10 5 - -kill timeout 20 - -script - set -x - echo "Starting $UPSTART_JOB" - - # Log file - logger -t "$0" "DEBUG: `set`" - . /etc/default/jellyfin - exec su -u $JELLYFIN_USER -c /usr/bin/jellyfin $JELLYFIN_ARGS -end script diff --git a/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in b/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in deleted file mode 100644 index cef83a3407..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/po/POTFILES.in +++ /dev/null @@ -1 +0,0 @@ -[type: gettext/rfc822deb] templates diff --git a/deployment/old/debian-package-x64/pkg-src/po/templates.pot b/deployment/old/debian-package-x64/pkg-src/po/templates.pot deleted file mode 100644 index 2cdcae4173..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/po/templates.pot +++ /dev/null @@ -1,57 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: jellyfin-server\n" -"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n" -"POT-Creation-Date: 2015-06-12 20:51-0600\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: note -#. Description -#: ../templates:1001 -msgid "Jellyfin permission info:" -msgstr "" - -#. Type: note -#. Description -#: ../templates:1001 -msgid "" -"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the " -"user jellyfin has read and write access to any folders you wish to add to your " -"library. Otherwise please run jellyfin under a different user." -msgstr "" - -#. Type: string -#. Description -#: ../templates:2001 -msgid "Username to run Jellyfin as:" -msgstr "" - -#. Type: string -#. Description -#: ../templates:2001 -msgid "The user that jellyfin will run as." -msgstr "" - -#. Type: note -#. Description -#: ../templates:3001 -msgid "Jellyfin still running" -msgstr "" - -#. Type: note -#. Description -#: ../templates:3001 -msgid "Jellyfin is currently running. Please close it and try again." -msgstr "" diff --git a/deployment/old/debian-package-x64/pkg-src/postinst b/deployment/old/debian-package-x64/pkg-src/postinst deleted file mode 100644 index 860222e051..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/postinst +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -case "$1" in - configure) - # create jellyfin group if it does not exist - if [[ -z "$(getent group jellyfin)" ]]; then - addgroup --quiet --system jellyfin > /dev/null 2>&1 - fi - # create jellyfin user if it does not exist - if [[ -z "$(getent passwd jellyfin)" ]]; then - adduser --system --ingroup jellyfin --shell /bin/false jellyfin --no-create-home --home ${PROGRAMDATA} \ - --gecos "Jellyfin default user" > /dev/null 2>&1 - fi - # ensure $PROGRAMDATA exists - if [[ ! -d $PROGRAMDATA ]]; then - mkdir $PROGRAMDATA - fi - # ensure $CONFIGDATA exists - if [[ ! -d $CONFIGDATA ]]; then - mkdir $CONFIGDATA - fi - # ensure $LOGDATA exists - if [[ ! -d $LOGDATA ]]; then - mkdir $LOGDATA - fi - # ensure $CACHEDATA exists - if [[ ! -d $CACHEDATA ]]; then - mkdir $CACHEDATA - fi - # Ensure permissions are correct on all config directories - chown -R jellyfin $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA - - chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true - - # Install jellyfin symlink into /usr/bin - ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin - - ;; - abort-upgrade|abort-remove|abort-deconfigure) - ;; - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER - -if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - # Manual init script handling - deb-systemd-helper unmask jellyfin.service >/dev/null || true - # was-enabled defaults to true, so new installations run enable. - if deb-systemd-helper --quiet was-enabled jellyfin.service; then - # Enables the unit on first installation, creates new - # symlinks on upgrades if the unit file has changed. - deb-systemd-helper enable jellyfin.service >/dev/null || true - else - # Update the statefile to add new symlinks (if any), which need to be - # cleaned up on purge. Also remove old symlinks. - deb-systemd-helper update-state jellyfin.service >/dev/null || true - fi -fi - -# End automatically added section -# Automatically added by dh_installinit -if [[ "$1" == "configure" ]] || [[ "$1" == "abort-upgrade" ]]; then - if [[ -d "/run/systemd/systemd" ]]; then - systemctl --system daemon-reload >/dev/null || true - deb-systemd-invoke start jellyfin >/dev/null || true - elif [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.conf" ]]; then - update-rc.d jellyfin defaults >/dev/null - invoke-rc.d jellyfin start || exit $? - fi -fi -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/postrm b/deployment/old/debian-package-x64/pkg-src/postrm deleted file mode 100644 index 1d00a984ec..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/postrm +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -# In case this system is running systemd, we make systemd reload the unit files -# to pick up changes. -if [[ -d /run/systemd/system ]] ; then - systemctl --system daemon-reload >/dev/null || true -fi - -case "$1" in - purge) - echo PURGE | debconf-communicate $NAME > /dev/null 2>&1 || true - - if [[ -x "/etc/init.d/jellyfin" ]] || [[ -e "/etc/init/jellyfin.connf" ]]; then - update-rc.d jellyfin remove >/dev/null 2>&1 || true - fi - - if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - deb-systemd-helper purge jellyfin.service >/dev/null - deb-systemd-helper unmask jellyfin.service >/dev/null - fi - - # Remove user and group - userdel jellyfin > /dev/null 2>&1 || true - delgroup --quiet jellyfin > /dev/null 2>&1 || true - # Remove config dir - if [[ -d $CONFIGDATA ]]; then - rm -rf $CONFIGDATA - fi - # Remove log dir - if [[ -d $LOGDATA ]]; then - rm -rf $LOGDATA - fi - # Remove cache dir - if [[ -d $CACHEDATA ]]; then - rm -rf $CACHEDATA - fi - # Remove program data dir - if [[ -d $PROGRAMDATA ]]; then - rm -rf $PROGRAMDATA - fi - # Remove binary symlink - [[ -f /usr/bin/jellyfin ]] && rm /usr/bin/jellyfin - # Remove sudoers config - [[ -f /etc/sudoers.d/jellyfin-sudoers ]] && rm /etc/sudoers.d/jellyfin-sudoers - # Remove anything at the default locations; catches situations where the user moved the defaults - [[ -e /etc/jellyfin ]] && rm -rf /etc/jellyfin - [[ -e /var/log/jellyfin ]] && rm -rf /var/log/jellyfin - [[ -e /var/cache/jellyfin ]] && rm -rf /var/cache/jellyfin - [[ -e /var/lib/jellyfin ]] && rm -rf /var/lib/jellyfin - ;; - remove) - if [[ -x "/usr/bin/deb-systemd-helper" ]]; then - deb-systemd-helper mask jellyfin.service >/dev/null - fi - ;; - upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) - ;; - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/preinst b/deployment/old/debian-package-x64/pkg-src/preinst deleted file mode 100644 index 2713fb9b80..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/preinst +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -# In case this system is running systemd, we make systemd reload the unit files -# to pick up changes. -if [[ -d /run/systemd/system ]] ; then - systemctl --system daemon-reload >/dev/null || true -fi - -case "$1" in - install|upgrade) - # try graceful termination; - if [[ -d /run/systemd/system ]]; then - deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true - elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then - invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true - fi - # try and figure out if jellyfin is running - PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) - [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) - # if its running, let's stop it - if [[ -n "$JELLYFIN_PID" ]]; then - echo "Stopping Jellyfin!" - # if jellyfin is still running, kill it - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - CPIDS=$(pgrep -P $JELLYFIN_PID) - sleep 2 && kill -KILL $CPIDS - kill -TERM $CPIDS > /dev/null 2>&1 - fi - sleep 1 - # if it's still running, show error - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - echo "Could not successfully stop JellyfinServer, please do so before uninstalling." - exit 1 - else - [[ -f $PIDFILE ]] && rm $PIDFILE - fi - fi - - # Clean up old Emby cruft that can break the user's system - [[ -f /etc/sudoers.d/emby ]] && rm -f /etc/sudoers.d/emby - - # If we have existing config, log, or cache dirs in /var/lib/jellyfin, move them into the right place - if [[ -d $PROGRAMDATA/config ]]; then - mv $PROGRAMDATA/config $CONFIGDATA - fi - if [[ -d $PROGRAMDATA/logs ]]; then - mv $PROGRAMDATA/logs $LOGDATA - fi - if [[ -d $PROGRAMDATA/logs ]]; then - mv $PROGRAMDATA/cache $CACHEDATA - fi - - ;; - abort-upgrade) - ;; - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/prerm b/deployment/old/debian-package-x64/pkg-src/prerm deleted file mode 100644 index e965cb7d71..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/prerm +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -set -e - -NAME=jellyfin -DEFAULT_FILE=/etc/default/${NAME} - -# Source Jellyfin default configuration -if [[ -f $DEFAULT_FILE ]]; then - . $DEFAULT_FILE -fi - -# Data directories for program data (cache, db), configs, and logs -PROGRAMDATA=${JELLYFIN_DATA_DIRECTORY-/var/lib/$NAME} -CONFIGDATA=${JELLYFIN_CONFIG_DIRECTORY-/etc/$NAME} -LOGDATA=${JELLYFIN_LOG_DIRECTORY-/var/log/$NAME} -CACHEDATA=${JELLYFIN_CACHE_DIRECTORY-/var/cache/$NAME} - -case "$1" in - remove|upgrade|deconfigure) - echo "Stopping Jellyfin!" - # try graceful termination; - if [[ -d /run/systemd/system ]]; then - deb-systemd-invoke stop ${NAME}.service > /dev/null 2>&1 || true - elif [ -x "/etc/init.d/${NAME}" ] || [ -e "/etc/init/${NAME}.conf" ]; then - invoke-rc.d ${NAME} stop > /dev/null 2>&1 || true - fi - # Ensure that it is shutdown - PIDFILE=$(find /var/run/ -maxdepth 1 -mindepth 1 -name "jellyfin*.pid" -print -quit) - [[ -n "$PIDFILE" ]] && [[ -s "$PIDFILE" ]] && JELLYFIN_PID=$(cat ${PIDFILE}) - # if its running, let's stop it - if [[ -n "$JELLYFIN_PID" ]]; then - # if jellyfin is still running, kill it - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - CPIDS=$(pgrep -P $JELLYFIN_PID) - sleep 2 && kill -KILL $CPIDS - kill -TERM $CPIDS > /dev/null 2>&1 - fi - sleep 1 - # if it's still running, show error - if [[ -n "$(ps -p $JELLYFIN_PID -o pid=)" ]]; then - echo "Could not successfully stop Jellyfin, please do so before uninstalling." - exit 1 - else - [[ -f $PIDFILE ]] && rm $PIDFILE - fi - fi - if [[ -f /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so ]]; then - rm /usr/lib/jellyfin/bin/MediaBrowser.Server.Mono.exe.so - fi - ;; - failed-upgrade) - ;; - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 diff --git a/deployment/old/debian-package-x64/pkg-src/rules b/deployment/old/debian-package-x64/pkg-src/rules deleted file mode 100755 index c2d57dfb22..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/rules +++ /dev/null @@ -1,66 +0,0 @@ -#! /usr/bin/make -f -CONFIG := Release -TERM := xterm -SHELL := /bin/bash -WEB_TARGET := $(CURDIR)/MediaBrowser.WebDashboard/jellyfin-web -WEB_VERSION := $(shell sed -n -e 's/^version: "\(.*\)"/\1/p' $(CURDIR)/build.yaml) - -HOST_ARCH := $(shell arch) -BUILD_ARCH := ${DEB_HOST_MULTIARCH} -ifeq ($(HOST_ARCH),x86_64) - # Building AMD64 - DOTNETRUNTIME := debian-x64 - ifeq ($(BUILD_ARCH),arm-linux-gnueabihf) - # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm - endif - ifeq ($(BUILD_ARCH),aarch64-linux-gnu) - # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm64 - endif -endif -ifeq ($(HOST_ARCH),armv7l) - # Building ARM - DOTNETRUNTIME := debian-arm -endif -ifeq ($(HOST_ARCH),arm64) - # Building ARM - DOTNETRUNTIME := debian-arm64 -endif - -export DH_VERBOSE=1 -export DOTNET_CLI_TELEMETRY_OPTOUT=1 - -%: - dh $@ - -# disable "make check" -override_dh_auto_test: - -# disable stripping debugging symbols -override_dh_clistrip: - -override_dh_auto_build: - echo $(WEB_VERSION) - # Clone down and build Web frontend - mkdir -p $(WEB_TARGET) - wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/v$(WEB_VERSION).tar.gz || wget -O web-src.tgz https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz - mkdir -p $(CURDIR)/web - tar -xzf web-src.tgz -C $(CURDIR)/web/ --strip 1 - cd $(CURDIR)/web/ && npm install yarn - cd $(CURDIR)/web/ && node_modules/yarn/bin/yarn install - mv $(CURDIR)/web/dist/* $(WEB_TARGET)/ - # Build the application - dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \ - "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server - -override_dh_auto_clean: - dotnet clean -maxcpucount:1 --configuration $(CONFIG) Jellyfin.Server || true - rm -f '$(CURDIR)/web-src.tgz' - rm -rf '$(CURDIR)/usr' - rm -rf '$(CURDIR)/web' - rm -rf '$(WEB_TARGET)' - -# Force the service name to jellyfin even if we're building jellyfin-nightly -override_dh_installinit: - dh_installinit --name=jellyfin diff --git a/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides b/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides deleted file mode 100644 index aeb332f13a..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source.lintian-overrides +++ /dev/null @@ -1,3 +0,0 @@ -# This is an override for the following lintian errors: -jellyfin source: license-problem-md5sum-non-free-file Emby.Drawing/ImageMagick/fonts/webdings.ttf* -jellyfin source: source-is-missing diff --git a/deployment/old/debian-package-x64/pkg-src/source/format b/deployment/old/debian-package-x64/pkg-src/source/format deleted file mode 100644 index d3827e75a5..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source/format +++ /dev/null @@ -1 +0,0 @@ -1.0 diff --git a/deployment/old/debian-package-x64/pkg-src/source/options b/deployment/old/debian-package-x64/pkg-src/source/options deleted file mode 100644 index 17b5373d5e..0000000000 --- a/deployment/old/debian-package-x64/pkg-src/source/options +++ /dev/null @@ -1,11 +0,0 @@ -tar-ignore='.git*' -tar-ignore='**/.git' -tar-ignore='**/.hg' -tar-ignore='**/.vs' -tar-ignore='**/.vscode' -tar-ignore='deployment' -tar-ignore='**/bin' -tar-ignore='**/obj' -tar-ignore='**/.nuget' -tar-ignore='*.deb' -tar-ignore='ThirdParty' diff --git a/deployment/old/fedora-package-x64/Dockerfile b/deployment/old/fedora-package-x64/Dockerfile deleted file mode 100644 index 87120f3a05..0000000000 --- a/deployment/old/fedora-package-x64/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM fedora:31 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist - -# Prepare Fedora environment -RUN dnf update -y - -# Install build dependencies -RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn - -# Install DotNET SDK -RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ - && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ - && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} - -# Create symlinks and directories -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${PLATFORM_DIR}/pkg-src/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/SOURCES - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/fedora-package-x64/clean.sh b/deployment/old/fedora-package-x64/clean.sh deleted file mode 100755 index 700c8f1bb3..0000000000 --- a/deployment/old/fedora-package-x64/clean.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" -VERSION="$( grep -A1 '^Version:' ${WORKDIR}/pkg-src/jellyfin.spec | awk '{ print $NF }' )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -package_source_dir="${WORKDIR}/pkg-src" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-fedora-build" - -rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null \ - || sudo rm -f "${package_source_dir}/jellyfin-${VERSION}.tar.gz" &>/dev/null - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/fedora-package-x64/dependencies.txt b/deployment/old/fedora-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/fedora-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/fedora-package-x64/docker-build.sh b/deployment/old/fedora-package-x64/docker-build.sh deleted file mode 100755 index 740e8d35ca..0000000000 --- a/deployment/old/fedora-package-x64/docker-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# Builds the RPM inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Build RPM -make -f .copr/Makefile srpm outdir=/root/rpmbuild/SRPMS -rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/rpm -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/rpm/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/fedora-package-x64/package.sh b/deployment/old/fedora-package-x64/package.sh deleted file mode 100755 index ae6962dd1f..0000000000 --- a/deployment/old/fedora-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-fedora-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the RPMs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the RPMs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/rpm/* "${output_dir}" diff --git a/deployment/old/fedora-package-x64/pkg-src/.gitignore b/deployment/old/fedora-package-x64/pkg-src/.gitignore deleted file mode 100644 index 6019b98c22..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.rpm -*.zip -*.tar.gz \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/README.md b/deployment/old/fedora-package-x64/pkg-src/README.md deleted file mode 100644 index 7ed6f7efc6..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Jellyfin RPM - -## Build Fedora Package with docker - -Change into this directory `cd rpm-package` -Run the build script `./build-fedora-rpm.sh`. -Resulting RPM and src.rpm will be in `../../jellyfin-*.rpm` - -## ffmpeg - -The RPM package for Fedora/CentOS requires some additional repositories as ffmpeg is not in the main repositories. - -```shell -# ffmpeg from RPMfusion free -# Fedora -$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm -# CentOS 7 -$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm -``` - -## ISO mounting - -To allow Jellyfin to mount/umount ISO files uncomment these two lines in `/etc/sudoers.d/jellyfin-sudoers` -``` -# %jellyfin ALL=(ALL) NOPASSWD: /bin/mount -# %jellyfin ALL=(ALL) NOPASSWD: /bin/umount -``` - -## Building with dotnet - -Jellyfin is build with `--self-contained` so no dotnet required for runtime. - -```shell -# dotnet required for building the RPM -# Fedora -$ sudo dnf copr enable @dotnet-sig/dotnet -# CentOS -$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm -``` - -## TODO - -- [ ] OpenSUSE \ No newline at end of file diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml b/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml deleted file mode 100644 index 538c5d65f8..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin-firewalld.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - Jellyfin - The Free Software Media System. - - - - - diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.env b/deployment/old/fedora-package-x64/pkg-src/jellyfin.env deleted file mode 100644 index de48f13af5..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.env +++ /dev/null @@ -1,34 +0,0 @@ -# Jellyfin default configuration options - -# Use this file to override the default configurations; add additional -# options with JELLYFIN_ADD_OPTS. - -# To override the user or this config file's location, use -# /etc/systemd/system/jellyfin.service.d/override.conf - -# -# This is a POSIX shell fragment -# - -# -# General options -# - -# Program directories -JELLYFIN_DATA_DIR="/var/lib/jellyfin" -JELLYFIN_CONFIG_DIR="/etc/jellyfin" -JELLYFIN_LOG_DIR="/var/log/jellyfin" -JELLYFIN_CACHE_DIR="/var/cache/jellyfin" - -# In-App service control -JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" - -# [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values -#JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg" - -# [OPTIONAL] run Jellyfin as a headless service -#JELLYFIN_SERVICE_OPT="--service" - -# [OPTIONAL] run Jellyfin without the web app -#JELLYFIN_NOWEBAPP_OPT="--noautorunwebapp" - diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf b/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf deleted file mode 100644 index 8652450bb4..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.override.conf +++ /dev/null @@ -1,7 +0,0 @@ -# Jellyfin systemd configuration options - -# Use this file to override the user or environment file location. - -[Service] -#User = jellyfin -#EnvironmentFile = /etc/sysconfig/jellyfin diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.service b/deployment/old/fedora-package-x64/pkg-src/jellyfin.service deleted file mode 100644 index f3dc594b1c..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -After=network.target -Description=Jellyfin is a free software media system that puts you in control of managing and streaming your media. - -[Service] -EnvironmentFile=/etc/sysconfig/jellyfin -WorkingDirectory=/var/lib/jellyfin -ExecStart=/usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} -TimeoutSec=15 -Restart=on-failure -User=jellyfin -Group=jellyfin - -[Install] -WantedBy=multi-user.target diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec deleted file mode 100644 index 33c6f6f648..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.spec +++ /dev/null @@ -1,181 +0,0 @@ -%global debug_package %{nil} -# Set the dotnet runtime -%if 0%{?fedora} -%global dotnet_runtime fedora-x64 -%else -%global dotnet_runtime centos-x64 -%endif - -Name: jellyfin -Version: 10.5.0 -Release: 1%{?dist} -Summary: The Free Software Media Browser -License: GPLv2 -URL: https://jellyfin.media -# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` -Source0: https://github.com/%{name}/%{name}/archive/%{name}-%{version}.tar.gz -# Jellyfin Webinterface downloaded by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz` -Source1: https://github.com/%{name}/%{name}-web/archive/%{name}-web-%{version}.tar.gz -Source11: jellyfin.service -Source12: jellyfin.env -Source13: jellyfin.sudoers -Source14: restart.sh -Source15: jellyfin.override.conf -Source16: jellyfin-firewalld.xml - -%{?systemd_requires} -BuildRequires: systemd -Requires(pre): shadow-utils -BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, glibc-devel, libicu-devel, git -%if 0%{?fedora} -BuildRequires: nodejs-yarn, git -%else -# Requirements not packaged in main repos -# From https://rpm.nodesource.com/pub_10.x/el/7/x86_64/ -BuildRequires: nodejs >= 10 yarn -%endif -Requires: libcurl, fontconfig, freetype, openssl, glibc libicu -# Requirements not packaged in main repos -# COPR @dotnet-sig/dotnet or -# https://packages.microsoft.com/rhel/7/prod/ -BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 -# RPMfusion free -Requires: ffmpeg - -# Disable Automatic Dependency Processing -AutoReqProv: no - -%description -Jellyfin is a free software media system that puts you in control of managing and streaming your media. - - -%prep -%autosetup -n %{name}-%{version} -b 0 -b 1 -web_build_dir="$(mktemp -d)" -web_target="$PWD/MediaBrowser.WebDashboard/jellyfin-web" -pushd ../jellyfin-web-%{version} || pushd ../jellyfin-web-master -%if 0%{?fedora} -nodejs-yarn install -%else -yarn install -%endif -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd - -%build - -%install -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -dotnet publish --configuration Release --output='%{buildroot}%{_libdir}/jellyfin' --self-contained --runtime %{dotnet_runtime} \ - "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" Jellyfin.Server -%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE -%{__install} -D -m 0644 %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/%{name}.service.d/override.conf -%{__install} -D -m 0644 Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/%{name}/logging.json -%{__mkdir} -p %{buildroot}%{_bindir} -tee %{buildroot}%{_bindir}/jellyfin << EOF -#!/bin/sh -exec %{_libdir}/%{name}/%{name} \${@} -EOF -%{__mkdir} -p %{buildroot}%{_sharedstatedir}/jellyfin -%{__mkdir} -p %{buildroot}%{_sysconfdir}/%{name} -%{__mkdir} -p %{buildroot}%{_var}/log/jellyfin -%{__mkdir} -p %{buildroot}%{_var}/cache/jellyfin - -%{__install} -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service -%{__install} -D -m 0644 %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/%{name} -%{__install} -D -m 0600 %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/%{name}-sudoers -%{__install} -D -m 0755 %{SOURCE14} %{buildroot}%{_libexecdir}/%{name}/restart.sh -%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/%{name}.xml - -%files -%{_libdir}/%{name}/jellyfin-web/* -%attr(755,root,root) %{_bindir}/%{name} -%{_libdir}/%{name}/*.json -%{_libdir}/%{name}/*.dll -%{_libdir}/%{name}/*.so -%{_libdir}/%{name}/*.a -%{_libdir}/%{name}/createdump -# Needs 755 else only root can run it since binary build by dotnet is 722 -%attr(755,root,root) %{_libdir}/%{name}/jellyfin -%{_libdir}/%{name}/SOS_README.md -%{_unitdir}/%{name}.service -%{_libexecdir}/%{name}/restart.sh -%{_prefix}/lib/firewalld/services/%{name}.xml -%attr(755,jellyfin,jellyfin) %dir %{_sysconfdir}/%{name} -%config %{_sysconfdir}/sysconfig/%{name} -%config(noreplace) %attr(600,root,root) %{_sysconfdir}/sudoers.d/%{name}-sudoers -%config(noreplace) %{_sysconfdir}/systemd/system/%{name}.service.d/override.conf -%config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/%{name}/logging.json -%attr(750,jellyfin,jellyfin) %dir %{_sharedstatedir}/jellyfin -%attr(-,jellyfin,jellyfin) %dir %{_var}/log/jellyfin -%attr(750,jellyfin,jellyfin) %dir %{_var}/cache/jellyfin -%if 0%{?fedora} -%license LICENSE -%else -%{_datadir}/licenses/%{name}/LICENSE -%endif - -%pre -getent group jellyfin >/dev/null || groupadd -r jellyfin -getent passwd jellyfin >/dev/null || \ - useradd -r -g jellyfin -d %{_sharedstatedir}/jellyfin -s /sbin/nologin \ - -c "Jellyfin default user" jellyfin -exit 0 - -%post -# Move existing configuration cache and logs to their new locations and symlink them. -if [ $1 -gt 1 ] ; then - service_state=$(systemctl is-active jellyfin.service) - if [ "${service_state}" = "active" ]; then - systemctl stop jellyfin.service - fi - if [ ! -L %{_sharedstatedir}/%{name}/config ]; then - mv %{_sharedstatedir}/%{name}/config/* %{_sysconfdir}/%{name}/ - rmdir %{_sharedstatedir}/%{name}/config - ln -sf %{_sysconfdir}/%{name} %{_sharedstatedir}/%{name}/config - fi - if [ ! -L %{_sharedstatedir}/%{name}/logs ]; then - mv %{_sharedstatedir}/%{name}/logs/* %{_var}/log/jellyfin - rmdir %{_sharedstatedir}/%{name}/logs - ln -sf %{_var}/log/jellyfin %{_sharedstatedir}/%{name}/logs - fi - if [ ! -L %{_sharedstatedir}/%{name}/cache ]; then - mv %{_sharedstatedir}/%{name}/cache/* %{_var}/cache/jellyfin - rmdir %{_sharedstatedir}/%{name}/cache - ln -sf %{_var}/cache/jellyfin %{_sharedstatedir}/%{name}/cache - fi - if [ "${service_state}" = "active" ]; then - systemctl start jellyfin.service - fi -fi -%systemd_post jellyfin.service - -%preun -%systemd_preun jellyfin.service - -%postun -%systemd_postun_with_restart jellyfin.service - -%changelog -* Fri Oct 11 2019 Jellyfin Packaging Team -- New upstream version 10.5.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.5.0 -* Sat Aug 31 2019 Jellyfin Packaging Team -- New upstream version 10.4.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.4.0 -* Wed Jul 24 2019 Jellyfin Packaging Team -- New upstream version 10.3.7; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.7 -* Sat Jul 06 2019 Jellyfin Packaging Team -- New upstream version 10.3.6; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.6 -* Sun Jun 09 2019 Jellyfin Packaging Team -- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5 -* Thu Jun 06 2019 Jellyfin Packaging Team -- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4 -* Fri May 17 2019 Jellyfin Packaging Team -- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3 -* Tue Apr 30 2019 Jellyfin Packaging Team -- New upstream version 10.3.2; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.2 -* Sat Apr 20 2019 Jellyfin Packaging Team -- New upstream version 10.3.1; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.1 -* Fri Apr 19 2019 Jellyfin Packaging Team -- New upstream version 10.3.0; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.0 diff --git a/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers b/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers deleted file mode 100644 index dd245af4b8..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/jellyfin.sudoers +++ /dev/null @@ -1,19 +0,0 @@ -# Allow jellyfin group to start, stop and restart itself -Cmnd_Alias RESTARTSERVER_SYSTEMD = /usr/bin/systemctl restart jellyfin, /bin/systemctl restart jellyfin -Cmnd_Alias STARTSERVER_SYSTEMD = /usr/bin/systemctl start jellyfin, /bin/systemctl start jellyfin -Cmnd_Alias STOPSERVER_SYSTEMD = /usr/bin/systemctl stop jellyfin, /bin/systemctl stop jellyfin - - -jellyfin ALL=(ALL) NOPASSWD: RESTARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STARTSERVER_SYSTEMD -jellyfin ALL=(ALL) NOPASSWD: STOPSERVER_SYSTEMD - -Defaults!RESTARTSERVER_SYSTEMD !requiretty -Defaults!STARTSERVER_SYSTEMD !requiretty -Defaults!STOPSERVER_SYSTEMD !requiretty - -# Allow the server to mount iso images -jellyfin ALL=(ALL) NOPASSWD: /bin/mount -jellyfin ALL=(ALL) NOPASSWD: /bin/umount - -Defaults:jellyfin !requiretty diff --git a/deployment/old/fedora-package-x64/pkg-src/restart.sh b/deployment/old/fedora-package-x64/pkg-src/restart.sh deleted file mode 100755 index 9b64b6d728..0000000000 --- a/deployment/old/fedora-package-x64/pkg-src/restart.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# restart.sh - Jellyfin server restart script -# Part of the Jellyfin project (https://github.com/jellyfin) -# -# This script restarts the Jellyfin daemon on Linux when using -# the Restart button on the admin dashboard. It supports the -# systemctl, service, and traditional /etc/init.d (sysv) restart -# methods, chosen automatically by which one is found first (in -# that order). -# -# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. - -get_service_command() { - for command in systemctl service; do - if which $command &>/dev/null; then - echo $command && return - fi - done - echo "sysv" -} - -cmd="$( get_service_command )" -echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." -case $cmd in - 'systemctl') - echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now - ;; - 'service') - echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now - ;; - 'sysv') - echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now - ;; -esac -exit 0 diff --git a/deployment/old/linux-x64/Dockerfile b/deployment/old/linux-x64/Dockerfile deleted file mode 100644 index c47057546d..0000000000 --- a/deployment/old/linux-x64/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/linux-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/linux-x64/clean.sh b/deployment/old/linux-x64/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/linux-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/linux-x64/dependencies.txt b/deployment/old/linux-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/linux-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/linux-x64/docker-build.sh b/deployment/old/linux-x64/docker-build.sh deleted file mode 100755 index e33328a36a..0000000000 --- a/deployment/old/linux-x64/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/linux-x64/package.sh b/deployment/old/linux-x64/package.sh deleted file mode 100755 index dfe8a9aa4a..0000000000 --- a/deployment/old/linux-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/macos/Dockerfile b/deployment/old/macos/Dockerfile deleted file mode 100644 index b522df8848..0000000000 --- a/deployment/old/macos/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/macos -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/macos/clean.sh b/deployment/old/macos/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/macos/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/macos/dependencies.txt b/deployment/old/macos/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/macos/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/macos/docker-build.sh b/deployment/old/macos/docker-build.sh deleted file mode 100755 index f9417388d7..0000000000 --- a/deployment/old/macos/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/macos/package.sh b/deployment/old/macos/package.sh deleted file mode 100755 index 464c0d382f..0000000000 --- a/deployment/old/macos/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-macos-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/portable/Dockerfile b/deployment/old/portable/Dockerfile deleted file mode 100644 index 965eb82b86..0000000000 --- a/deployment/old/portable/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/portable -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/portable/clean.sh b/deployment/old/portable/clean.sh deleted file mode 100755 index c07501a7bb..0000000000 --- a/deployment/old/portable/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-linux-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/portable/dependencies.txt b/deployment/old/portable/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/portable/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/portable/docker-build.sh b/deployment/old/portable/docker-build.sh deleted file mode 100755 index 094190bbf6..0000000000 --- a/deployment/old/portable/docker-build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Builds the TAR archive inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build archives -dotnet publish Jellyfin.Server --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/portable/package.sh b/deployment/old/portable/package.sh deleted file mode 100755 index 0ceb54dda1..0000000000 --- a/deployment/old/portable/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-portable-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 deleted file mode 100644 index b11994a18a..0000000000 --- a/deployment/old/ubuntu-package-arm64/Dockerfile.amd64 +++ /dev/null @@ -1,59 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Prepare the cross-toolchain -RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && dpkg --add-architecture arm64 \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -y gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64 - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 deleted file mode 100644 index 8f004b2f1a..0000000000 --- a/deployment/old/ubuntu-package-arm64/Dockerfile.arm64 +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=arm64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-arm64/clean.sh b/deployment/old/ubuntu-package-arm64/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-arm64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-arm64/dependencies.txt b/deployment/old/ubuntu-package-arm64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-arm64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-arm64/docker-build.sh b/deployment/old/ubuntu-package-arm64/docker-build.sh deleted file mode 100755 index 67ab6bd74b..0000000000 --- a/deployment/old/ubuntu-package-arm64/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarm64 - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-arm64/package.sh b/deployment/old/ubuntu-package-arm64/package.sh deleted file mode 100755 index d1140a7274..0000000000 --- a/deployment/old/ubuntu-package-arm64/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu_arm64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.arm64" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-arm64/pkg-src b/deployment/old/ubuntu-package-arm64/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/ubuntu-package-arm64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 deleted file mode 100644 index e475b14389..0000000000 --- a/deployment/old/ubuntu-package-armhf/Dockerfile.amd64 +++ /dev/null @@ -1,59 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Prepare the cross-toolchain -RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && dpkg --add-architecture armhf \ - && apt-get update \ - && apt-get install -y cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -y gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/Dockerfile.armhf b/deployment/old/ubuntu-package-armhf/Dockerfile.armhf deleted file mode 100644 index 0e71fa6938..0000000000 --- a/deployment/old/ubuntu-package-armhf/Dockerfile.armhf +++ /dev/null @@ -1,40 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=armhf - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev liblttng-ust0 libssl-dev - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -# Link to Debian source dir; mkdir needed or it fails, can't force dest -RUN mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-armhf/clean.sh b/deployment/old/ubuntu-package-armhf/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-armhf/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-armhf/dependencies.txt b/deployment/old/ubuntu-package-armhf/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-armhf/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-armhf/docker-build.sh b/deployment/old/ubuntu-package-armhf/docker-build.sh deleted file mode 100755 index 1bd7fb2911..0000000000 --- a/deployment/old/ubuntu-package-armhf/docker-build.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} -dpkg-buildpackage -us -uc -aarmhf - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-armhf/package.sh b/deployment/old/ubuntu-package-armhf/package.sh deleted file mode 100755 index 2ceb3e8165..0000000000 --- a/deployment/old/ubuntu-package-armhf/package.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -ARCH="$( arch )" -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu_armhf-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Determine which Dockerfile to use -case $ARCH in - 'x86_64') - DOCKERFILE="Dockerfile.amd64" - ;; - 'armv7l') - DOCKERFILE="Dockerfile.armhf" - ;; -esac - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./${DOCKERFILE} -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-armhf/pkg-src b/deployment/old/ubuntu-package-armhf/pkg-src deleted file mode 120000 index 4c695fea17..0000000000 --- a/deployment/old/ubuntu-package-armhf/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src \ No newline at end of file diff --git a/deployment/old/ubuntu-package-x64/Dockerfile b/deployment/old/ubuntu-package-x64/Dockerfile deleted file mode 100644 index e2dda6392c..0000000000 --- a/deployment/old/ubuntu-package-x64/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM ubuntu:bionic -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Ubuntu build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev liblttng-ust0 \ - && ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh \ - && mkdir -p ${SOURCE_DIR} && ln -sf ${PLATFORM_DIR}/pkg-src ${SOURCE_DIR}/debian - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install npm package manager -RUN wget -q -O- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ - && echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" > /etc/apt/sources.list.d/npm.list \ - && apt update \ - && apt install -y nodejs - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/ubuntu-package-x64/clean.sh b/deployment/old/ubuntu-package-x64/clean.sh deleted file mode 100755 index 82d427f9e5..0000000000 --- a/deployment/old/ubuntu-package-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/ubuntu-package-x64/dependencies.txt b/deployment/old/ubuntu-package-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/ubuntu-package-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/ubuntu-package-x64/docker-build.sh b/deployment/old/ubuntu-package-x64/docker-build.sh deleted file mode 100755 index 962a522ebc..0000000000 --- a/deployment/old/ubuntu-package-x64/docker-build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Builds the DEB inside the Docker container - -set -o errexit -set -o xtrace - -# Move to source directory -pushd ${SOURCE_DIR} - -# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image -sed -i '/dotnet-sdk-3.1,/d' debian/control - -# Build DEB -dpkg-buildpackage -us -uc - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/deb -mv /jellyfin[-_]* ${ARTIFACT_DIR}/deb/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/ubuntu-package-x64/package.sh b/deployment/old/ubuntu-package-x64/package.sh deleted file mode 100755 index 08c003778b..0000000000 --- a/deployment/old/ubuntu-package-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-ubuntu-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/deb/* "${output_dir}" diff --git a/deployment/old/ubuntu-package-x64/pkg-src b/deployment/old/ubuntu-package-x64/pkg-src deleted file mode 120000 index 0bb6d55249..0000000000 --- a/deployment/old/ubuntu-package-x64/pkg-src +++ /dev/null @@ -1 +0,0 @@ -../debian-package-x64/pkg-src/ \ No newline at end of file diff --git a/deployment/old/unraid/docker-templates/README.md b/deployment/old/unraid/docker-templates/README.md deleted file mode 100644 index 2c268e8b3e..0000000000 --- a/deployment/old/unraid/docker-templates/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# docker-templates - -### Installation: - -Open unRaid GUI (at least unRaid 6.5) - -Click on the Docker tab - -Add the following line under "Template Repositories" - -https://github.com/jellyfin/jellyfin/blob/master/deployment/unraid/docker-templates - -Click save than click on Add Container and select jellyfin. - -Adjust to your paths to your liking and off you go! diff --git a/deployment/old/unraid/docker-templates/jellyfin.xml b/deployment/old/unraid/docker-templates/jellyfin.xml deleted file mode 100644 index 57b4cc5ae1..0000000000 --- a/deployment/old/unraid/docker-templates/jellyfin.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - https://raw.githubusercontent.com/jellyfin/jellyfin/deployment/unraid/docker-templates/jellyfin.xml - False - MediaApp:Video MediaApp:Music MediaApp:Photos MediaServer:Video MediaServer:Music MediaServer:Photos - Jellyfin - - Jellyfin is The Free Software Media Browser Converted By Community Applications Always verify this template (and values) against the dockerhub support page for the container!![br][br] - You can add as many mount points as needed for recordings, movies ,etc. [br][br] - [b][span style='color: #E80000;']Directions:[/span][/b][br] - [b]/config[/b] : This is where Jellyfin will store it's databases and configuration.[br][br] - [b]Port[/b] : This is the default port for Jellyfin. (Will add ssl port later)[br][br] - [b]Media[/b] : This is the mounting point of your media. When you access it in Jellyfin it will be /media or whatever you chose for a mount point[br][br] - [b]Cache[/b] : This is where Jellyfin will store and manage cached files like images to serve to clients. This is not where all images are stored.[br][br] - [b]Tip:[/b] You can add more volume mappings if you wish Jellyfin has access to it. - - - Jellyfin Server is a home media server built on top of other popular open source technologies such as Service Stack, jQuery, jQuery mobile, and Mono and will remain completely free! - - https://www.reddit.com/r/jellyfin/ - https://hub.docker.com/r/jellyfin/jellyfin/ - https://github.com/jellyfin/jellyfin/> - jellyfin/jellyfin - https://jellyfin.media/ - true - false - - host - - - 8096 - 8096 - tcp - - - - - - /mnt/user/appdata/Jellyfin - /config - rw - - - /mnt/user - /media - rw - - - /mnt/user/appdata/Jellyfin/cache/ - /cache - rw - - - http://[IP]:[PORT:8096]/ - https://raw.githubusercontent.com/binhex/docker-templates/master/binhex/images/jellyfin-icon.png - - diff --git a/deployment/old/win-x64/Dockerfile b/deployment/old/win-x64/Dockerfile deleted file mode 100644 index 8a33749541..0000000000 --- a/deployment/old/win-x64/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/win-x64 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x64/clean.sh b/deployment/old/win-x64/clean.sh deleted file mode 100755 index 6c183f3371..0000000000 --- a/deployment/old/win-x64/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x64-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/win-x64/dependencies.txt b/deployment/old/win-x64/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/win-x64/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/win-x64/docker-build.sh b/deployment/old/win-x64/docker-build.sh deleted file mode 100755 index 79e5fb0bcd..0000000000 --- a/deployment/old/win-x64/docker-build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Builds the ZIP archive inside the Docker container - -set -o errexit -set -o xtrace - -# Version variables -NSSM_VERSION="nssm-2.24-101-g897c7ad" -NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.2.1-win64-static" -FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip" - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" - -# Prepare addins -addin_build_dir="$( mktemp -d )" -wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip -wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip -unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe -unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe -rm -rf ${addin_build_dir} - -# Prepare scripts -cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 -cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat - -# Create zip package -pushd /dist -zip -r /jellyfin_${version}.portable.zip jellyfin_${version} -popd -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x64/package.sh b/deployment/old/win-x64/package.sh deleted file mode 100755 index a8ab190fa5..0000000000 --- a/deployment/old/win-x64/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x64-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" diff --git a/deployment/old/win-x86/Dockerfile b/deployment/old/win-x86/Dockerfile deleted file mode 100644 index f8dc5be83d..0000000000 --- a/deployment/old/win-x86/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -FROM debian:10 -# Docker build arguments -ARG SOURCE_DIR=/jellyfin -ARG PLATFORM_DIR=/jellyfin/deployment/win-x86 -ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.1 -# Docker run environment -ENV SOURCE_DIR=/jellyfin -ENV ARTIFACT_DIR=/dist -ENV DEB_BUILD_OPTIONS=noddebs -ENV ARCH=amd64 - -# Prepare Debian build environment -RUN apt-get update \ - && apt-get install -y apt-transport-https debhelper gnupg wget devscripts mmv libc6-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip - -# Install dotnet repository -# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - -# Install yarn package manager -RUN wget -q -O- https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ - && apt update \ - && apt install -y yarn - -# Link to docker-build script -RUN ln -sf ${PLATFORM_DIR}/docker-build.sh /docker-build.sh - -VOLUME ${ARTIFACT_DIR}/ - -COPY . ${SOURCE_DIR}/ - -ENTRYPOINT ["/docker-build.sh"] diff --git a/deployment/old/win-x86/clean.sh b/deployment/old/win-x86/clean.sh deleted file mode 100755 index 8b78c5e4b6..0000000000 --- a/deployment/old/win-x86/clean.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -keep_artifacts="${1}" - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x86-build" - -rm -rf "${package_temporary_dir}" &>/dev/null \ - || sudo rm -rf "${package_temporary_dir}" &>/dev/null - -rm -rf "${output_dir}" &>/dev/null \ - || sudo rm -rf "${output_dir}" &>/dev/null - -if [[ ${keep_artifacts} == 'n' ]]; then - docker_sudo="" - if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo=sudo - fi - ${docker_sudo} docker image rm ${image_name} --force -fi diff --git a/deployment/old/win-x86/dependencies.txt b/deployment/old/win-x86/dependencies.txt deleted file mode 100644 index bdb9670965..0000000000 --- a/deployment/old/win-x86/dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -docker diff --git a/deployment/old/win-x86/docker-build.sh b/deployment/old/win-x86/docker-build.sh deleted file mode 100755 index 977dcf78fa..0000000000 --- a/deployment/old/win-x86/docker-build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -# Builds the ZIP archive inside the Docker container - -set -o errexit -set -o xtrace - -# Version variables -NSSM_VERSION="nssm-2.24-101-g897c7ad" -NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.2.1-win32-static" -FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip" - -# Move to source directory -pushd ${SOURCE_DIR} - -# Clone down and build Web frontend -web_build_dir="$( mktemp -d )" -web_target="${SOURCE_DIR}/MediaBrowser.WebDashboard/jellyfin-web" -git clone https://github.com/jellyfin/jellyfin-web.git ${web_build_dir}/ -pushd ${web_build_dir} -if [[ -n ${web_branch} ]]; then - checkout -b origin/${web_branch} -fi -yarn install -mkdir -p ${web_target} -mv dist/* ${web_target}/ -popd -rm -rf ${web_build_dir} - -# Get version -version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" - -# Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" - -# Prepare addins -addin_build_dir="$( mktemp -d )" -wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip -wget ${FFMPEG_URL} -O ${addin_build_dir}/ffmpeg.zip -unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe /dist/jellyfin_${version}/nssm.exe -unzip ${addin_build_dir}/ffmpeg.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffmpeg.exe /dist/jellyfin_${version}/ffmpeg.exe -cp ${addin_build_dir}/${FFMPEG_VERSION}/bin/ffprobe.exe /dist/jellyfin_${version}/ffprobe.exe -rm -rf ${addin_build_dir} - -# Prepare scripts -cp ${SOURCE_DIR}/deployment/windows/legacy/install-jellyfin.ps1 /dist/jellyfin_${version}/install-jellyfin.ps1 -cp ${SOURCE_DIR}/deployment/windows/legacy/install.bat /dist/jellyfin_${version}/install.bat - -# Create zip package -pushd /dist -zip -r /jellyfin_${version}.portable.zip jellyfin_${version} -popd -rm -rf /dist/jellyfin_${version} - -# Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv /jellyfin[-_]*.zip ${ARTIFACT_DIR}/ -chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} diff --git a/deployment/old/win-x86/package.sh b/deployment/old/win-x86/package.sh deleted file mode 100755 index 65e7e2928c..0000000000 --- a/deployment/old/win-x86/package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -args="${@}" -declare -a docker_envvars -for arg in ${args}; do - docker_envvars+=("-e ${arg}") -done - -WORKDIR="$( pwd )" - -package_temporary_dir="${WORKDIR}/pkg-dist-tmp" -output_dir="${WORKDIR}/pkg-dist" -current_user="$( whoami )" -image_name="jellyfin-windows-x86-build" - -# Determine if sudo should be used for Docker -if [[ ! -z $(id -Gn | grep -q 'docker') ]] \ - && [[ ! ${EUID:-1000} -eq 0 ]] \ - && [[ ! ${USER} == "root" ]] \ - && [[ ! -z $( echo "${OSTYPE}" | grep -q "darwin" ) ]]; then - docker_sudo="sudo" -else - docker_sudo="" -fi - -# Prepare temporary package dir -mkdir -p "${package_temporary_dir}" -# Set up the build environment Docker image -${docker_sudo} docker build ../.. -t "${image_name}" -f ./Dockerfile -# Build the DEBs and copy out to ${package_temporary_dir} -${docker_sudo} docker run --rm -v "${package_temporary_dir}:/dist" "${image_name}" ${docker_envvars} -# Move the DEBs to the output directory -mkdir -p "${output_dir}" -mv "${package_temporary_dir}"/* "${output_dir}" From 762a46045ee784c500f7fe4c2c0131ca83e8b50e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:55:14 -0400 Subject: [PATCH 130/411] Update gitignore paths for debian/ --- .gitignore | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 17cf4a6277..f3ad8c09bd 100644 --- a/.gitignore +++ b/.gitignore @@ -244,14 +244,14 @@ pip-log.txt ######################### # Artifacts for debian-x64 -deployment/debian-package-x64/pkg-src/.debhelper/ -deployment/debian-package-x64/pkg-src/*.debhelper -deployment/debian-package-x64/pkg-src/debhelper-build-stamp -deployment/debian-package-x64/pkg-src/files -deployment/debian-package-x64/pkg-src/jellyfin.substvars -deployment/debian-package-x64/pkg-src/jellyfin/ +debian/.debhelper/ +debian/*.debhelper +debian/debhelper-build-stamp +debian/files +debian/jellyfin.substvars +debian/jellyfin/ # Don't ignore the debian/bin folder -!deployment/debian-package-x64/pkg-src/bin/ +!debian/bin/ deployment/**/dist/ deployment/**/pkg-dist/ From 7eac3684862380a857edc6c7e98c20f25c5b9a03 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Thu, 9 Apr 2020 11:55:27 -0400 Subject: [PATCH 131/411] Fix missing restart script --- debian/bin/restart.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 debian/bin/restart.sh diff --git a/debian/bin/restart.sh b/debian/bin/restart.sh new file mode 100755 index 0000000000..9b64b6d728 --- /dev/null +++ b/debian/bin/restart.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# restart.sh - Jellyfin server restart script +# Part of the Jellyfin project (https://github.com/jellyfin) +# +# This script restarts the Jellyfin daemon on Linux when using +# the Restart button on the admin dashboard. It supports the +# systemctl, service, and traditional /etc/init.d (sysv) restart +# methods, chosen automatically by which one is found first (in +# that order). +# +# This script is used by the Debian/Ubuntu/Fedora/CentOS packages. + +get_service_command() { + for command in systemctl service; do + if which $command &>/dev/null; then + echo $command && return + fi + done + echo "sysv" +} + +cmd="$( get_service_command )" +echo "Detected service control platform '$cmd'; using it to restart Jellyfin..." +case $cmd in + 'systemctl') + echo "sleep 2; /usr/bin/sudo $( which systemctl ) restart jellyfin" | at now + ;; + 'service') + echo "sleep 2; /usr/bin/sudo $( which service ) jellyfin restart" | at now + ;; + 'sysv') + echo "sleep 2; /usr/bin/sudo /etc/init.d/jellyfin restart" | at now + ;; +esac +exit 0 From 4d949cb38b531455bc42551a7c095a0c2a00170e Mon Sep 17 00:00:00 2001 From: hauntingEcho <1661988+hauntingEcho@users.noreply.github.com> Date: Tue, 31 Mar 2020 21:41:10 -0500 Subject: [PATCH 132/411] Specify a version for jellyfin-ffmpeg dependency in .deb Lower versions cause #2126 in Jellyfin >= 10.4.3 --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 5559700cf5..896d8286b7 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,7 @@ Conflicts: mediabrowser, emby, emby-server-beta, jellyfin-dev, emby-server Architecture: any Depends: at, libsqlite3-0, - jellyfin-ffmpeg, + jellyfin-ffmpeg (>= 4.2.1-2), libfontconfig1, libfreetype6, libssl1.1 From 4e894b4b660f33e8c8a6ce9b235edaaf313b9c9e Mon Sep 17 00:00:00 2001 From: ferferga Date: Thu, 9 Apr 2020 18:23:21 +0200 Subject: [PATCH 133/411] Remove unnecessary space in hardware decoders argument for ffmpeg --- .../MediaEncoding/EncodingHelper.cs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 95b7df9bd6..3fb8c825a6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2549,7 +2549,7 @@ namespace MediaBrowser.Controller.MediaEncoding encodingOptions.HardwareDecodingCodecs = Array.Empty(); return null; } - return "-c:v h264_qsv "; + return "-c:v h264_qsv"; } break; case "hevc": @@ -2557,19 +2557,19 @@ namespace MediaBrowser.Controller.MediaEncoding if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { //return "-c:v hevc_qsv -load_plugin hevc_hw "; - return "-c:v hevc_qsv "; + return "-c:v hevc_qsv"; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_qsv "; + return "-c:v mpeg2_qsv"; } break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vc1_qsv "; + return "-c:v vc1_qsv"; } break; } @@ -2589,32 +2589,32 @@ namespace MediaBrowser.Controller.MediaEncoding encodingOptions.HardwareDecodingCodecs = Array.Empty(); return null; } - return "-c:v h264_cuvid "; + return "-c:v h264_cuvid"; } break; case "hevc": case "h265": if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_cuvid "; + return "-c:v hevc_cuvid"; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_cuvid "; + return "-c:v mpeg2_cuvid"; } break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vc1_cuvid "; + return "-c:v vc1_cuvid"; } break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg4_cuvid "; + return "-c:v mpeg4_cuvid"; } break; } @@ -2628,38 +2628,38 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - return "-c:v h264_mediacodec "; + return "-c:v h264_mediacodec"; } break; case "hevc": case "h265": if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_mediacodec "; + return "-c:v hevc_mediacodec"; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_mediacodec "; + return "-c:v mpeg2_mediacodec"; } break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg4_mediacodec "; + return "-c:v mpeg4_mediacodec"; } break; case "vp8": if (_mediaEncoder.SupportsDecoder("vp8_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vp8_mediacodec "; + return "-c:v vp8_mediacodec"; } break; case "vp9": if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vp9_mediacodec "; + return "-c:v vp9_mediacodec"; } break; } @@ -2673,25 +2673,25 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - return "-c:v h264_mmal "; + return "-c:v h264_mmal"; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_mmal "; + return "-c:v mpeg2_mmal"; } break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg4_mmal "; + return "-c:v mpeg4_mmal"; } break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vc1_mmal "; + return "-c:v vc1_mmal"; } break; } From 49fe5e0a21907797248daada0a0446b37bb304ba Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Apr 2020 12:03:10 +0200 Subject: [PATCH 134/411] Fix some warnings --- .../Activity/ActivityLogEntryPoint.cs | 38 ++-- .../Activity/ActivityManager.cs | 11 +- .../Activity/ActivityRepository.cs | 104 +++++------ .../ApplicationHost.cs | 2 +- .../Archiving/ZipClient.cs | 3 + .../Channels/ChannelImageProvider.cs | 4 +- .../Channels/ChannelManager.cs | 168 +++++++++--------- .../Channels/ChannelPostScanTask.cs | 4 +- .../Channels/RefreshChannelsScheduledTask.cs | 8 +- .../Collections/CollectionImageProvider.cs | 4 +- .../Collections/CollectionManager.cs | 20 ++- .../LiveTv/LiveTvDtoService.cs | 25 ++- .../LiveTv/LiveTvManager.cs | 24 ++- .../IDisplayPreferencesRepository.cs | 22 ++- .../LiveTv/SeriesTimerInfoDto.cs | 2 +- 15 files changed, 231 insertions(+), 208 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index d900520b2a..9f1d5096d3 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -27,6 +25,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { + /// + /// Entry point for the activity logger. + /// public sealed class ActivityLogEntryPoint : IServerEntryPoint { private readonly ILogger _logger; @@ -42,16 +43,15 @@ namespace Emby.Server.Implementations.Activity /// /// Initializes a new instance of the class. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// + /// The logger. + /// The session manager. + /// The device manager. + /// The task manager. + /// The activity manager. + /// The localization manager. + /// The installation manager. + /// The subtitle manager. + /// The user manager. public ActivityLogEntryPoint( ILogger logger, ISessionManager sessionManager, @@ -74,6 +74,7 @@ namespace Emby.Server.Implementations.Activity _userManager = userManager; } + /// public Task RunAsync() { _taskManager.TaskCompleted += OnTaskCompleted; @@ -168,7 +169,12 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), + Name = string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), + user.Name, + GetItemName(item), + e.DeviceName), Type = GetPlaybackStoppedNotificationType(item.MediaType), UserId = user.Id }); @@ -485,8 +491,8 @@ namespace Emby.Server.Implementations.Activity var result = e.Result; var task = e.Task; - var activityTask = task.ScheduledTask as IConfigurableScheduledTask; - if (activityTask != null && !activityTask.IsLogged) + if (task.ScheduledTask is IConfigurableScheduledTask activityTask + && !activityTask.IsLogged) { return; } @@ -560,7 +566,7 @@ namespace Emby.Server.Implementations.Activity /// /// Constructs a user-friendly string for this TimeSpan instance. /// - public static string ToUserFriendlyString(TimeSpan span) + private static string ToUserFriendlyString(TimeSpan span) { const int DaysInYear = 365; const int DaysInMonth = 30; diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index ee10845cfa..c1d8dd8da8 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -11,22 +11,17 @@ namespace Emby.Server.Implementations.Activity { public class ActivityManager : IActivityManager { - public event EventHandler> EntryCreated; - private readonly IActivityRepository _repo; - private readonly ILogger _logger; private readonly IUserManager _userManager; - public ActivityManager( - ILoggerFactory loggerFactory, - IActivityRepository repo, - IUserManager userManager) + public ActivityManager(IActivityRepository repo, IUserManager userManager) { - _logger = loggerFactory.CreateLogger(nameof(ActivityManager)); _repo = repo; _userManager = userManager; } + public event EventHandler> EntryCreated; + public void Create(ActivityLogEntry entry) { entry.Date = DateTime.UtcNow; diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 7be72319ea..26fc229f73 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -17,11 +17,12 @@ namespace Emby.Server.Implementations.Activity { public class ActivityRepository : BaseSqliteRepository, IActivityRepository { - private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); + private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; + private readonly IFileSystem _fileSystem; - public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) - : base(loggerFactory.CreateLogger(nameof(ActivityRepository))) + public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) + : base(logger) { DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); _fileSystem = fileSystem; @@ -76,8 +77,6 @@ namespace Emby.Server.Implementations.Activity } } - private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; - public void Create(ActivityLogEntry entry) { if (entry == null) @@ -87,32 +86,34 @@ namespace Emby.Server.Implementations.Activity 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)")) + connection.RunInTransaction( + db => { - statement.TryBind("@Name", entry.Name); + 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); + 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)); - } + 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.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); + statement.TryBind("@LogSeverity", entry.Severity.ToString()); - statement.MoveNext(); - } - }, TransactionMode); + statement.MoveNext(); + } + }, + TransactionMode); } } @@ -125,33 +126,35 @@ namespace Emby.Server.Implementations.Activity 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")) + connection.RunInTransaction( + db => { - statement.TryBind("@Id", entry.Id); + 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); + 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)); - } + 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.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); + statement.TryBind("@LogSeverity", entry.Severity.ToString()); - statement.MoveNext(); - } - }, TransactionMode); + statement.MoveNext(); + } + }, + TransactionMode); } } @@ -164,6 +167,7 @@ namespace Emby.Server.Implementations.Activity { whereClauses.Add("DateCreated>=@DateCreated"); } + if (hasUserId.HasValue) { if (hasUserId.Value) @@ -204,7 +208,7 @@ namespace Emby.Server.Implementations.Activity if (limit.HasValue) { - commandText += " LIMIT " + limit.Value.ToString(_usCulture); + commandText += " LIMIT " + limit.Value.ToString(CultureInfo.InvariantCulture); } var statementTexts = new[] @@ -304,7 +308,7 @@ namespace Emby.Server.Implementations.Activity index++; if (reader[index].SQLiteType != SQLiteType.Null) { - info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true); + 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 49d3b4d6ae..76416392f1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -841,7 +841,7 @@ namespace Emby.Server.Implementations var activityLogRepo = GetActivityLogRepository(); serviceCollection.AddSingleton(activityLogRepo); - serviceCollection.AddSingleton(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); + serviceCollection.AddSingleton(new ActivityManager(activityLogRepo, UserManager)); var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton(authContext); diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs index 4a6e5cfd75..e01495e192 100644 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ b/Emby.Server.Implementations/Archiving/ZipClient.cs @@ -50,6 +50,7 @@ namespace Emby.Server.Implementations.Archiving } } + /// public void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles) { using (var reader = ZipReader.Open(source)) @@ -66,6 +67,7 @@ namespace Emby.Server.Implementations.Archiving } } + /// public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles) { using (var reader = GZipReader.Open(source)) @@ -82,6 +84,7 @@ namespace Emby.Server.Implementations.Archiving } } + /// public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName) { using (var reader = GZipReader.Open(source)) diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs index 62aeb9bcb9..cf56a5faef 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs @@ -20,6 +20,8 @@ namespace Emby.Server.Implementations.Channels _channelManager = channelManager; } + public string Name => "Channel Image Provider"; + public IEnumerable GetSupportedImages(BaseItem item) { return GetChannel(item).GetSupportedChannelImages(); @@ -32,8 +34,6 @@ namespace Emby.Server.Implementations.Channels return channel.GetChannelImage(type, cancellationToken); } - public string Name => "Channel Image Provider"; - public bool Supports(BaseItem item) { return item is Channel; diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 6e1baddfed..8502af97a9 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -31,8 +31,6 @@ namespace Emby.Server.Implementations.Channels { public class ChannelManager : IChannelManager { - internal IChannel[] Channels { get; private set; } - private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; private readonly IDtoService _dtoService; @@ -43,11 +41,16 @@ namespace Emby.Server.Implementations.Channels private readonly IJsonSerializer _jsonSerializer; private readonly IProviderManager _providerManager; + private readonly ConcurrentDictionary>> _channelItemMediaInfo = + new ConcurrentDictionary>>(); + + private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); + public ChannelManager( IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, - ILoggerFactory loggerFactory, + ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, @@ -57,7 +60,7 @@ namespace Emby.Server.Implementations.Channels _userManager = userManager; _dtoService = dtoService; _libraryManager = libraryManager; - _logger = loggerFactory.CreateLogger(nameof(ChannelManager)); + _logger = logger; _config = config; _fileSystem = fileSystem; _userDataManager = userDataManager; @@ -65,6 +68,8 @@ namespace Emby.Server.Implementations.Channels _providerManager = providerManager; } + internal IChannel[] Channels { get; private set; } + private static TimeSpan CacheLength => TimeSpan.FromHours(3); public void AddParts(IEnumerable channels) @@ -85,8 +90,7 @@ namespace Emby.Server.Implementations.Channels var internalChannel = _libraryManager.GetItemById(item.ChannelId); var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); - var supportsDelete = channel as ISupportsDelete; - return supportsDelete != null && supportsDelete.CanDelete(item); + return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item); } public bool EnableMediaProbe(BaseItem item) @@ -146,15 +150,13 @@ namespace Emby.Server.Implementations.Channels { try { - var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes; - - return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; + return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes + && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; } catch { return false; } - }).ToList(); } @@ -171,7 +173,6 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } @@ -188,9 +189,9 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } + if (query.IsFavorite.HasValue) { var val = query.IsFavorite.Value; @@ -215,7 +216,6 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } @@ -226,6 +226,7 @@ namespace Emby.Server.Implementations.Channels { all = all.Skip(query.StartIndex.Value).ToList(); } + if (query.Limit.HasValue) { all = all.Take(query.Limit.Value).ToList(); @@ -256,11 +257,9 @@ namespace Emby.Server.Implementations.Channels var internalResult = GetChannelsInternal(query); - var dtoOptions = new DtoOptions() - { - }; + var dtoOptions = new DtoOptions(); - //TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues. + // TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues. var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user); var result = new QueryResult @@ -341,8 +340,8 @@ namespace Emby.Server.Implementations.Channels } catch { - } + return; } @@ -365,11 +364,9 @@ namespace Emby.Server.Implementations.Channels var channel = GetChannel(item.ChannelId); var channelPlugin = GetChannelProvider(channel); - var requiresCallback = channelPlugin as IRequiresMediaInfoCallback; - IEnumerable results; - if (requiresCallback != null) + if (channelPlugin is IRequiresMediaInfoCallback requiresCallback) { results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) .ConfigureAwait(false); @@ -384,9 +381,6 @@ namespace Emby.Server.Implementations.Channels .ToList(); } - private readonly ConcurrentDictionary>> _channelItemMediaInfo = - new ConcurrentDictionary>>(); - private async Task> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken) { if (_channelItemMediaInfo.TryGetValue(id, out Tuple> cachedInfo)) @@ -444,18 +438,21 @@ namespace Emby.Server.Implementations.Channels { isNew = true; } + item.Path = path; if (!item.ChannelId.Equals(id)) { forceUpdate = true; } + item.ChannelId = id; if (item.ParentId != parentFolderId) { forceUpdate = true; } + item.ParentId = parentFolderId; item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating); @@ -472,10 +469,12 @@ namespace Emby.Server.Implementations.Channels _libraryManager.CreateItem(item, null); } - await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - ForceSave = !isNew && forceUpdate - }, cancellationToken).ConfigureAwait(false); + await item.RefreshMetadata( + new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + ForceSave = !isNew && forceUpdate + }, + cancellationToken).ConfigureAwait(false); return item; } @@ -509,12 +508,12 @@ namespace Emby.Server.Implementations.Channels public ChannelFeatures[] GetAllChannelFeatures() { - return _libraryManager.GetItemIds(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Channel).Name }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) } - - }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray(); + return _libraryManager.GetItemIds( + new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(Channel).Name }, + OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) } + }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray(); } public ChannelFeatures GetChannelFeatures(string id) @@ -532,13 +531,13 @@ namespace Emby.Server.Implementations.Channels public bool SupportsExternalTransfer(Guid channelId) { - //var channel = GetChannel(channelId); var channelProvider = GetChannelProvider(channelId); return channelProvider.GetChannelFeatures().SupportsContentDownloading; } - public ChannelFeatures GetChannelFeaturesDto(Channel channel, + public ChannelFeatures GetChannelFeaturesDto( + Channel channel, IChannel provider, InternalChannelFeatures features) { @@ -567,6 +566,7 @@ namespace Emby.Server.Implementations.Channels { throw new ArgumentNullException(nameof(name)); } + return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel)); } @@ -614,7 +614,7 @@ namespace Emby.Server.Implementations.Channels query.IsFolder = false; // hack for trailers, figure out a better way later - var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.IndexOf("Trailer") != -1; + var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.Contains("Trailer", StringComparison.Ordinal); if (sortByPremiereDate) { @@ -640,10 +640,12 @@ namespace Emby.Server.Implementations.Channels { var internalChannel = await GetChannel(channel, cancellationToken).ConfigureAwait(false); - var query = new InternalItemsQuery(); - query.Parent = internalChannel; - query.EnableTotalRecordCount = false; - query.ChannelIds = new Guid[] { internalChannel.Id }; + var query = new InternalItemsQuery + { + Parent = internalChannel, + EnableTotalRecordCount = false, + ChannelIds = new Guid[] { internalChannel.Id } + }; var result = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); @@ -651,13 +653,15 @@ namespace Emby.Server.Implementations.Channels { if (item is Folder folder) { - await GetChannelItemsInternal(new InternalItemsQuery - { - Parent = folder, - EnableTotalRecordCount = false, - ChannelIds = new Guid[] { internalChannel.Id } - - }, new SimpleProgress(), cancellationToken).ConfigureAwait(false); + await GetChannelItemsInternal( + new InternalItemsQuery + { + Parent = folder, + EnableTotalRecordCount = false, + ChannelIds = new Guid[] { internalChannel.Id } + }, + new SimpleProgress(), + cancellationToken).ConfigureAwait(false); } } } @@ -672,7 +676,8 @@ namespace Emby.Server.Implementations.Channels var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId); - var itemsResult = await GetChannelItems(channelProvider, + var itemsResult = await GetChannelItems( + channelProvider, query.User, parentItem is Channel ? null : parentItem.ExternalId, null, @@ -684,13 +689,12 @@ namespace Emby.Server.Implementations.Channels { query.Parent = channel; } + query.ChannelIds = Array.Empty(); // Not yet sure why this is causing a problem query.GroupByPresentationUniqueKey = false; - //_logger.LogDebug("GetChannelItemsInternal"); - // null if came from cache if (itemsResult != null) { @@ -707,12 +711,15 @@ namespace Emby.Server.Implementations.Channels var deadItem = _libraryManager.GetItemById(deadId); if (deadItem != null) { - _libraryManager.DeleteItem(deadItem, new DeleteOptions - { - DeleteFileLocation = false, - DeleteFromExternalProvider = false - - }, parentItem, false); + _libraryManager.DeleteItem( + deadItem, + new DeleteOptions + { + DeleteFileLocation = false, + DeleteFromExternalProvider = false + }, + parentItem, + false); } } } @@ -735,7 +742,6 @@ namespace Emby.Server.Implementations.Channels return result; } - private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); private async Task GetChannelItems(IChannel channel, User user, string externalFolderId, @@ -743,7 +749,7 @@ namespace Emby.Server.Implementations.Channels bool sortDescending, CancellationToken cancellationToken) { - var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture); + var userId = user?.Id.ToString("N", CultureInfo.InvariantCulture); var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending); @@ -761,11 +767,9 @@ namespace Emby.Server.Implementations.Channels } catch (FileNotFoundException) { - } catch (IOException) { - } await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -785,11 +789,9 @@ namespace Emby.Server.Implementations.Channels } catch (FileNotFoundException) { - } catch (IOException) { - } var query = new InternalChannelItemQuery @@ -833,7 +835,8 @@ namespace Emby.Server.Implementations.Channels } } - private string GetChannelDataCachePath(IChannel channel, + private string GetChannelDataCachePath( + IChannel channel, string userId, string externalFolderId, ChannelItemSortField? sortField, @@ -843,8 +846,7 @@ namespace Emby.Server.Implementations.Channels var userCacheKey = string.Empty; - var hasCacheKey = channel as IHasCacheKey; - if (hasCacheKey != null) + if (channel is IHasCacheKey hasCacheKey) { userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty; } @@ -858,6 +860,7 @@ namespace Emby.Server.Implementations.Channels { filename += "-sortField-" + sortField.Value; } + if (sortDescending) { filename += "-sortDescending"; @@ -865,7 +868,8 @@ namespace Emby.Server.Implementations.Channels filename = filename.GetMD5().ToString("N", CultureInfo.InvariantCulture); - return Path.Combine(_config.ApplicationPaths.CachePath, + return Path.Combine( + _config.ApplicationPaths.CachePath, "channels", channelId, version, @@ -981,7 +985,6 @@ namespace Emby.Server.Implementations.Channels { item.RunTimeTicks = null; } - else if (isNew || !enableMediaProbe) { item.RunTimeTicks = info.RunTimeTicks; @@ -1014,26 +1017,24 @@ namespace Emby.Server.Implementations.Channels } } - var hasArtists = item as IHasArtist; - if (hasArtists != null) + if (item is IHasArtist hasArtists) { hasArtists.Artists = info.Artists.ToArray(); } - var hasAlbumArtists = item as IHasAlbumArtist; - if (hasAlbumArtists != null) + if (item is IHasAlbumArtist hasAlbumArtists) { hasAlbumArtists.AlbumArtists = info.AlbumArtists.ToArray(); } - var trailer = item as Trailer; - if (trailer != null) + if (item is Trailer trailer) { if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes)) { _logger.LogDebug("Forcing update due to TrailerTypes {0}", item.Name); forceUpdate = true; } + trailer.TrailerTypes = info.TrailerTypes.ToArray(); } @@ -1057,6 +1058,7 @@ namespace Emby.Server.Implementations.Channels forceUpdate = true; _logger.LogDebug("Forcing update due to ChannelId {0}", item.Name); } + item.ChannelId = internalChannelId; if (!item.ParentId.Equals(parentFolderId)) @@ -1064,16 +1066,17 @@ namespace Emby.Server.Implementations.Channels forceUpdate = true; _logger.LogDebug("Forcing update due to parent folder Id {0}", item.Name); } + item.ParentId = parentFolderId; - var hasSeries = item as IHasSeries; - if (hasSeries != null) + if (item is IHasSeries hasSeries) { if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase)) { forceUpdate = true; _logger.LogDebug("Forcing update due to SeriesName {0}", item.Name); } + hasSeries.SeriesName = info.SeriesName; } @@ -1082,24 +1085,23 @@ namespace Emby.Server.Implementations.Channels forceUpdate = true; _logger.LogDebug("Forcing update due to ExternalId {0}", item.Name); } + item.ExternalId = info.Id; - var channelAudioItem = item as Audio; - if (channelAudioItem != null) + if (item is Audio channelAudioItem) { channelAudioItem.ExtraType = info.ExtraType; var mediaSource = info.MediaSources.FirstOrDefault(); - item.Path = mediaSource == null ? null : mediaSource.Path; + item.Path = mediaSource?.Path; } - var channelVideoItem = item as Video; - if (channelVideoItem != null) + if (item is Video channelVideoItem) { channelVideoItem.ExtraType = info.ExtraType; var mediaSource = info.MediaSources.FirstOrDefault(); - item.Path = mediaSource == null ? null : mediaSource.Path; + item.Path = mediaSource?.Path; } if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary)) @@ -1156,7 +1158,7 @@ namespace Emby.Server.Implementations.Channels } } - if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime)) + if (isNew || forceUpdate || item.DateLastRefreshed == default) { _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); } diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index 266d539d0a..250e9da649 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -14,14 +14,12 @@ namespace Emby.Server.Implementations.Channels public class ChannelPostScanTask { private readonly IChannelManager _channelManager; - private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager) + public ChannelPostScanTask(IChannelManager channelManager, ILogger logger, ILibraryManager libraryManager) { _channelManager = channelManager; - _userManager = userManager; _logger = logger; _libraryManager = libraryManager; } diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 367efcb134..e1d35f2b0e 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -7,29 +7,26 @@ using System.Threading.Tasks; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.Channels { public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask { private readonly IChannelManager _channelManager; - private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; public RefreshChannelsScheduledTask( IChannelManager channelManager, - IUserManager userManager, ILogger logger, ILibraryManager libraryManager, ILocalizationManager localization) { _channelManager = channelManager; - _userManager = userManager; _logger = logger; _libraryManager = libraryManager; _localization = localization; @@ -63,7 +60,7 @@ namespace Emby.Server.Implementations.Channels await manager.RefreshChannels(new SimpleProgress(), cancellationToken).ConfigureAwait(false); - await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken) + await new ChannelPostScanTask(_channelManager, _logger, _libraryManager).Run(progress, cancellationToken) .ConfigureAwait(false); } @@ -72,7 +69,6 @@ namespace Emby.Server.Implementations.Channels { return new[] { - // Every so often new TaskTriggerInfo { diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 21ba0288ec..320552465e 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -46,9 +46,7 @@ namespace Emby.Server.Implementations.Collections { var subItem = i; - var episode = subItem as Episode; - - if (episode != null) + if (subItem is Episode episode) { var series = episode.Series; if (series != null && series.HasImage(ImageType.Primary)) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 3219528749..68bcc5a4f4 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -52,7 +52,9 @@ namespace Emby.Server.Implementations.Collections } public event EventHandler CollectionCreated; + public event EventHandler ItemsAddedToCollection; + public event EventHandler ItemsRemovedFromCollection; private IEnumerable FindFolders(string path) @@ -109,9 +111,9 @@ namespace Emby.Server.Implementations.Collections { var folder = GetCollectionsFolder(false).Result; - return folder == null ? - new List() : - folder.GetChildren(user, true).OfType(); + return folder == null + ? Enumerable.Empty() + : folder.GetChildren(user, true).OfType(); } public BoxSet CreateCollection(CollectionCreationOptions options) @@ -191,7 +193,6 @@ namespace Emby.Server.Implementations.Collections private void AddToCollection(Guid collectionId, IEnumerable ids, bool fireEvent, MetadataRefreshOptions refreshOptions) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; - if (collection == null) { throw new ArgumentException("No collection exists with the supplied Id"); @@ -289,10 +290,13 @@ namespace Emby.Server.Implementations.Collections } collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); - _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - ForceSave = true - }, RefreshPriority.High); + _providerManager.QueueRefresh( + collection.Id, + new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + ForceSave = true + }, + RefreshPriority.High); ItemsRemovedFromCollection?.Invoke(this, new CollectionModifiedEventArgs { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs index 6e903a18ef..3b36247a9f 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -22,6 +22,10 @@ namespace Emby.Server.Implementations.LiveTv { public class LiveTvDtoService { + private const string InternalVersionNumber = "4"; + + private const string ServiceName = "Emby"; + private readonly ILogger _logger; private readonly IImageProcessor _imageProcessor; @@ -32,13 +36,13 @@ namespace Emby.Server.Implementations.LiveTv public LiveTvDtoService( IDtoService dtoService, IImageProcessor imageProcessor, - ILoggerFactory loggerFactory, + ILogger logger, IApplicationHost appHost, ILibraryManager libraryManager) { _dtoService = dtoService; _imageProcessor = imageProcessor; - _logger = loggerFactory.CreateLogger(nameof(LiveTvDtoService)); + _logger = logger; _appHost = appHost; _libraryManager = libraryManager; } @@ -161,7 +165,6 @@ namespace Emby.Server.Implementations.LiveTv Limit = 1, ImageTypes = new ImageType[] { ImageType.Thumb }, DtoOptions = new DtoOptions(false) - }).FirstOrDefault(); if (librarySeries != null) @@ -179,6 +182,7 @@ namespace Emby.Server.Implementations.LiveTv _logger.LogError(ex, "Error"); } } + image = librarySeries.GetImageInfo(ImageType.Backdrop, 0); if (image != null) { @@ -199,13 +203,12 @@ namespace Emby.Server.Implementations.LiveTv var program = _libraryManager.GetItemList(new InternalItemsQuery { - IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name }, + IncludeItemTypes = new string[] { nameof(LiveTvProgram) }, ExternalSeriesId = programSeriesId, Limit = 1, ImageTypes = new ImageType[] { ImageType.Primary }, DtoOptions = new DtoOptions(false), Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null - }).FirstOrDefault(); if (program != null) @@ -232,9 +235,10 @@ namespace Emby.Server.Implementations.LiveTv try { dto.ParentBackdropImageTags = new string[] - { + { _imageProcessor.GetImageCacheTag(program, image) - }; + }; + dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture); } catch (Exception ex) @@ -255,7 +259,6 @@ namespace Emby.Server.Implementations.LiveTv Limit = 1, ImageTypes = new ImageType[] { ImageType.Thumb }, DtoOptions = new DtoOptions(false) - }).FirstOrDefault(); if (librarySeries != null) @@ -273,6 +276,7 @@ namespace Emby.Server.Implementations.LiveTv _logger.LogError(ex, "Error"); } } + image = librarySeries.GetImageInfo(ImageType.Backdrop, 0); if (image != null) { @@ -298,7 +302,6 @@ namespace Emby.Server.Implementations.LiveTv Limit = 1, ImageTypes = new ImageType[] { ImageType.Primary }, DtoOptions = new DtoOptions(false) - }).FirstOrDefault(); if (program == null) @@ -311,7 +314,6 @@ namespace Emby.Server.Implementations.LiveTv ImageTypes = new ImageType[] { ImageType.Primary }, DtoOptions = new DtoOptions(false), Name = string.IsNullOrEmpty(programSeriesId) ? seriesName : null - }).FirstOrDefault(); } @@ -396,8 +398,6 @@ namespace Emby.Server.Implementations.LiveTv return null; } - private const string InternalVersionNumber = "4"; - public Guid GetInternalChannelId(string serviceName, string externalId) { var name = serviceName + externalId + InternalVersionNumber; @@ -405,7 +405,6 @@ namespace Emby.Server.Implementations.LiveTv return _libraryManager.GetNewItemId(name.ToLowerInvariant(), typeof(LiveTvChannel)); } - private const string ServiceName = "Emby"; public string GetInternalTimerId(string externalId) { var name = ServiceName + externalId + InternalVersionNumber; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index b64fe8634c..bbc064a247 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -41,6 +41,10 @@ namespace Emby.Server.Implementations.LiveTv /// public class LiveTvManager : ILiveTvManager, IDisposable { + private const string ExternalServiceTag = "ExternalServiceId"; + + private const string EtagKey = "ProgramEtag"; + private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly IItemRepository _itemRepo; @@ -91,7 +95,7 @@ namespace Emby.Server.Implementations.LiveTv _userDataManager = userDataManager; _channelManager = channelManager; - _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory, appHost, _libraryManager); + _tvDtoService = new LiveTvDtoService(dtoService, imageProcessor, loggerFactory.CreateLogger(), appHost, _libraryManager); } public event EventHandler> SeriesTimerCancelled; @@ -178,7 +182,6 @@ namespace Emby.Server.Implementations.LiveTv { Name = i.Name, Id = i.Type - }).ToList(); } @@ -261,6 +264,7 @@ namespace Emby.Server.Implementations.LiveTv var endTime = DateTime.UtcNow; _logger.LogInformation("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds); } + info.RequiresClosing = true; var idPrefix = service.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + "_"; @@ -362,30 +366,37 @@ namespace Emby.Server.Implementations.LiveTv { stream.BitRate = null; } + if (stream.Channels.HasValue && stream.Channels <= 0) { stream.Channels = null; } + if (stream.AverageFrameRate.HasValue && stream.AverageFrameRate <= 0) { stream.AverageFrameRate = null; } + if (stream.RealFrameRate.HasValue && stream.RealFrameRate <= 0) { stream.RealFrameRate = null; } + if (stream.Width.HasValue && stream.Width <= 0) { stream.Width = null; } + if (stream.Height.HasValue && stream.Height <= 0) { stream.Height = null; } + if (stream.SampleRate.HasValue && stream.SampleRate <= 0) { stream.SampleRate = null; } + if (stream.Level.HasValue && stream.Level <= 0) { stream.Level = null; @@ -427,7 +438,6 @@ namespace Emby.Server.Implementations.LiveTv } } - private const string ExternalServiceTag = "ExternalServiceId"; private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) { var parentFolderId = parentFolder.Id; @@ -456,6 +466,7 @@ namespace Emby.Server.Implementations.LiveTv { isNew = true; } + item.Tags = channelInfo.Tags; } @@ -463,6 +474,7 @@ namespace Emby.Server.Implementations.LiveTv { isNew = true; } + item.ParentId = parentFolderId; item.ChannelType = channelInfo.ChannelType; @@ -472,24 +484,28 @@ namespace Emby.Server.Implementations.LiveTv { forceUpdate = true; } + item.SetProviderId(ExternalServiceTag, serviceName); if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal)) { forceUpdate = true; } + item.ExternalId = channelInfo.Id; if (!string.Equals(channelInfo.Number, item.Number, StringComparison.Ordinal)) { forceUpdate = true; } + item.Number = channelInfo.Number; if (!string.Equals(channelInfo.Name, item.Name, StringComparison.Ordinal)) { forceUpdate = true; } + item.Name = channelInfo.Name; if (!item.HasImage(ImageType.Primary)) @@ -518,8 +534,6 @@ namespace Emby.Server.Implementations.LiveTv return item; } - private const string EtagKey = "ProgramEtag"; - private Tuple GetProgram(ProgramInfo info, Dictionary allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken) { var id = _tvDtoService.GetInternalProgramId(info.Id); diff --git a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs index 4424e044bb..c2dcb66d7c 100644 --- a/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs +++ b/MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs @@ -6,30 +6,34 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Persistence { /// - /// Interface IDisplayPreferencesRepository + /// Interface IDisplayPreferencesRepository. /// public interface IDisplayPreferencesRepository : IRepository { /// - /// Saves display preferences for an item + /// Saves display preferences for an item. /// /// The display preferences. /// The user id. /// The client. /// The cancellation token. - /// Task. - void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, - CancellationToken cancellationToken); + void SaveDisplayPreferences( + DisplayPreferences displayPreferences, + string userId, + string client, + CancellationToken cancellationToken); /// - /// Saves all display preferences for a user + /// Saves all display preferences for a user. /// /// The display preferences. /// The user id. /// The cancellation token. - /// Task. - void SaveAllDisplayPreferences(IEnumerable displayPreferences, Guid userId, - CancellationToken cancellationToken); + void SaveAllDisplayPreferences( + IEnumerable displayPreferences, + Guid userId, + CancellationToken cancellationToken); + /// /// Gets the display preferences. /// diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs index e30dd84dcd..29f417489a 100644 --- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Model.LiveTv public SeriesTimerInfoDto() { ImageTags = new Dictionary(); - Days = new DayOfWeek[] { }; + Days = Array.Empty(); Type = "SeriesTimer"; } From d8a7462205dbd98e7dec0451a609199ffc3f0cc3 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Apr 2020 12:29:04 +0200 Subject: [PATCH 135/411] DvdLib: remove dependency on MediaBrowser.Model --- DvdLib/BigEndianBinaryReader.cs | 13 ++---- DvdLib/DvdLib.csproj | 4 -- DvdLib/Ifo/Dvd.cs | 10 ++--- .../Library/LibraryManager.cs | 2 +- .../MediaInfo/FFProbeProvider.cs | 6 +-- .../MediaInfo/FFProbeVideoInfo.cs | 45 ++++++++++--------- .../MediaInfo/SubtitleResolver.cs | 7 ++- 7 files changed, 34 insertions(+), 53 deletions(-) diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs index b3b2eabd5c..473005b556 100644 --- a/DvdLib/BigEndianBinaryReader.cs +++ b/DvdLib/BigEndianBinaryReader.cs @@ -1,4 +1,4 @@ -using System; +using System.Buffers.Binary; using System.IO; namespace DvdLib @@ -12,19 +12,12 @@ namespace DvdLib public override ushort ReadUInt16() { - return BitConverter.ToUInt16(ReadAndReverseBytes(2), 0); + return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2)); } public override uint ReadUInt32() { - return BitConverter.ToUInt32(ReadAndReverseBytes(4), 0); - } - - private byte[] ReadAndReverseBytes(int count) - { - byte[] val = base.ReadBytes(count); - Array.Reverse(val, 0, count); - return val; + return BinaryPrimitives.ReadUInt32BigEndian(base.ReadBytes(4)); } } } diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index f4df6a9f52..36f949616a 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -4,10 +4,6 @@ - - - - netstandard2.1 false diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs index c0f9cf4107..5af58a2dc8 100644 --- a/DvdLib/Ifo/Dvd.cs +++ b/DvdLib/Ifo/Dvd.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using MediaBrowser.Model.IO; namespace DvdLib.Ifo { @@ -13,13 +12,10 @@ namespace DvdLib.Ifo private ushort _titleCount; public readonly Dictionary VTSPaths = new Dictionary(); - private readonly IFileSystem _fileSystem; - - public Dvd(string path, IFileSystem fileSystem) + public Dvd(string path) { - _fileSystem = fileSystem; Titles = new List(); - var allFiles = _fileSystem.GetFiles(path, true).ToList(); + var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories); var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ?? allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase)); @@ -76,7 +72,7 @@ namespace DvdLib.Ifo } } - private void ReadVTS(ushort vtsNum, IEnumerable<FileSystemMetadata> allFiles) + private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles) { var filename = string.Format("VTS_{0:00}_0.IFO", vtsNum); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 4e2cf334c0..50a5135d48 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2364,7 +2364,7 @@ namespace Emby.Server.Implementations.Library string videoPath, string[] files) { - new SubtitleResolver(BaseItem.LocalizationManager, _fileSystem).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files); + new SubtitleResolver(BaseItem.LocalizationManager).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files); } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 3999025d8e..6982568eb2 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -46,7 +46,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IApplicationPaths _appPaths; private readonly IJsonSerializer _json; private readonly IEncodingManager _encodingManager; - private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; @@ -134,7 +133,6 @@ namespace MediaBrowser.Providers.MediaInfo IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, - IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, @@ -149,7 +147,6 @@ namespace MediaBrowser.Providers.MediaInfo _appPaths = appPaths; _json = json; _encodingManager = encodingManager; - _fileSystem = fileSystem; _config = config; _subtitleManager = subtitleManager; _chapterManager = chapterManager; @@ -157,7 +154,7 @@ namespace MediaBrowser.Providers.MediaInfo _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; - _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager, fileSystem); + _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); } private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); @@ -202,7 +199,6 @@ namespace MediaBrowser.Providers.MediaInfo _blurayExaminer, _localization, _encodingManager, - _fileSystem, _config, _subtitleManager, _chapterManager, diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index d2e98a5a94..89496622fc 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -39,7 +39,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; private readonly IEncodingManager _encodingManager; - private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; @@ -56,7 +55,6 @@ namespace MediaBrowser.Providers.MediaInfo IBlurayExaminer blurayExaminer, ILocalizationManager localization, IEncodingManager encodingManager, - IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager, @@ -68,7 +66,6 @@ namespace MediaBrowser.Providers.MediaInfo _blurayExaminer = blurayExaminer; _localization = localization; _encodingManager = encodingManager; - _fileSystem = fileSystem; _config = config; _subtitleManager = subtitleManager; _chapterManager = chapterManager; @@ -76,7 +73,8 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; } - public async Task<ItemUpdateType> ProbeVideo<T>(T item, + public async Task<ItemUpdateType> ProbeVideo<T>( + T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video @@ -99,7 +97,6 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } } - else if (item.VideoType == VideoType.BluRay) { var inputPath = item.Path; @@ -130,7 +127,8 @@ namespace MediaBrowser.Providers.MediaInfo return ItemUpdateType.MetadataImport; } - private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(Video item, + private Task<Model.MediaInfo.MediaInfo> GetMediaInfo( + Video item, string[] streamFileNames, CancellationToken cancellationToken) { @@ -145,22 +143,24 @@ namespace MediaBrowser.Providers.MediaInfo protocol = _mediaSourceManager.GetPathProtocol(path); } - return _mediaEncoder.GetMediaInfo(new MediaInfoRequest - { - PlayableStreamFileNames = streamFileNames, - ExtractChapters = true, - MediaType = DlnaProfileType.Video, - MediaSource = new MediaSourceInfo + return _mediaEncoder.GetMediaInfo( + new MediaInfoRequest { - Path = path, - Protocol = protocol, - VideoType = item.VideoType - } - - }, cancellationToken); + PlayableStreamFileNames = streamFileNames, + ExtractChapters = true, + MediaType = DlnaProfileType.Video, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = protocol, + VideoType = item.VideoType + } + }, + cancellationToken); } - protected async Task Fetch(Video video, + protected async Task Fetch( + Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, BlurayDiscInfo blurayInfo, @@ -491,12 +491,13 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="options">The refreshOptions.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task AddExternalSubtitles(Video video, + private async Task AddExternalSubtitles( + Video video, List<MediaStream> currentStreams, MetadataRefreshOptions options, CancellationToken cancellationToken) { - var subtitleResolver = new SubtitleResolver(_localization, _fileSystem); + var subtitleResolver = new SubtitleResolver(_localization); var startIndex = currentStreams.Count == 0 ? 0 : (currentStreams.Select(i => i.Index).Max() + 1); var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, startIndex, options.DirectoryService, false); @@ -605,7 +606,7 @@ namespace MediaBrowser.Providers.MediaInfo private string[] FetchFromDvdLib(Video item) { var path = item.Path; - var dvd = new Dvd(path, _fileSystem); + var dvd = new Dvd(path); var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 7ebbb9e237..ed470b117f 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -13,7 +13,6 @@ namespace MediaBrowser.Providers.MediaInfo public class SubtitleResolver { private readonly ILocalizationManager _localization; - private readonly IFileSystem _fileSystem; private static readonly HashSet<string> SubtitleExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { @@ -26,13 +25,13 @@ namespace MediaBrowser.Providers.MediaInfo ".vtt" }; - public SubtitleResolver(ILocalizationManager localization, IFileSystem fileSystem) + public SubtitleResolver(ILocalizationManager localization) { _localization = localization; - _fileSystem = fileSystem; } - public List<MediaStream> GetExternalSubtitleStreams(Video video, + public List<MediaStream> GetExternalSubtitleStreams( + Video video, int startIndex, IDirectoryService directoryService, bool clearCache) From 8e9aeb84b18f43b6fe8dd89ab84f1627bf2e8dbd Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 19:33:36 +0900 Subject: [PATCH 136/411] remove release channel from plugin classes --- .../ApplicationHost.cs | 14 ------------- .../Updates/InstallationManager.cs | 21 +++++++------------ MediaBrowser.Api/PackageService.cs | 10 +-------- MediaBrowser.Common/IApplicationHost.cs | 6 ------ .../Updates/IInstallationManager.cs | 8 ++----- .../Services/IHasRequestFilter.cs | 10 ++++----- MediaBrowser.Model/System/SystemInfo.cs | 2 -- .../Updates/InstallationInfo.cs | 6 ------ MediaBrowser.Model/Updates/ReleaseChannel.cs | 18 ---------------- MediaBrowser.Model/Updates/VersionInfo.cs | 6 ------ 10 files changed, 15 insertions(+), 86 deletions(-) delete mode 100644 MediaBrowser.Model/Updates/ReleaseChannel.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ad77ab8b48..3cf4d6c6df 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -211,19 +211,6 @@ namespace Emby.Server.Implementations public IFileSystem FileSystemManager { get; set; } - /// <inheritdoc /> - public ReleaseChannel SystemUpdateLevel - { - get - { -#if NIGHTLY - return PackageChannel.Nightly; -#else - return ReleaseChannel.Stable; -#endif - } - } - /// <summary> /// Gets or sets the service provider. /// </summary> @@ -1416,7 +1403,6 @@ namespace Emby.Server.Implementations SupportsLibraryMonitor = true, EncoderLocation = MediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, - SystemUpdateLevel = SystemUpdateLevel, PackageName = StartupOptions.PackageName }; } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 1450c74d2d..a00dec4c3a 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -150,13 +150,11 @@ namespace Emby.Server.Implementations.Updates /// <inheritdoc /> public IEnumerable<VersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> availableVersions, - Version minVersion = null, - ReleaseChannel releaseChannel = ReleaseChannel.Stable) + Version minVersion = null) { var appVer = _applicationHost.ApplicationVersion; availableVersions = availableVersions - .Where(x => x.channel == releaseChannel - && Version.Parse(x.minimumServerVersion) <= appVer); + .Where(x => Version.Parse(x.minimumServerVersion) <= appVer); if (minVersion != null) { @@ -171,8 +169,7 @@ namespace Emby.Server.Implementations.Updates IEnumerable<PackageInfo> availablePackages, string name = null, Guid guid = default, - Version minVersion = null, - ReleaseChannel releaseChannel = ReleaseChannel.Stable) + Version minVersion = null) { var package = FilterPackages(availablePackages, name, guid).FirstOrDefault(); @@ -184,8 +181,7 @@ namespace Emby.Server.Implementations.Updates return GetCompatibleVersions( package.versions, - minVersion, - releaseChannel); + minVersion); } /// <inheritdoc /> @@ -193,12 +189,10 @@ namespace Emby.Server.Implementations.Updates { var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); - var systemUpdateLevel = _applicationHost.SystemUpdateLevel; - // Figure out what needs to be installed foreach (var plugin in _applicationHost.Plugins) { - var compatibleVersions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version, systemUpdateLevel); + var compatibleVersions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version); var version = compatibleVersions.FirstOrDefault(y => y.versionCode > plugin.Version); if (version != null && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase))) @@ -221,7 +215,6 @@ namespace Emby.Server.Implementations.Updates Id = Guid.NewGuid(), Name = package.name, AssemblyGuid = package.guid, - UpdateClass = package.channel, Version = package.versionString }; @@ -313,13 +306,13 @@ namespace Emby.Server.Implementations.Updates // Do plugin-specific processing if (plugin == null) { - _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionString ?? string.Empty, package.channel); + _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionString ?? string.Empty); PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package)); } else { - _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionString ?? string.Empty, package.channel); + _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionString ?? string.Empty); PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package))); } diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index ccc978295c..444354a992 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -71,13 +71,6 @@ namespace MediaBrowser.Api /// <value>The version.</value> [ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string Version { get; set; } - - /// <summary> - /// Gets or sets the update class. - /// </summary> - /// <value>The update class.</value> - [ApiMember(Name = "UpdateClass", Description = "Optional update class (Dev, Beta, Release). Defaults to Release.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public ReleaseChannel UpdateClass { get; set; } } /// <summary> @@ -152,8 +145,7 @@ namespace MediaBrowser.Api packages, request.Name, string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid), - string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version), - request.UpdateClass).FirstOrDefault(); + string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version)).FirstOrDefault(); if (package == null) { diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index c88eac27a1..695e6f8752 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -47,12 +47,6 @@ namespace MediaBrowser.Common /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> bool CanSelfRestart { get; } - /// <summary> - /// Gets the version class of the system. - /// </summary> - /// <value><see cref="ReleaseChannel.Stable" /> or <see cref="ReleaseChannel.Nightly" />.</value> - ReleaseChannel SystemUpdateLevel { get; } - /// <summary> /// Gets the application version. /// </summary> diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 284e418d9d..4d512220bb 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -65,12 +65,10 @@ namespace MediaBrowser.Common.Updates /// </summary> /// <param name="availableVersions">The available version of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param> - /// <param name="releaseChannel">The classification of updates.</param> /// <returns>All compatible versions ordered from newest to oldest.</returns> IEnumerable<VersionInfo> GetCompatibleVersions( IEnumerable<VersionInfo> availableVersions, - Version minVersion = null, - ReleaseChannel releaseChannel = ReleaseChannel.Stable); + Version minVersion = null); /// <summary> /// Returns all compatible versions ordered from newest to oldest. @@ -79,14 +77,12 @@ namespace MediaBrowser.Common.Updates /// <param name="name">The name.</param> /// <param name="guid">The guid of the plugin.</param> /// <param name="minVersion">The minimum required version of the plugin.</param> - /// <param name="releaseChannel">The classification.</param> /// <returns>All compatible versions ordered from newest to oldest.</returns> IEnumerable<VersionInfo> GetCompatibleVersions( IEnumerable<PackageInfo> availablePackages, string name = null, Guid guid = default, - Version minVersion = null, - ReleaseChannel releaseChannel = ReleaseChannel.Stable); + Version minVersion = null); /// <summary> /// Returns the available plugin updates. diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs index c81e49e4eb..418d5c501e 100644 --- a/MediaBrowser.Model/Services/IHasRequestFilter.cs +++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs @@ -9,17 +9,17 @@ namespace MediaBrowser.Model.Services { /// <summary> /// Order in which Request Filters are executed. - /// <0 Executed before global request filters - /// >0 Executed after global request filters + /// <0 Executed before global request filters. + /// >0 Executed after global request filters. /// </summary> int Priority { get; } /// <summary> /// The request filter is executed before the service. /// </summary> - /// <param name="req">The http request wrapper</param> - /// <param name="res">The http response wrapper</param> - /// <param name="requestDto">The request DTO</param> + /// <param name="req">The http request wrapper.</param> + /// <param name="res">The http response wrapper.</param> + /// <param name="requestDto">The request DTO.</param> void RequestFilter(IRequest req, HttpResponse res, object requestDto); } } diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index da39ee208a..bda43e1afa 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -27,8 +27,6 @@ namespace MediaBrowser.Model.System /// </summary> public class SystemInfo : PublicSystemInfo { - public ReleaseChannel SystemUpdateLevel { get; set; } - /// <summary> /// Gets or sets the display name of the operating system. /// </summary> diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs index 870bf8c0be..95357262ac 100644 --- a/MediaBrowser.Model/Updates/InstallationInfo.cs +++ b/MediaBrowser.Model/Updates/InstallationInfo.cs @@ -30,11 +30,5 @@ namespace MediaBrowser.Model.Updates /// </summary> /// <value>The version.</value> public string Version { get; set; } - - /// <summary> - /// Gets or sets the update class. - /// </summary> - /// <value>The update class.</value> - public ReleaseChannel UpdateClass { get; set; } } } diff --git a/MediaBrowser.Model/Updates/ReleaseChannel.cs b/MediaBrowser.Model/Updates/ReleaseChannel.cs deleted file mode 100644 index ed4a774a72..0000000000 --- a/MediaBrowser.Model/Updates/ReleaseChannel.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace MediaBrowser.Model.Updates -{ - /// <summary> - /// Enum PackageVersionClass. - /// </summary> - public enum ReleaseChannel - { - /// <summary> - /// The stable. - /// </summary> - Stable = 0, - - /// <summary> - /// The nightly. - /// </summary> - Nightly = 1 - } -} diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index ad893db2e2..177b8dbc33 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -34,12 +34,6 @@ namespace MediaBrowser.Model.Updates /// <value>The version.</value> public Version versionCode { get; set; } - /// <summary> - /// Gets or sets the release channel. - /// </summary> - /// <value>The release channel for a given package version.</value> - public ReleaseChannel channel { get; set; } - /// <summary> /// Gets or sets the description. /// </summary> From 78abbcc25117a20511f70f65c89f2f1063d0e547 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 19:52:40 +0900 Subject: [PATCH 137/411] standardize plugin version and guid properties --- .../Activity/ActivityLogEntryPoint.cs | 4 +-- .../Updates/InstallationManager.cs | 21 +++++++------- .../Updates/CheckForUpdateResult.cs | 29 ------------------- .../Updates/InstallationInfo.cs | 12 ++------ MediaBrowser.Model/Updates/PackageInfo.cs | 12 -------- MediaBrowser.Model/Updates/VersionInfo.cs | 8 +---- 6 files changed, 16 insertions(+), 70 deletions(-) delete mode 100644 MediaBrowser.Model/Updates/CheckForUpdateResult.cs diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 0f0b8b97b1..f60fdea846 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -433,7 +433,7 @@ namespace Emby.Server.Implementations.Activity ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), - e.Argument.Item2.versionString), + e.Argument.Item2.version), Overview = e.Argument.Item2.description }); } @@ -462,7 +462,7 @@ namespace Emby.Server.Implementations.Activity ShortOverview = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), - e.Argument.versionString) + e.Argument.version) }); } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index a00dec4c3a..ffb6b5cbc3 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -158,10 +158,10 @@ namespace Emby.Server.Implementations.Updates if (minVersion != null) { - availableVersions = availableVersions.Where(x => x.versionCode >= minVersion); + availableVersions = availableVersions.Where(x => x.version >= minVersion); } - return availableVersions.OrderByDescending(x => x.versionCode); + return availableVersions.OrderByDescending(x => x.version); } /// <inheritdoc /> @@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.Updates foreach (var plugin in _applicationHost.Plugins) { var compatibleVersions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version); - var version = compatibleVersions.FirstOrDefault(y => y.versionCode > plugin.Version); + var version = compatibleVersions.FirstOrDefault(y => y.version > plugin.Version); if (version != null - && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase))) + && !CompletedInstallations.Any(x => string.Equals(x.Guid, version.guid, StringComparison.OrdinalIgnoreCase))) { yield return version; } @@ -212,10 +212,9 @@ namespace Emby.Server.Implementations.Updates var installationInfo = new InstallationInfo { - Id = Guid.NewGuid(), + Guid = package.guid, Name = package.name, - AssemblyGuid = package.guid, - Version = package.versionString + Version = package.version.ToString() }; var innerCancellationTokenSource = new CancellationTokenSource(); @@ -258,7 +257,7 @@ namespace Emby.Server.Implementations.Updates _currentInstallations.Remove(tuple); } - _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.versionString); + _logger.LogInformation("Package installation cancelled: {0} {1}", package.name, package.version); PackageInstallationCancelled?.Invoke(this, installationEventArgs); @@ -306,13 +305,13 @@ namespace Emby.Server.Implementations.Updates // Do plugin-specific processing if (plugin == null) { - _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.versionString ?? string.Empty); + _logger.LogInformation("New plugin installed: {0} {1} {2}", package.name, package.version); PluginInstalled?.Invoke(this, new GenericEventArgs<VersionInfo>(package)); } else { - _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.versionString ?? string.Empty); + _logger.LogInformation("Plugin updated: {0} {1} {2}", package.name, package.version); PluginUpdated?.Invoke(this, new GenericEventArgs<(IPlugin, VersionInfo)>((plugin, package))); } @@ -430,7 +429,7 @@ namespace Emby.Server.Implementations.Updates { lock (_currentInstallationsLock) { - var install = _currentInstallations.Find(x => x.info.Id == id); + var install = _currentInstallations.Find(x => x.info.Guid == id.ToString()); if (install == default((InstallationInfo, CancellationTokenSource))) { return false; diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs deleted file mode 100644 index 883fc636b4..0000000000 --- a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace MediaBrowser.Model.Updates -{ - /// <summary> - /// Class CheckForUpdateResult. - /// </summary> - public class CheckForUpdateResult - { - /// <summary> - /// Gets or sets a value indicating whether this instance is update available. - /// </summary> - /// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value> - public bool IsUpdateAvailable { get; set; } - - /// <summary> - /// Gets or sets the available version. - /// </summary> - /// <value>The available version.</value> - public string AvailableVersion - { - get => Package != null ? Package.versionString : "0.0.0.1"; - set { } // need this for the serializer - } - - /// <summary> - /// Get or sets package information for an available update - /// </summary> - public VersionInfo Package { get; set; } - } -} diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs index 95357262ac..e0d450d065 100644 --- a/MediaBrowser.Model/Updates/InstallationInfo.cs +++ b/MediaBrowser.Model/Updates/InstallationInfo.cs @@ -8,10 +8,10 @@ namespace MediaBrowser.Model.Updates public class InstallationInfo { /// <summary> - /// Gets or sets the id. + /// Gets or sets the guid. /// </summary> - /// <value>The id.</value> - public Guid Id { get; set; } + /// <value>The guid.</value> + public string Guid { get; set; } /// <summary> /// Gets or sets the name. @@ -19,12 +19,6 @@ namespace MediaBrowser.Model.Updates /// <value>The name.</value> public string Name { get; set; } - /// <summary> - /// Gets or sets the assembly guid. - /// </summary> - /// <value>The guid of the assembly.</value> - public string AssemblyGuid { get; set; } - /// <summary> /// Gets or sets the version. /// </summary> diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs index d06ffe1e6c..b19c311e4b 100644 --- a/MediaBrowser.Model/Updates/PackageInfo.cs +++ b/MediaBrowser.Model/Updates/PackageInfo.cs @@ -26,18 +26,6 @@ namespace MediaBrowser.Model.Updates /// <value>The overview.</value> public string overview { get; set; } - /// <summary> - /// Gets or sets the thumb image. - /// </summary> - /// <value>The thumb image.</value> - public string thumbImage { get; set; } - - /// <summary> - /// Gets or sets the preview image. - /// </summary> - /// <value>The preview image.</value> - public string previewImage { get; set; } - /// <summary> /// Gets or sets the target filename for the downloaded binary. /// </summary> diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 177b8dbc33..6756e7454a 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -22,17 +22,11 @@ namespace MediaBrowser.Model.Updates /// <value>The guid.</value> public string guid { get; set; } - /// <summary> - /// Gets or sets the version string. - /// </summary> - /// <value>The version string.</value> - public string versionString { get; set; } - /// <summary> /// Gets or sets the version. /// </summary> /// <value>The version.</value> - public Version versionCode { get; set; } + public Version version { get; set; } /// <summary> /// Gets or sets the description. From abb7ed9c35cc7727e81357c3b55555ee4c714d10 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 19:54:33 +0900 Subject: [PATCH 138/411] rename target abi property --- Emby.Server.Implementations/Updates/InstallationManager.cs | 2 +- MediaBrowser.Model/Updates/VersionInfo.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index ffb6b5cbc3..6a34ca74b0 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -154,7 +154,7 @@ namespace Emby.Server.Implementations.Updates { var appVer = _applicationHost.ApplicationVersion; availableVersions = availableVersions - .Where(x => Version.Parse(x.minimumServerVersion) <= appVer); + .Where(x => Version.Parse(x.targetAbi) <= appVer); if (minVersion != null) { diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 6756e7454a..ac47e97daa 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -35,10 +35,10 @@ namespace MediaBrowser.Model.Updates public string description { get; set; } /// <summary> - /// Gets or sets the minimum required version for the server. + /// Gets or sets the ABI that this version was built against. /// </summary> - /// <value>The minimum required version.</value> - public string minimumServerVersion { get; set; } + /// <value>The target ABI version.</value> + public string targetAbi { get; set; } /// <summary> /// Gets or sets the source URL. From 2da5df6c25b96e2a2d6790df7df8066a666dbe0e Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 19:56:42 +0900 Subject: [PATCH 139/411] add new property for version changelogs --- .../Activity/ActivityLogEntryPoint.cs | 2 +- MediaBrowser.Model/Updates/VersionInfo.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index f60fdea846..c91506b85b 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -434,7 +434,7 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.version), - Overview = e.Argument.Item2.description + Overview = e.Argument.Item2.changelog }); } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index ac47e97daa..e67e60d745 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -29,10 +29,10 @@ namespace MediaBrowser.Model.Updates public Version version { get; set; } /// <summary> - /// Gets or sets the description. + /// Gets or sets the changelog for this version. /// </summary> - /// <value>The description.</value> - public string description { get; set; } + /// <value>The changelog.</value> + public string changelog { get; set; } /// <summary> /// Gets or sets the ABI that this version was built against. From cbe1fe2c8f82af2233201203367ba8a532dbec8b Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 20:00:32 +0900 Subject: [PATCH 140/411] update description and overview for plugins --- MediaBrowser.Model/Updates/PackageInfo.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs index b19c311e4b..f5aa8b6fa2 100644 --- a/MediaBrowser.Model/Updates/PackageInfo.cs +++ b/MediaBrowser.Model/Updates/PackageInfo.cs @@ -15,23 +15,17 @@ namespace MediaBrowser.Model.Updates public string name { get; set; } /// <summary> - /// Gets or sets the short description. + /// Gets or sets a long description of the plugin containing features or helpful explanations. /// </summary> - /// <value>The short description.</value> - public string shortDescription { get; set; } + /// <value>The description.</value> + public string description { get; set; } /// <summary> - /// Gets or sets the overview. + /// Gets or sets a short overview of what the plugin does. /// </summary> /// <value>The overview.</value> public string overview { get; set; } - /// <summary> - /// Gets or sets the target filename for the downloaded binary. - /// </summary> - /// <value>The target filename.</value> - public string filename { get; set; } - /// <summary> /// Gets or sets the owner. /// </summary> From ff065df9863f30a4eec52a95d260cdadcced7b1e Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sat, 11 Apr 2020 20:11:41 +0900 Subject: [PATCH 141/411] update plugin manifest url --- Emby.Server.Implementations/ConfigurationOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 20bdd18e70..db7c35a7c8 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -18,7 +18,7 @@ namespace Emby.Server.Implementations { { HostWebClientKey, bool.TrueString }, { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, - { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest.json" }, + { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.TrueString } From 6b7517e5061ef2dca8d6d397705291f03ee0393a Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sat, 11 Apr 2020 15:37:24 +0200 Subject: [PATCH 142/411] Fix indentation --- MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index ed470b117f..2bbe8a968d 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -32,9 +32,9 @@ namespace MediaBrowser.Providers.MediaInfo public List<MediaStream> GetExternalSubtitleStreams( Video video, - int startIndex, - IDirectoryService directoryService, - bool clearCache) + int startIndex, + IDirectoryService directoryService, + bool clearCache) { var streams = new List<MediaStream>(); From 17e8813378c2fe1a83d1eddb829dae68f8c71bfe Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sat, 11 Apr 2020 10:53:13 -0400 Subject: [PATCH 143/411] Use ActivatorUtilities to construct MediaEncoder and update constructor to inject EncodingHelper correctly --- Emby.Server.Implementations/ApplicationHost.cs | 16 ++++------------ .../Encoder/MediaEncoder.cs | 16 ++++------------ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5b93981f01..ad0a69b19e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -88,7 +88,6 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; @@ -106,7 +105,6 @@ 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; @@ -614,17 +612,11 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); serviceCollection.AddSingleton<IUserManager, UserManager>(); - // TODO: Add StartupOptions.FFmpegPath to IConfiguration so this doesn't need to be constructed manually + // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required + // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation + serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>)); serviceCollection.AddSingleton<IMediaEncoder>(provider => - new MediaBrowser.MediaEncoding.Encoder.MediaEncoder( - provider.GetRequiredService<ILogger<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>>(), - provider.GetRequiredService<IServerConfigurationManager>(), - provider.GetRequiredService<IFileSystem>(), - provider.GetRequiredService<IProcessFactory>(), - provider.GetRequiredService<ILocalizationManager>(), - provider.GetRequiredService<ISubtitleEncoder>, - provider.GetRequiredService<IConfiguration>(), - _startupOptions.FFmpegPath)); + ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty)); // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>)); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f3f2b86eee..c5bba87802 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -40,8 +40,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly IFileSystem _fileSystem; private readonly IProcessFactory _processFactory; private readonly ILocalizationManager _localization; - private readonly Func<ISubtitleEncoder> _subtitleEncoder; - private readonly IConfiguration _configuration; + private readonly Lazy<EncodingHelper> _encodingHelperFactory; private readonly string _startupOptionFFmpegPath; private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2); @@ -49,8 +48,6 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly object _runningProcessesLock = new object(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); - private EncodingHelper _encodingHelper; - private string _ffmpegPath; private string _ffprobePath; @@ -60,8 +57,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IFileSystem fileSystem, IProcessFactory processFactory, ILocalizationManager localization, - Func<ISubtitleEncoder> subtitleEncoder, - IConfiguration configuration, + Lazy<EncodingHelper> encodingHelperFactory, string startupOptionsFFmpegPath) { _logger = logger; @@ -69,15 +65,11 @@ namespace MediaBrowser.MediaEncoding.Encoder _fileSystem = fileSystem; _processFactory = processFactory; _localization = localization; + _encodingHelperFactory = encodingHelperFactory; _startupOptionFFmpegPath = startupOptionsFFmpegPath; - _subtitleEncoder = subtitleEncoder; - _configuration = configuration; } - private EncodingHelper EncodingHelper - => LazyInitializer.EnsureInitialized( - ref _encodingHelper, - () => new EncodingHelper(this, _fileSystem, _subtitleEncoder(), _configuration)); + private EncodingHelper EncodingHelper => _encodingHelperFactory.Value; /// <inheritdoc /> public string EncoderPath => _ffmpegPath; From 129e844252bae9becd3515a6dbf3bdff35180696 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Mon, 13 Apr 2020 02:20:37 +0900 Subject: [PATCH 144/411] use web artifacts for build step on ci --- .ci/azure-pipelines-compat.yml | 18 ++++++------ .ci/azure-pipelines-main.yml | 52 ++++++++++++++-------------------- .ci/azure-pipelines-test.yml | 35 +++++++++++------------ .ci/azure-pipelines.yml | 12 ++++---- 4 files changed, 54 insertions(+), 63 deletions(-) diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-compat.yml index de60d2ccb1..1ffaaf2b98 100644 --- a/.ci/azure-pipelines-compat.yml +++ b/.ci/azure-pipelines-compat.yml @@ -1,13 +1,13 @@ parameters: - - name: Packages - type: object - default: {} - - name: LinuxImage - type: string - default: "ubuntu-latest" - - name: DotNetSdkVersion - type: string - default: 3.1.100 +- name: Packages + type: object + default: {} +- name: LinuxImage + type: string + default: "ubuntu-latest" +- name: DotNetSdkVersion + type: string + default: 3.1.100 jobs: - job: CompatibilityCheck diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index d155624abb..59acd51ec5 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -20,41 +20,33 @@ jobs: submodules: true persistCredentials: true - - task: CmdLine@2 - displayName: "Clone Web Branch" - condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + - task: DownloadPipelineArtifact@2 + displayName: "Download Web Branch" + condition: in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion') inputs: - script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + path: '$(Agent.TempDirectory)' + artifact: 'jellyfin-web-production' + source: 'specific' + project: 'Jellyfin Web' + pipeline: 'Build' + runBranch: variables['Build.SourceBranch'] - - task: CmdLine@2 - displayName: "Clone Web Target" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) + - task: DownloadPipelineArtifact@2 + displayName: "Download Web Target" + condition: in(variables['Build.Reason'], 'PullRequest') inputs: - script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + path: '$(Agent.TempDirectory)' + artifact: 'jellyfin-web-production' + source: 'specific' + project: 'Jellyfin Web' + pipeline: 'Build' + runBranch: variables['System.PullRequest.TargetBranch'] - - task: NodeTool@0 - displayName: "Install Node" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + - task: ExtractFiles@1 + displayName: "Extract Web Client" inputs: - versionSpec: "12.x" - - - task: CmdLine@2 - displayName: "Build Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - script: yarn install - workingDirectory: $(Agent.TempDirectory)/jellyfin-web - - - task: CopyFiles@2 - displayName: "Copy Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist - contents: "**" - targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web - cleanTargetFolder: true - overWrite: true - flattenFolders: false + archiveFilePatterns: '$(Agent.TempDirectory)/*.zip' + destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard' - task: UseDotNet@2 displayName: "Update DotNet" diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index 4455632e15..a5d29fb619 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -1,26 +1,25 @@ parameters: - - name: ImageNames - type: object - default: - Linux: "ubuntu-latest" - Windows: "windows-latest" - macOS: "macos-latest" - - name: TestProjects - type: string - default: "tests/**/*Tests.csproj" - - name: DotNetSdkVersion - type: string - default: 3.1.100 +- name: ImageNames + type: object + default: + Linux: "ubuntu-latest" + Windows: "windows-latest" + macOS: "macos-latest" +- name: TestProjects + type: string + default: "tests/**/*Tests.csproj" +- name: DotNetSdkVersion + type: string + default: 3.1.100 jobs: - - job: MainTest - displayName: Main Test + - job: Test + displayName: Test strategy: matrix: ${{ each imageName in parameters.ImageNames }}: ${{ imageName.key }}: ImageName: ${{ imageName.value }} - maxParallel: 3 pool: vmImage: "$(ImageName)" steps: @@ -36,7 +35,7 @@ jobs: version: ${{ parameters.DotNetSdkVersion }} - task: DotNetCoreCLI@2 - displayName: Run .NET Core CLI tests + displayName: 'Run CLI Tests' inputs: command: "test" projects: ${{ parameters.TestProjects }} @@ -47,7 +46,7 @@ jobs: - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging - displayName: ReportGenerator (merge) + displayName: 'Run ReportGenerator' inputs: reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml" targetdir: "$(Agent.TempDirectory)/merged/" @@ -56,7 +55,7 @@ jobs: ## V2 is already in the repository but it does not work "wrong number of segments" YAML error. - task: PublishCodeCoverageResults@1 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging - displayName: Publish Code Coverage + displayName: 'Publish Code Coverage' inputs: codeCoverageTool: "cobertura" #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2 diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 3522cbf006..1a439c7185 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -1,12 +1,12 @@ name: $(Date:yyyyMMdd)$(Rev:.r) variables: - - name: TestProjects - value: "tests/**/*Tests.csproj" - - name: RestoreBuildProjects - value: "Jellyfin.Server/Jellyfin.Server.csproj" - - name: DotNetSdkVersion - value: 3.1.100 +- name: TestProjects + value: "tests/**/*Tests.csproj" +- name: RestoreBuildProjects + value: "Jellyfin.Server/Jellyfin.Server.csproj" +- name: DotNetSdkVersion + value: 3.1.100 pr: autoCancel: true From 8c6e1ef27e79ad38f8cd5fdc42feb2e8ee313d41 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Mon, 13 Apr 2020 02:27:42 +0900 Subject: [PATCH 145/411] fix pipeline references --- .ci/azure-pipelines-main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index 59acd51ec5..de20412abe 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -27,8 +27,8 @@ jobs: path: '$(Agent.TempDirectory)' artifact: 'jellyfin-web-production' source: 'specific' - project: 'Jellyfin Web' - pipeline: 'Build' + project: 'jellyfin' + pipeline: 'Jellyfin Web' runBranch: variables['Build.SourceBranch'] - task: DownloadPipelineArtifact@2 @@ -38,8 +38,8 @@ jobs: path: '$(Agent.TempDirectory)' artifact: 'jellyfin-web-production' source: 'specific' - project: 'Jellyfin Web' - pipeline: 'Build' + project: 'jellyfin' + pipeline: 'Jellyfin Web' runBranch: variables['System.PullRequest.TargetBranch'] - task: ExtractFiles@1 From 4758e750907f28df3b324b8fb3243baf716c5a30 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Mon, 13 Apr 2020 02:34:52 +0900 Subject: [PATCH 146/411] leave files in destination folder during extraction --- .ci/azure-pipelines-main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index de20412abe..40568a3ae4 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -47,6 +47,7 @@ jobs: inputs: archiveFilePatterns: '$(Agent.TempDirectory)/*.zip' destinationFolder: '$(Build.SourcesDirectory)/MediaBrowser.WebDashboard' + cleanDestinationFolder: false - task: UseDotNet@2 displayName: "Update DotNet" From 5a658cf260c18c8f17e2ffc02af43832b9076a53 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Sun, 12 Apr 2020 19:17:46 -0600 Subject: [PATCH 147/411] Fix casing of JSON in Jellyfin API. Currently only affects startup wizard. --- Jellyfin.Api/Controllers/StartupController.cs | 1 + Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index afc9b8f3da..4e515bd8d8 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; namespace Jellyfin.Api.Controllers { diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index dd4f9cd238..8e124d0701 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -71,6 +71,10 @@ namespace Jellyfin.Server.Extensions // Clear app parts to avoid other assemblies being picked up .ConfigureApplicationPartManager(a => a.ApplicationParts.Clear()) .AddApplicationPart(typeof(StartupController).Assembly) + .AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = null; + }) .AddControllersAsServices(); } From 16a66dc89c78180f8abb8a65c454b39b3b253a55 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Sun, 12 Apr 2020 19:25:02 -0600 Subject: [PATCH 148/411] Remove unused import, fix casing of startup wizard POST parameters --- Jellyfin.Api/Controllers/StartupController.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 4e515bd8d8..1e490faaec 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; namespace Jellyfin.Api.Controllers { @@ -61,18 +60,18 @@ namespace Jellyfin.Api.Controllers /// <summary> /// Endpoint for updating the initial startup wizard configuration. /// </summary> - /// <param name="uiCulture">The UI language culture.</param> - /// <param name="metadataCountryCode">The metadata country code.</param> - /// <param name="preferredMetadataLanguage">The preferred language for metadata.</param> + /// <param name="UICulture">The UI language culture.</param> + /// <param name="MetadataCountryCode">The metadata country code.</param> + /// <param name="PreferredMetadataLanguage">The preferred language for metadata.</param> [HttpPost("Configuration")] public void UpdateInitialConfiguration( - [FromForm] string uiCulture, - [FromForm] string metadataCountryCode, - [FromForm] string preferredMetadataLanguage) + [FromForm] string UICulture, + [FromForm] string MetadataCountryCode, + [FromForm] string PreferredMetadataLanguage) { - _config.Configuration.UICulture = uiCulture; - _config.Configuration.MetadataCountryCode = metadataCountryCode; - _config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage; + _config.Configuration.UICulture = UICulture; + _config.Configuration.MetadataCountryCode = MetadataCountryCode; + _config.Configuration.PreferredMetadataLanguage = PreferredMetadataLanguage; _config.SaveConfiguration(); } From b52199e9e20dcd10c1921e43c53892ec5ec1c5eb Mon Sep 17 00:00:00 2001 From: Matt Lyons <lyonzy@fastmail.com> Date: Mon, 13 Apr 2020 12:44:15 +1000 Subject: [PATCH 149/411] Handle null outputFileExtension in GetOutputFilePath --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index eb44cb4266..b92ea6d1f4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -131,6 +131,8 @@ namespace MediaBrowser.Api.Playback /// </summary> private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension) { + if (outputFileExtension == null) outputFileExtension = ""; + var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}"; var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture); From b937eb0c1150a650492e666016e268d2b3f463c9 Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Mon, 13 Apr 2020 14:34:31 +0900 Subject: [PATCH 150/411] change conditional for download task on ci Co-Authored-By: Mark Monteiro <marknr.monteiro@protonmail.com> --- .ci/azure-pipelines-main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index 40568a3ae4..456be7108f 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -33,7 +33,7 @@ jobs: - task: DownloadPipelineArtifact@2 displayName: "Download Web Target" - condition: in(variables['Build.Reason'], 'PullRequest') + condition: eq(variables['Build.Reason'], 'PullRequest') inputs: path: '$(Agent.TempDirectory)' artifact: 'jellyfin-web-production' From 4ecce9e0855a6f132a66bd3b5864ba3538ad1eef Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 10:39:10 +0000 Subject: [PATCH 151/411] Bump Mono.Nat from 2.0.0 to 2.0.1 Bumps [Mono.Nat](https://github.com/mono/Mono.Nat) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/mono/Mono.Nat/releases) - [Commits](https://github.com/mono/Mono.Nat/compare/Mono.Nat-2.0.0...Mono.Nat-2.0.1) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d46b9507ba..3d065f70a9 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -33,7 +33,7 @@ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" /> - <PackageReference Include="Mono.Nat" Version="2.0.0" /> + <PackageReference Include="Mono.Nat" Version="2.0.1" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> <PackageReference Include="sharpcompress" Version="0.25.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> From 5c117734a59a714584451c1c760e941e74dbd0f9 Mon Sep 17 00:00:00 2001 From: Delgan <delgan.py@gmail.com> Date: Mon, 13 Apr 2020 14:35:00 +0200 Subject: [PATCH 152/411] Improve movie resolver if space precedes the year --- CONTRIBUTORS.md | 1 + Emby.Naming/Common/NamingOptions.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3d6023b829..ce956176e8 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -22,6 +22,7 @@ - [cvium](https://github.com/cvium) - [dannymichel](https://github.com/dannymichel) - [DaveChild](https://github.com/DaveChild) + - [Delgan](https://github.com/Delgan) - [dcrdev](https://github.com/dcrdev) - [dhartung](https://github.com/dhartung) - [dinki](https://github.com/dinki) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index c00181c7e6..caff7b7dae 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -136,7 +136,7 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { - @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" + @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" }; CleanStrings = new[] From 90d289f9468c5ac642e26edf84e747eeb8aa6e04 Mon Sep 17 00:00:00 2001 From: Delgan <delgan.py@gmail.com> Date: Mon, 13 Apr 2020 15:55:18 +0200 Subject: [PATCH 153/411] Fix failing tests? --- Emby.Naming/Common/NamingOptions.cs | 1 + tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index caff7b7dae..2c8a554d83 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -136,6 +136,7 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*", @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" }; diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index a2ef2dcd64..3ec914e489 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -39,12 +39,12 @@ namespace Jellyfin.Naming.Tests.Video [InlineData(@"[rec].mkv", "[rec].mkv", null)] [InlineData(@"St. Vincent (2014)", "St. Vincent", 2014)] [InlineData("Super movie(2009).mp4", "Super movie", 2009)] - // FIXME: [InlineData("Drug War 2013.mp4", "Drug War", 2013)] + [InlineData("Drug War 2013.mp4", "Drug War", 2013)] [InlineData("My Movie (1997) - GreatestReleaseGroup 2019.mp4", "My Movie", 1997)] - // FIXME: [InlineData("First Man 2018 1080p.mkv", "First Man", 2018)] + [InlineData("First Man 2018 1080p.mkv", "First Man", 2018)] [InlineData("First Man (2018) 1080p.mkv", "First Man", 2018)] - // FIXME: [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)] - // FIXME: [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)] + [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)] + [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 public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) { From be6cc9644f8c5ee4baa632ce53f8a7af5070140b Mon Sep 17 00:00:00 2001 From: Delgan <delgan.py@gmail.com> Date: Mon, 13 Apr 2020 16:11:02 +0200 Subject: [PATCH 154/411] Another iteration --- Emby.Naming/Common/NamingOptions.cs | 2 +- tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 2c8a554d83..a2d75d0b8d 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -137,7 +137,7 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*", - @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" + @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" }; CleanStrings = new[] diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index 3ec914e489..49cb2387bb 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("First Man 2018 1080p.mkv", "First Man", 2018)] [InlineData("First Man (2018) 1080p.mkv", "First Man", 2018)] [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)] - [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)] + // 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 public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) { From 6d35dd6b326b98995e363c64083a2ca46b2582fd Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 13:13:48 -0400 Subject: [PATCH 155/411] Clean up SecurityException - Remove unused SecurityExceptionType - Add missing constructor for InnerException - Add missing documentation --- .../HttpServer/Security/AuthService.cs | 30 ++++------------- MediaBrowser.Api/UserService.cs | 2 +- .../Net/SecurityException.cs | 32 ++++++++++++++----- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 58421aaf19..1360a5e0ce 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -108,18 +108,12 @@ namespace Emby.Server.Implementations.HttpServer.Security { if (user.Policy.IsDisabled) { - throw new SecurityException("User account has been disabled.") - { - SecurityExceptionType = SecurityExceptionType.Unauthenticated - }; + throw new SecurityException("User account has been disabled."); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(request.RemoteIp)) { - throw new SecurityException("User account has been disabled.") - { - SecurityExceptionType = SecurityExceptionType.Unauthenticated - }; + throw new SecurityException("User account has been disabled."); } if (!user.Policy.IsAdministrator @@ -128,10 +122,7 @@ namespace Emby.Server.Implementations.HttpServer.Security { request.Response.Headers.Add("X-Application-Error-Code", "ParentalControl"); - throw new SecurityException("This user account is not allowed access at this time.") - { - SecurityExceptionType = SecurityExceptionType.ParentalControl - }; + throw new SecurityException("This user account is not allowed access at this time."); } } @@ -190,10 +181,7 @@ namespace Emby.Server.Implementations.HttpServer.Security { if (user == null || !user.Policy.IsAdministrator) { - throw new SecurityException("User does not have admin access.") - { - SecurityExceptionType = SecurityExceptionType.Unauthenticated - }; + throw new SecurityException("User does not have admin access."); } } @@ -201,10 +189,7 @@ namespace Emby.Server.Implementations.HttpServer.Security { if (user == null || !user.Policy.EnableContentDeletion) { - throw new SecurityException("User does not have delete access.") - { - SecurityExceptionType = SecurityExceptionType.Unauthenticated - }; + throw new SecurityException("User does not have delete access."); } } @@ -212,10 +197,7 @@ namespace Emby.Server.Implementations.HttpServer.Security { if (user == null || !user.Policy.EnableContentDownloading) { - throw new SecurityException("User does not have download access.") - { - SecurityExceptionType = SecurityExceptionType.Unauthenticated - }; + throw new SecurityException("User does not have download access."); } } } diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 4015143497..78fc6c6941 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -426,7 +426,7 @@ namespace MediaBrowser.Api catch (SecurityException e) { // rethrow adding IP address to message - throw new SecurityException($"[{Request.RemoteIp}] {e.Message}"); + throw new SecurityException($"[{Request.RemoteIp}] {e.Message}", e); } } diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs index 3ccecf0eb8..a5b94ea5e3 100644 --- a/MediaBrowser.Controller/Net/SecurityException.cs +++ b/MediaBrowser.Controller/Net/SecurityException.cs @@ -2,20 +2,36 @@ using System; namespace MediaBrowser.Controller.Net { + /// <summary> + /// The exception that is thrown when a user is authenticated, but not authorized to access a requested resource. + /// </summary> public class SecurityException : Exception { + /// <summary> + /// Initializes a new instance of the <see cref="SecurityException"/> class. + /// </summary> + public SecurityException() + : base() + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="SecurityException"/> class. + /// </summary> + /// <param name="message">The message that describes the error.</param> public SecurityException(string message) : base(message) { - } - public SecurityExceptionType SecurityExceptionType { get; set; } - } - - public enum SecurityExceptionType - { - Unauthenticated = 0, - ParentalControl = 1 + /// <summary> + /// Initializes a new instance of the <see cref="SecurityException"/> class. + /// </summary> + /// <param name="message">The message that describes the error</param> + /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param> + public SecurityException(string message, Exception innerException) + : base(message, innerException) + { + } } } From 53380689ad00f00efc0c1790f1d25d08c95d7f2d Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 13:17:46 -0400 Subject: [PATCH 156/411] Return correct status codes for authentication and authorization errors - Use AuthenticatonException to return 401 - Use SecurityException to return 403 - Update existing throws to throw the correct exception for the circumstance --- .../HttpServer/HttpListenerHost.cs | 5 ++++- .../HttpServer/Security/AuthService.cs | 7 ++++--- Emby.Server.Implementations/Library/UserManager.cs | 11 ++++------- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 5ae65a4e3d..f496ff1ba1 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -14,6 +14,7 @@ using Emby.Server.Implementations.Services; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Events; @@ -230,7 +231,8 @@ namespace Emby.Server.Implementations.HttpServer switch (ex) { case ArgumentException _: return 400; - case SecurityException _: return 401; + case AuthenticationException _: return 401; + case SecurityException _: return 403; case DirectoryNotFoundException _: case FileNotFoundException _: case ResourceNotFoundException _: return 404; @@ -550,6 +552,7 @@ namespace Emby.Server.Implementations.HttpServer || ex is IOException || ex is OperationCanceledException || ex is SecurityException + || ex is AuthenticationException || ex is FileNotFoundException; await ErrorHandler(ex, httpReq, !ignoreStackTrace, urlToLog).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 1360a5e0ce..256b24924e 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -2,6 +2,7 @@ using System; using System.Linq; +using System.Security.Authentication; using Emby.Server.Implementations.SocketSharp; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -68,7 +69,7 @@ namespace Emby.Server.Implementations.HttpServer.Security if (user == null && auth.UserId != Guid.Empty) { - throw new SecurityException("User with Id " + auth.UserId + " not found"); + throw new AuthenticationException("User with Id " + auth.UserId + " not found"); } if (user != null) @@ -212,14 +213,14 @@ namespace Emby.Server.Implementations.HttpServer.Security { if (string.IsNullOrEmpty(token)) { - throw new SecurityException("Access token is required."); + throw new AuthenticationException("Access token is required."); } var info = GetTokenInfo(request); if (info == null) { - throw new SecurityException("Access token is invalid or expired."); + throw new AuthenticationException("Access token is invalid or expired."); } //if (!string.IsNullOrEmpty(info.UserId)) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 7b17cc913f..f92cb6ae6d 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -20,6 +20,7 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; @@ -324,21 +325,17 @@ namespace Emby.Server.Implementations.Library if (user.Policy.IsDisabled) { - throw new AuthenticationException( - string.Format( - CultureInfo.InvariantCulture, - "The {0} account is currently disabled. Please consult with your administrator.", - user.Name)); + throw new SecurityException($"The {user.Name} account is currently disabled. Please consult with your administrator."); } if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint)) { - throw new AuthenticationException("Forbidden."); + throw new SecurityException("Forbidden."); } if (!user.IsParentalScheduleAllowed()) { - throw new AuthenticationException("User is not allowed access at this time."); + throw new SecurityException("User is not allowed access at this time."); } // Update LastActivityDate and LastLoginDate, then save diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index de768333d8..c93c02c480 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1414,7 +1414,7 @@ namespace Emby.Server.Implementations.Session if (user == null) { AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request)); - throw new SecurityException("Invalid username or password entered."); + throw new AuthenticationException("Invalid username or password entered."); } if (!string.IsNullOrEmpty(request.DeviceId) From 98044e77935afdd4ff00a65d43b17d285fea82c7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 13:18:12 -0400 Subject: [PATCH 157/411] Document AuthenticationException correctly --- .../Authentication/AuthenticationException.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs index 62eca3ea9f..28209f6391 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationException.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -7,23 +7,29 @@ namespace MediaBrowser.Controller.Authentication /// </summary> public class AuthenticationException : Exception { - /// <inheritdoc /> + /// <summary> + /// Initializes a new instance of the <see cref="AuthenticationException"/> class. + /// </summary> public AuthenticationException() : base() { - } - /// <inheritdoc /> + /// <summary> + /// Initializes a new instance of the <see cref="AuthenticationException"/> class. + /// </summary> + /// <param name="message">The message that describes the error.</param> public AuthenticationException(string message) : base(message) { - } - /// <inheritdoc /> + /// <summary> + /// Initializes a new instance of the <see cref="AuthenticationException"/> class. + /// </summary> + /// <param name="message">The message that describes the error</param> + /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param> public AuthenticationException(string message, Exception innerException) : base(message, innerException) { - } } } From 9c7b3850f98633570cfb426e020153363921ce40 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 14:55:24 -0400 Subject: [PATCH 158/411] Throw AuthenticationException instead of ArgumentNullException when a user does not exist --- .../Library/DefaultAuthenticationProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index ab036eca7a..52c8facc3e 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Library { if (resolvedUser == null) { - throw new ArgumentNullException(nameof(resolvedUser)); + throw new AuthenticationException($"Specified user does not exist."); } bool success = false; From a8c3951c1798ced5c10631da7d9ca64731a12f26 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 15:26:49 -0400 Subject: [PATCH 159/411] Only show developer exception page for 500 server exceptions Other response codes should be returned as normal --- .../HttpServer/HttpListenerHost.cs | 89 ++++++++++--------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index f496ff1ba1..4c69b35e2a 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -241,40 +241,38 @@ namespace Emby.Server.Implementations.HttpServer } } - private async Task ErrorHandler(Exception ex, IRequest httpReq, bool logExceptionStackTrace, string urlToLog) + private async Task ErrorHandler(Exception ex, IRequest httpReq, int statusCode, string urlToLog) { - try - { - ex = GetActualException(ex); - - if (logExceptionStackTrace) - { - _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog); - } - else - { - _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog); - } + bool ignoreStackTrace = + ex is SocketException + || ex is IOException + || ex is OperationCanceledException + || ex is SecurityException + || ex is AuthenticationException + || ex is FileNotFoundException; - var httpRes = httpReq.Response; - - if (httpRes.HasStarted) - { - return; - } + if (ignoreStackTrace) + { + _logger.LogError("Error processing request: {Message}. URL: {Url}", ex.Message.TrimEnd('.'), urlToLog); + } + else + { + _logger.LogError(ex, "Error processing request. URL: {Url}", urlToLog); + } - var statusCode = GetStatusCode(ex); - httpRes.StatusCode = statusCode; + var httpRes = httpReq.Response; - var errContent = NormalizeExceptionMessage(ex.Message); - httpRes.ContentType = "text/plain"; - httpRes.ContentLength = errContent.Length; - await httpRes.WriteAsync(errContent).ConfigureAwait(false); - } - catch (Exception errorEx) + if (httpRes.HasStarted) { - _logger.LogError(errorEx, "Error this.ProcessRequest(context)(Exception while writing error to the response). URL: {Url}", urlToLog); + return; } + + httpRes.StatusCode = statusCode; + + var errContent = NormalizeExceptionMessage(ex.Message); + httpRes.ContentType = "text/plain"; + httpRes.ContentLength = errContent.Length; + await httpRes.WriteAsync(errContent).ConfigureAwait(false); } private string NormalizeExceptionMessage(string msg) @@ -538,23 +536,32 @@ namespace Emby.Server.Implementations.HttpServer throw new FileNotFoundException(); } } - catch (Exception ex) + catch (Exception requestEx) { - // Do not handle exceptions manually when in development mode - // The framework-defined development exception page will be returned instead - if (_hostEnvironment.IsDevelopment()) + try { - throw; + var requestInnerEx = GetActualException(requestEx); + var statusCode = GetStatusCode(requestInnerEx); + + // 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()) + { + throw; + } + + await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false); } + catch (Exception handlerException) + { + var aggregateEx = new AggregateException("Error while handling request exception", requestEx, handlerException); + _logger.LogError(aggregateEx, "Error while handling exception in response to {Url}", urlToLog); - bool ignoreStackTrace = - ex is SocketException - || ex is IOException - || ex is OperationCanceledException - || ex is SecurityException - || ex is AuthenticationException - || ex is FileNotFoundException; - await ErrorHandler(ex, httpReq, !ignoreStackTrace, urlToLog).ConfigureAwait(false); + if (_hostEnvironment.IsDevelopment()) + { + throw aggregateEx; + } + } } finally { From 8b4b4b4127945354731036e13ca0b5366134958d Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 13 Apr 2020 16:10:55 -0400 Subject: [PATCH 160/411] Do not return the exception message to the client for AuthenticationExceptions --- .../HttpServer/HttpListenerHost.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 4c69b35e2a..211a0c1d99 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -269,25 +269,24 @@ namespace Emby.Server.Implementations.HttpServer httpRes.StatusCode = statusCode; - var errContent = NormalizeExceptionMessage(ex.Message); + var errContent = NormalizeExceptionMessage(ex) ?? string.Empty; httpRes.ContentType = "text/plain"; httpRes.ContentLength = errContent.Length; await httpRes.WriteAsync(errContent).ConfigureAwait(false); } - private string NormalizeExceptionMessage(string msg) + private string NormalizeExceptionMessage(Exception ex) { - if (msg == null) + // Do not expose the exception message for AuthenticationException + if (ex is AuthenticationException) { - return string.Empty; + return null; } // Strip any information we don't want to reveal - - msg = msg.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase); - msg = msg.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase); - - return msg; + return ex.Message + ?.Replace(_config.ApplicationPaths.ProgramSystemPath, string.Empty, StringComparison.OrdinalIgnoreCase) + .Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase); } /// <summary> From 9df49cc7961e9442e888f97f9d5cc2c3af706809 Mon Sep 17 00:00:00 2001 From: Vasily <just.one.man@yandex.ru> Date: Tue, 14 Apr 2020 01:52:43 +0300 Subject: [PATCH 161/411] Make Last-Modified and If-Modified-Since headers follow the spec --- .../HttpServer/HttpResultFactory.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index b42662420b..3b1563fdda 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -28,6 +28,12 @@ namespace Emby.Server.Implementations.HttpServer /// </summary> public class HttpResultFactory : IHttpResultFactory { + // Last-Modified and If-Modified-Since must follow strict date format, + // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since + private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\""; + // We specifically use en-US culture because both day of week and month names must be in it9 + private static readonly CultureInfo _enUSculture = CultureInfo.CreateSpecificCulture("en-US"); + /// <summary> /// The logger. /// </summary> @@ -420,7 +426,11 @@ namespace Emby.Server.Implementations.HttpServer if (!noCache) { - DateTime.TryParse(requestContext.Headers[HeaderNames.IfModifiedSince], out var ifModifiedSinceHeader); + if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal, out var ifModifiedSinceHeader)) + { + _logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]); + return null; + } if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified)) { @@ -629,7 +639,7 @@ namespace Emby.Server.Implementations.HttpServer if (lastModifiedDate.HasValue) { - responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToString(CultureInfo.InvariantCulture); + responseHeaders[HeaderNames.LastModified] = lastModifiedDate.Value.ToUniversalTime().ToString(HttpDateFormat, _enUSculture); } } From 9c679b657045734eef9cbd1c2160602592a30b41 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 14:45:57 -0400 Subject: [PATCH 162/411] Clean up and document ActivityLogEntryPoint.cs --- .../Activity/ActivityLogEntryPoint.cs | 75 +++++++------------ 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index d900520b2a..e025417d13 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -27,6 +25,10 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { + /// <summary> + /// The activity log entry point. + /// <see cref="IServerEntryPoint"/>. + /// </summary> public sealed class ActivityLogEntryPoint : IServerEntryPoint { private readonly ILogger _logger; @@ -42,16 +44,15 @@ namespace Emby.Server.Implementations.Activity /// <summary> /// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class. /// </summary> - /// <param name="logger"></param> - /// <param name="sessionManager"></param> - /// <param name="deviceManager"></param> - /// <param name="taskManager"></param> - /// <param name="activityManager"></param> - /// <param name="localization"></param> - /// <param name="installationManager"></param> - /// <param name="subManager"></param> - /// <param name="userManager"></param> - /// <param name="appHost"></param> + /// <param name="logger">The logger.</param> + /// <param name="sessionManager">The session manager.</param> + /// <param name="deviceManager">The device manager.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="activityManager">The activity manager.</param> + /// <param name="localization">The localization manager.</param> + /// <param name="installationManager">The installation manager.</param> + /// <param name="subManager">The subtitle manager.</param> + /// <param name="userManager">The user manager.</param> public ActivityLogEntryPoint( ILogger<ActivityLogEntryPoint> logger, ISessionManager sessionManager, @@ -74,6 +75,7 @@ namespace Emby.Server.Implementations.Activity _userManager = userManager; } + /// <inheritdoc /> public Task RunAsync() { _taskManager.TaskCompleted += OnTaskCompleted; @@ -136,7 +138,7 @@ namespace Emby.Server.Implementations.Activity CultureInfo.InvariantCulture, _localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, - Emby.Notifications.NotificationEntryPoint.GetItemName(e.Item)), + Notifications.NotificationEntryPoint.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture), ShortOverview = e.Exception.Message @@ -259,31 +261,20 @@ namespace Emby.Server.Implementations.Activity private void OnSessionEnded(object sender, SessionEventArgs e) { - string name; var session = e.SessionInfo; if (string.IsNullOrEmpty(session.UserName)) { - name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("DeviceOfflineWithName"), - session.DeviceName); - - // Causing too much spam for now return; } - else + + CreateLogEntry(new ActivityLogEntry { - name = string.Format( + Name = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, - session.DeviceName); - } - - CreateLogEntry(new ActivityLogEntry - { - Name = name, + session.DeviceName), Type = "SessionEnded", ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -382,31 +373,20 @@ namespace Emby.Server.Implementations.Activity private void OnSessionStarted(object sender, SessionEventArgs e) { - string name; var session = e.SessionInfo; if (string.IsNullOrEmpty(session.UserName)) { - name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("DeviceOnlineWithName"), - session.DeviceName); - - // Causing too much spam for now return; } - else + + CreateLogEntry(new ActivityLogEntry { - name = string.Format( + Name = string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, - session.DeviceName); - } - - CreateLogEntry(new ActivityLogEntry - { - Name = name, + session.DeviceName), Type = "SessionStarted", ShortOverview = string.Format( CultureInfo.InvariantCulture, @@ -485,8 +465,7 @@ namespace Emby.Server.Implementations.Activity var result = e.Result; var task = e.Task; - var activityTask = task.ScheduledTask as IConfigurableScheduledTask; - if (activityTask != null && !activityTask.IsLogged) + if (task.ScheduledTask is IConfigurableScheduledTask activityTask && !activityTask.IsLogged) { return; } @@ -560,6 +539,8 @@ namespace Emby.Server.Implementations.Activity /// <summary> /// Constructs a user-friendly string for this TimeSpan instance. /// </summary> + /// <param name="span">The timespan.</param> + /// <returns>The user-friendly string.</returns> public static string ToUserFriendlyString(TimeSpan span) { const int DaysInYear = 365; @@ -574,7 +555,7 @@ namespace Emby.Server.Implementations.Activity { int years = days / DaysInYear; values.Add(CreateValueString(years, "year")); - days = days % DaysInYear; + days %= DaysInYear; } // Number of months @@ -582,7 +563,7 @@ namespace Emby.Server.Implementations.Activity { int months = days / DaysInMonth; values.Add(CreateValueString(months, "month")); - days = days % DaysInMonth; + days %= DaysInMonth; } // Number of days From af4d617df22301d2740f1286727280bc1865f889 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 14:50:19 -0400 Subject: [PATCH 163/411] Clean up and document ActivityManager.cs --- .../Activity/ActivityManager.cs | 16 ++++++++++------ Emby.Server.Implementations/ApplicationHost.cs | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index ee10845cfa..2d2dc8e61e 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -1,32 +1,34 @@ -#pragma warning disable CS1591 - using System; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; -using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Activity { + /// <inheritdoc /> public class ActivityManager : IActivityManager { + /// <inheritdoc /> public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; private readonly IActivityRepository _repo; - private readonly ILogger _logger; private readonly IUserManager _userManager; + /// <summary> + /// Initializes a new instance of the <see cref="ActivityManager"/> class. + /// </summary> + /// <param name="repo">The activity repository.</param> + /// <param name="userManager">The user manager.</param> public ActivityManager( - ILoggerFactory loggerFactory, IActivityRepository repo, IUserManager userManager) { - _logger = loggerFactory.CreateLogger(nameof(ActivityManager)); _repo = repo; _userManager = userManager; } + /// <inheritdoc /> public void Create(ActivityLogEntry entry) { entry.Date = DateTime.UtcNow; @@ -36,6 +38,7 @@ namespace Emby.Server.Implementations.Activity EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry)); } + /// <inheritdoc /> public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit); @@ -59,6 +62,7 @@ namespace Emby.Server.Implementations.Activity return result; } + /// <inheritdoc /> public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) { return GetActivityLogEntries(minDate, null, startIndex, limit); diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 81a80ddb26..7e80900f49 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -833,7 +833,7 @@ namespace Emby.Server.Implementations var activityLogRepo = GetActivityLogRepository(); serviceCollection.AddSingleton(activityLogRepo); - serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager)); + serviceCollection.AddSingleton<IActivityManager>(new ActivityManager(activityLogRepo, UserManager)); var authContext = new AuthorizationContext(AuthenticationRepository, UserManager); serviceCollection.AddSingleton<IAuthorizationContext>(authContext); From f6899de33850ddd6ccfc4262bc6c2fd836bfc32d Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:04:54 -0400 Subject: [PATCH 164/411] Clean up and document ActivityRepository.cs --- .../Activity/ActivityRepository.cs | 180 +++++++++--------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 7be72319ea..697ad7fcc0 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -15,11 +13,18 @@ using SQLitePCL.pretty; namespace Emby.Server.Implementations.Activity { + /// <inheritdoc cref="IActivityRepository" /> public class ActivityRepository : BaseSqliteRepository, IActivityRepository { private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); private readonly IFileSystem _fileSystem; + /// <summary> + /// Initializes a new instance of the <see cref="ActivityRepository"/> class. + /// </summary> + /// <param name="loggerFactory">The logger factory.</param> + /// <param name="appPaths">The server application paths.</param> + /// <param name="fileSystem">The filesystem.</param> public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem) : base(loggerFactory.CreateLogger(nameof(ActivityRepository))) { @@ -27,6 +32,9 @@ namespace Emby.Server.Implementations.Activity _fileSystem = fileSystem; } + /// <summary> + /// Initializes the <see cref="ActivityRepository"/>. + /// </summary> public void Initialize() { try @@ -45,16 +53,14 @@ namespace Emby.Server.Implementations.Activity private void InitializeInternal() { - using (var connection = GetConnection()) + using var connection = GetConnection(); + connection.RunQueries(new[] { - 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" - }); + "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); - } + TryMigrate(connection); } private void TryMigrate(ManagedConnection connection) @@ -78,6 +84,7 @@ namespace Emby.Server.Implementations.Activity private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; + /// <inheritdoc /> public void Create(ActivityLogEntry entry) { if (entry == null) @@ -85,37 +92,38 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (var connection = GetConnection()) + using var connection = GetConnection(); + connection.RunInTransaction(db => { - 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); + 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); + 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)); - } + 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.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); + statement.TryBind("@LogSeverity", entry.Severity.ToString()); - statement.MoveNext(); - } - }, TransactionMode); - } + statement.MoveNext(); + }, TransactionMode); } + /// <summary> + /// Adds the provided <see cref="ActivityLogEntry"/> to this repository. + /// </summary> + /// <param name="entry">The activity log entry.</param> + /// <exception cref="ArgumentNullException">If entry is null.</exception> public void Update(ActivityLogEntry entry) { if (entry == null) @@ -123,38 +131,35 @@ namespace Emby.Server.Implementations.Activity throw new ArgumentNullException(nameof(entry)); } - using (var connection = GetConnection()) + using var connection = GetConnection(); + connection.RunInTransaction(db => { - 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); + 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); + 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)); - } + 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.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); + statement.TryBind("@LogSeverity", entry.Severity.ToString()); - statement.MoveNext(); - } - }, TransactionMode); - } + statement.MoveNext(); + }, TransactionMode); } + /// <inheritdoc /> public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { var commandText = BaseActivitySelectText; @@ -164,16 +169,10 @@ namespace Emby.Server.Implementations.Activity { whereClauses.Add("DateCreated>=@DateCreated"); } + if (hasUserId.HasValue) { - if (hasUserId.Value) - { - whereClauses.Add("UserId not null"); - } - else - { - whereClauses.Add("UserId is null"); - } + whereClauses.Add(hasUserId.Value ? "UserId not null" : "UserId is null"); } var whereTextWithoutPaging = whereClauses.Count == 0 ? @@ -216,38 +215,33 @@ namespace Emby.Server.Implementations.Activity var list = new List<ActivityLogEntry>(); var result = new QueryResult<ActivityLogEntry>(); - using (var connection = GetConnection(true)) - { - connection.RunInTransaction( - db => - { - var statements = PrepareAll(db, statementTexts).ToList(); + using var connection = GetConnection(true); + connection.RunInTransaction( + db => + { + var statements = PrepareAll(db, statementTexts).ToList(); - using (var statement = statements[0]) + using (var statement = statements[0]) + { + if (minDate.HasValue) { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } - - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetEntry(row)); - } + statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); } - using (var statement = statements[1]) - { - if (minDate.HasValue) - { - statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); - } + list.AddRange(statement.ExecuteQuery().Select(GetEntry)); + } - result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); + using (var statement = statements[1]) + { + if (minDate.HasValue) + { + statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue()); } - }, - ReadTransactionMode); - } + + result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); + } + }, + ReadTransactionMode); result.Items = list; return result; From 0e8f30f64b9d9f88d2593d97289ed055b7bfbd0d Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:08:20 -0400 Subject: [PATCH 165/411] Documented BaseApplicationPath constructor parameters --- Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index bc47817438..2adc1d6c34 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -15,6 +15,11 @@ namespace Emby.Server.Implementations.AppBase /// <summary> /// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class. /// </summary> + /// <param name="programDataPath">The program data path.</param> + /// <param name="logDirectoryPath">The log directory path.</param> + /// <param name="configurationDirectoryPath">The configuration directory path.</param> + /// <param name="cacheDirectoryPath">The cache directory path.</param> + /// <param name="webDirectoryPath">The web directory path.</param> protected BaseApplicationPaths( string programDataPath, string logDirectoryPath, From 9cec01d8ce610fcff99a901d6685668abc96106d Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:11:21 -0400 Subject: [PATCH 166/411] Switch to using declaration --- .../AppBase/ConfigurationHelper.cs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs index 854d7b4cbf..0b681fddfc 100644 --- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs +++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs @@ -36,24 +36,22 @@ namespace Emby.Server.Implementations.AppBase configuration = Activator.CreateInstance(type); } - using (var stream = new MemoryStream()) - { - xmlSerializer.SerializeToStream(configuration, stream); - - // Take the object we just got and serialize it back to bytes - var newBytes = stream.ToArray(); + using var stream = new MemoryStream(); + xmlSerializer.SerializeToStream(configuration, stream); - // If the file didn't exist before, or if something has changed, re-save - if (buffer == null || !buffer.SequenceEqual(newBytes)) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + // Take the object we just got and serialize it back to bytes + var newBytes = stream.ToArray(); - // Save it after load in case we got new items - File.WriteAllBytes(path, newBytes); - } + // If the file didn't exist before, or if something has changed, re-save + if (buffer == null || !buffer.SequenceEqual(newBytes)) + { + Directory.CreateDirectory(Path.GetDirectoryName(path)); - return configuration; + // Save it after load in case we got new items + File.WriteAllBytes(path, newBytes); } + + return configuration; } } } From 90e256416942d14a4c765cfcb48e7c740fb2361c Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:16:04 -0400 Subject: [PATCH 167/411] Document and clean up ZipClient.cs --- .../Archiving/ZipClient.cs | 130 +++++++----------- 1 file changed, 53 insertions(+), 77 deletions(-) diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs index 4a6e5cfd75..591ae547d6 100644 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ b/Emby.Server.Implementations/Archiving/ZipClient.cs @@ -22,10 +22,8 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles) { - using (var fileStream = File.OpenRead(sourceFile)) - { - ExtractAll(fileStream, targetPath, overwriteExistingFiles); - } + using var fileStream = File.OpenRead(sourceFile); + ExtractAll(fileStream, targetPath, overwriteExistingFiles); } /// <summary> @@ -36,67 +34,61 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles) { - using (var reader = ReaderFactory.Open(source)) + using var reader = ReaderFactory.Open(source); + var options = new ExtractionOptions { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; - - if (overwriteExistingFiles) - { - options.Overwrite = true; - } + ExtractFullPath = true + }; - reader.WriteAllToDirectory(targetPath, options); + if (overwriteExistingFiles) + { + options.Overwrite = true; } + + reader.WriteAllToDirectory(targetPath, options); } + /// <inheritdoc /> public void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles) { - using (var reader = ZipReader.Open(source)) + using var reader = ZipReader.Open(source); + var options = new ExtractionOptions { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; + ExtractFullPath = true, + Overwrite = overwriteExistingFiles + }; - if (overwriteExistingFiles) - { - options.Overwrite = true; - } - - reader.WriteAllToDirectory(targetPath, options); - } + reader.WriteAllToDirectory(targetPath, options); } + /// <inheritdoc /> public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles) { - using (var reader = GZipReader.Open(source)) + using var reader = GZipReader.Open(source); + var options = new ExtractionOptions { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; + ExtractFullPath = true, + Overwrite = overwriteExistingFiles + }; - if (overwriteExistingFiles) - { - options.Overwrite = true; - } - - reader.WriteAllToDirectory(targetPath, options); - } + reader.WriteAllToDirectory(targetPath, options); } + /// <inheritdoc /> public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName) { - using (var reader = GZipReader.Open(source)) + using var reader = GZipReader.Open(source); + if (reader.MoveToNextEntry()) { - if (reader.MoveToNextEntry()) + var entry = reader.Entry; + + var filename = entry.Key; + if (string.IsNullOrWhiteSpace(filename)) { - var entry = reader.Entry; - - var filename = entry.Key; - if (string.IsNullOrWhiteSpace(filename)) - { - filename = defaultFileName; - } - reader.WriteEntryToFile(Path.Combine(targetPath, filename)); + filename = defaultFileName; } + + reader.WriteEntryToFile(Path.Combine(targetPath, filename)); } } @@ -108,10 +100,8 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles) { - using (var fileStream = File.OpenRead(sourceFile)) - { - ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles); - } + using var fileStream = File.OpenRead(sourceFile); + ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles); } /// <summary> @@ -122,21 +112,15 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles) { - using (var archive = SevenZipArchive.Open(source)) + using var archive = SevenZipArchive.Open(source); + using var reader = archive.ExtractAllEntries(); + var options = new ExtractionOptions { - using (var reader = archive.ExtractAllEntries()) - { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; - - if (overwriteExistingFiles) - { - options.Overwrite = true; - } + ExtractFullPath = true, + Overwrite = overwriteExistingFiles + }; - reader.WriteAllToDirectory(targetPath, options); - } - } + reader.WriteAllToDirectory(targetPath, options); } /// <summary> @@ -147,10 +131,8 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles) { - using (var fileStream = File.OpenRead(sourceFile)) - { - ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles); - } + using var fileStream = File.OpenRead(sourceFile); + ExtractAllFromTar(fileStream, targetPath, overwriteExistingFiles); } /// <summary> @@ -161,21 +143,15 @@ namespace Emby.Server.Implementations.Archiving /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> public void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles) { - using (var archive = TarArchive.Open(source)) + using var archive = TarArchive.Open(source); + using var reader = archive.ExtractAllEntries(); + var options = new ExtractionOptions { - using (var reader = archive.ExtractAllEntries()) - { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; + ExtractFullPath = true, + Overwrite = overwriteExistingFiles + }; - if (overwriteExistingFiles) - { - options.Overwrite = true; - } - - reader.WriteAllToDirectory(targetPath, options); - } - } + reader.WriteAllToDirectory(targetPath, options); } } } From 4c0547f90c0fa1cd8a931de630899e68933e21c7 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:19:11 -0400 Subject: [PATCH 168/411] Document BrandingConfigurationFactory.cs --- .../Branding/BrandingConfigurationFactory.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs index 93000ae127..43c8cd5fa2 100644 --- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs +++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs @@ -1,13 +1,15 @@ -#pragma warning disable CS1591 - using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Branding; namespace Emby.Server.Implementations.Branding { + /// <summary> + /// Branding configuration factory. + /// </summary> public class BrandingConfigurationFactory : IConfigurationFactory { + /// <inheritdoc /> public IEnumerable<ConfigurationStore> GetConfigurations() { return new[] From 543c76a8f10f7d55b4e0d2c0ed848f40d35debda Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:28:02 -0400 Subject: [PATCH 169/411] Clean up and document ChannelDynamicMediaSourceProvider.cs --- .../Channels/ChannelDynamicMediaSourceProvider.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 6016fed079..c677e9fbc3 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Threading; @@ -11,6 +9,9 @@ using MediaBrowser.Model.Dto; namespace Emby.Server.Implementations.Channels { + /// <summary> + /// A media source provider for channels. + /// </summary> public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider { private readonly ChannelManager _channelManager; @@ -27,12 +28,9 @@ namespace Emby.Server.Implementations.Channels /// <inheritdoc /> public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken) { - if (item.SourceType == SourceType.Channel) - { - return _channelManager.GetDynamicMediaSources(item, cancellationToken); - } - - return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); + return item.SourceType == SourceType.Channel + ? _channelManager.GetDynamicMediaSources(item, cancellationToken) + : Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); } /// <inheritdoc /> From 2c920cff33f8e765b79e805d96f3bc547bc7b3fc Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:29:57 -0400 Subject: [PATCH 170/411] Document ChannelImageProvider.cs --- .../Channels/ChannelImageProvider.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs index 62aeb9bcb9..c08a237fb4 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System.Collections.Generic; using System.Linq; using System.Threading; @@ -11,20 +9,29 @@ using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Channels { + /// <summary> + /// An image provider for channels. + /// </summary> public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor { private readonly IChannelManager _channelManager; + /// <summary> + /// Initializes a new instance of the <see cref="ChannelImageProvider"/> class. + /// </summary> + /// <param name="channelManager">The channel manager.</param> public ChannelImageProvider(IChannelManager channelManager) { _channelManager = channelManager; } + /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { return GetChannel(item).GetSupportedChannelImages(); } + /// <inheritdoc /> public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken) { var channel = GetChannel(item); @@ -32,8 +39,10 @@ namespace Emby.Server.Implementations.Channels return channel.GetChannelImage(type, cancellationToken); } + /// <inheritdoc /> public string Name => "Channel Image Provider"; + /// <inheritdoc /> public bool Supports(BaseItem item) { return item is Channel; @@ -46,6 +55,7 @@ namespace Emby.Server.Implementations.Channels return ((ChannelManager)_channelManager).GetChannelProvider(channel); } + /// <inheritdoc /> public bool HasChanged(BaseItem item, IDirectoryService directoryService) { return GetSupportedImages(item).Any(i => !item.HasImage(i)); From c8e26b6d46f98ab04041064a0e7db2e731360db8 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:36:29 -0400 Subject: [PATCH 171/411] Document ChannelPostScanTask.cs --- .../Channels/ChannelPostScanTask.cs | 22 ++++++++++++++----- .../Channels/RefreshChannelsScheduledTask.cs | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index 266d539d0a..f48b0a7faa 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Linq; using System.Threading; @@ -11,21 +9,35 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Channels { + /// <summary> + /// Channel post scan task. + /// This task removes all non-installed channels from the database. + /// </summary> public class ChannelPostScanTask { private readonly IChannelManager _channelManager; - private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager) + /// <summary> + /// Initializes a new instance of the <see cref="ChannelPostScanTask"/> class. + /// </summary> + /// <param name="channelManager">The channel manager.</param> + /// <param name="logger">The logger.</param> + /// <param name="libraryManager">The library manager.</param> + public ChannelPostScanTask(IChannelManager channelManager, ILogger logger, ILibraryManager libraryManager) { _channelManager = channelManager; - _userManager = userManager; _logger = logger; _libraryManager = libraryManager; } + /// <summary> + /// Runs this task. + /// </summary> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The completed task.</returns> public Task Run(IProgress<double> progress, CancellationToken cancellationToken) { CleanDatabase(cancellationToken); diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 367efcb134..36cec75ec8 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Channels await manager.RefreshChannels(new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false); - await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken) + await new ChannelPostScanTask(_channelManager, _logger, _libraryManager).Run(progress, cancellationToken) .ConfigureAwait(false); } From f29e6badb3d6350fc828ac58dc645dc7b166c376 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:48:06 -0400 Subject: [PATCH 172/411] Remove unused field and documented RefreshChannelsScheduledTask.cs --- .../Channels/RefreshChannelsScheduledTask.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 36cec75ec8..54b621e250 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Threading; @@ -7,29 +5,36 @@ using System.Threading.Tasks; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Globalization; namespace Emby.Server.Implementations.Channels { + /// <summary> + /// The "Refresh Channels" scheduled task. + /// </summary> public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask { private readonly IChannelManager _channelManager; - private readonly IUserManager _userManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; + /// <summary> + /// Initializes a new instance of the <see cref="RefreshChannelsScheduledTask"/> class. + /// </summary> + /// <param name="channelManager">The channel manager.</param> + /// <param name="logger">The logger.</param> + /// <param name="libraryManager">The library manager.</param> + /// <param name="localization">The localization manager.</param> public RefreshChannelsScheduledTask( IChannelManager channelManager, - IUserManager userManager, ILogger<RefreshChannelsScheduledTask> logger, ILibraryManager libraryManager, ILocalizationManager localization) { _channelManager = channelManager; - _userManager = userManager; _logger = logger; _libraryManager = libraryManager; _localization = localization; @@ -72,7 +77,6 @@ namespace Emby.Server.Implementations.Channels { return new[] { - // Every so often new TaskTriggerInfo { From 77df0c943b342b4b8642809fc6dbfbd6e0e8d5a7 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:50:48 -0400 Subject: [PATCH 173/411] Clean up and document CollectionImageProvider.cs --- .../Collections/CollectionImageProvider.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 21ba0288ec..4d9f865e06 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System.Collections.Generic; using System.Linq; using Emby.Server.Implementations.Images; @@ -15,8 +13,18 @@ using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Collections { + /// <summary> + /// A collection image provider. + /// </summary> public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet> { + /// <summary> + /// Initializes a new instance of the <see cref="CollectionImageProvider"/> class. + /// </summary> + /// <param name="fileSystem">The filesystem.</param> + /// <param name="providerManager">The provider manager.</param> + /// <param name="applicationPaths">The application paths.</param> + /// <param name="imageProcessor">The image processor.</param> public CollectionImageProvider( IFileSystem fileSystem, IProviderManager providerManager, @@ -48,13 +56,10 @@ namespace Emby.Server.Implementations.Collections var episode = subItem as Episode; - if (episode != null) + var series = episode?.Series; + if (series != null && series.HasImage(ImageType.Primary)) { - var series = episode.Series; - if (series != null && series.HasImage(ImageType.Primary)) - { - return series; - } + return series; } if (subItem.HasImage(ImageType.Primary)) From ddd8120aabc0754660e3f282408523932b61dd77 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 15:57:05 -0400 Subject: [PATCH 174/411] Clean up and document CollectionManager.cs --- .../Collections/CollectionManager.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 3219528749..297e8327a5 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -23,6 +21,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Collections { + /// <inheritdoc /> public class CollectionManager : ICollectionManager { private readonly ILibraryManager _libraryManager; @@ -33,6 +32,16 @@ namespace Emby.Server.Implementations.Collections private readonly ILocalizationManager _localizationManager; private readonly IApplicationPaths _appPaths; + /// <summary> + /// Initializes a new instance of the <see cref="CollectionManager"/> class. + /// </summary> + /// <param name="libraryManager">The library manager.</param> + /// <param name="appPaths">The application paths.</param> + /// <param name="localizationManager">The localization manager.</param> + /// <param name="fileSystem">The filesystem.</param> + /// <param name="iLibraryMonitor">The library monitor.</param> + /// <param name="loggerFactory">The logger factory.</param> + /// <param name="providerManager">The provider manager.</param> public CollectionManager( ILibraryManager libraryManager, IApplicationPaths appPaths, @@ -51,8 +60,13 @@ namespace Emby.Server.Implementations.Collections _appPaths = appPaths; } + /// <inheritdoc /> public event EventHandler<CollectionCreatedEventArgs> CollectionCreated; + + /// <inheritdoc /> public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection; + + /// <inheritdoc /> public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection; private IEnumerable<Folder> FindFolders(string path) @@ -114,6 +128,7 @@ namespace Emby.Server.Implementations.Collections folder.GetChildren(user, true).OfType<BoxSet>(); } + /// <inheritdoc /> public BoxSet CreateCollection(CollectionCreationOptions options) { var name = options.Name; @@ -178,11 +193,13 @@ namespace Emby.Server.Implementations.Collections } } + /// <inheritdoc /> public void AddToCollection(Guid collectionId, IEnumerable<string> ids) { AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); } + /// <inheritdoc /> public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids) { AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem))); @@ -246,11 +263,13 @@ namespace Emby.Server.Implementations.Collections } } + /// <inheritdoc /> public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds) { RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i))); } + /// <inheritdoc /> public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds) { var collection = _libraryManager.GetItemById(collectionId) as BoxSet; @@ -301,6 +320,7 @@ namespace Emby.Server.Implementations.Collections }); } + /// <inheritdoc /> public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user) { var results = new Dictionary<Guid, BaseItem>(); @@ -309,9 +329,7 @@ namespace Emby.Server.Implementations.Collections foreach (var item in items) { - var grouping = item as ISupportsBoxSetGrouping; - - if (grouping == null) + if (!(item is ISupportsBoxSetGrouping)) { results[item.Id] = item; } @@ -341,12 +359,21 @@ namespace Emby.Server.Implementations.Collections } } + /// <summary> + /// The collection manager entry point. + /// </summary> public sealed class CollectionManagerEntryPoint : IServerEntryPoint { private readonly CollectionManager _collectionManager; private readonly IServerConfigurationManager _config; private readonly ILogger _logger; + /// <summary> + /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class. + /// </summary> + /// <param name="collectionManager">The collection manager.</param> + /// <param name="config">The server configuration manager.</param> + /// <param name="logger">The logger.</param> public CollectionManagerEntryPoint( ICollectionManager collectionManager, IServerConfigurationManager config, From ecaae2c8de3bf03d3a74de865546dc6b14c06b12 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 16:01:21 -0400 Subject: [PATCH 175/411] Clean up and document ServerConfigurationManager.cs --- .../Configuration/ServerConfigurationManager.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index f407317ec7..0ff70decae 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -69,21 +69,16 @@ namespace Emby.Server.Implementations.Configuration /// </summary> private void UpdateMetadataPath() { - if (string.IsNullOrWhiteSpace(Configuration.MetadataPath)) - { - ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Path.Combine(ApplicationPaths.ProgramDataPath, "metadata"); - } - else - { - ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = Configuration.MetadataPath; - } + ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrWhiteSpace(Configuration.MetadataPath) + ? Path.Combine(ApplicationPaths.ProgramDataPath, "metadata") + : Configuration.MetadataPath; } /// <summary> /// Replaces the configuration. /// </summary> /// <param name="newConfiguration">The new configuration.</param> - /// <exception cref="DirectoryNotFoundException"></exception> + /// <exception cref="DirectoryNotFoundException">If the configuration path doesn't exist.</exception> public override void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration) { var newConfig = (ServerConfiguration)newConfiguration; From fd750a9c79a8bb39ecfab053315f39ea54d0f53b Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 16:13:41 -0400 Subject: [PATCH 176/411] Clean up and document CryptographyProvider.cs --- .../Cryptography/CryptographyProvider.cs | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs index de83b023d7..a037415a95 100644 --- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs @@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Cryptography private RandomNumberGenerator _randomNumberGenerator; - private bool _disposed = false; + private bool _disposed; /// <summary> /// Initializes a new instance of the <see cref="CryptographyProvider"/> class. @@ -56,15 +56,13 @@ namespace Emby.Server.Implementations.Cryptography { // downgrading for now as we need this library to be dotnetstandard compliant // with this downgrade we'll add a check to make sure we're on the downgrade method at the moment - if (method == DefaultHashMethod) + if (method != DefaultHashMethod) { - using (var r = new Rfc2898DeriveBytes(bytes, salt, iterations)) - { - return r.GetBytes(32); - } + throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}"); } - throw new CryptographicException($"Cannot currently use PBKDF2 with requested hash method: {method}"); + using var r = new Rfc2898DeriveBytes(bytes, salt, iterations); + return r.GetBytes(32); } /// <inheritdoc /> @@ -74,25 +72,22 @@ namespace Emby.Server.Implementations.Cryptography { return PBKDF2(hashMethod, bytes, salt, DefaultIterations); } - else if (_supportedHashMethods.Contains(hashMethod)) + + if (!_supportedHashMethods.Contains(hashMethod)) + { + throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); + } + + using var h = HashAlgorithm.Create(hashMethod); + if (salt.Length == 0) { - using (var h = HashAlgorithm.Create(hashMethod)) - { - if (salt.Length == 0) - { - return h.ComputeHash(bytes); - } - else - { - byte[] salted = new byte[bytes.Length + salt.Length]; - Array.Copy(bytes, salted, bytes.Length); - Array.Copy(salt, 0, salted, bytes.Length, salt.Length); - return h.ComputeHash(salted); - } - } + return h.ComputeHash(bytes); } - throw new CryptographicException($"Requested hash method is not supported: {hashMethod}"); + byte[] salted = new byte[bytes.Length + salt.Length]; + Array.Copy(bytes, salted, bytes.Length); + Array.Copy(salt, 0, salted, bytes.Length, salt.Length); + return h.ComputeHash(salted); } /// <inheritdoc /> From a54dca09d8c581bc2f64235d2d9a07278f5c02c4 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Tue, 14 Apr 2020 19:34:38 -0400 Subject: [PATCH 177/411] Added the last missing documentation --- .../Collections/CollectionImageProvider.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 4d9f865e06..c69a07e83e 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -34,6 +34,7 @@ namespace Emby.Server.Implementations.Collections { } + /// <inheritdoc /> protected override bool Supports(BaseItem item) { // Right now this is the only way to prevent this image from getting created ahead of internet image providers @@ -45,6 +46,7 @@ namespace Emby.Server.Implementations.Collections return base.Supports(item); } + /// <inheritdoc /> protected override IReadOnlyList<BaseItem> GetItemsWithImages(BaseItem item) { var playlist = (BoxSet)item; @@ -85,6 +87,7 @@ namespace Emby.Server.Implementations.Collections .ToList(); } + /// <inheritdoc /> protected override string CreateImage(BaseItem item, IReadOnlyCollection<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); From 72862d7b46a1656f7d798cf13e66f3cd6b820866 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Wed, 15 Apr 2020 00:24:15 -0600 Subject: [PATCH 178/411] Lowercase actual parameters in code and remove whitespace to comply with StyleCopAnalyzers --- Jellyfin.Api/Controllers/StartupController.cs | 18 +++++++++--------- .../ApiServiceCollectionExtensions.cs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 1e490faaec..afc9b8f3da 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -60,18 +60,18 @@ namespace Jellyfin.Api.Controllers /// <summary> /// Endpoint for updating the initial startup wizard configuration. /// </summary> - /// <param name="UICulture">The UI language culture.</param> - /// <param name="MetadataCountryCode">The metadata country code.</param> - /// <param name="PreferredMetadataLanguage">The preferred language for metadata.</param> + /// <param name="uiCulture">The UI language culture.</param> + /// <param name="metadataCountryCode">The metadata country code.</param> + /// <param name="preferredMetadataLanguage">The preferred language for metadata.</param> [HttpPost("Configuration")] public void UpdateInitialConfiguration( - [FromForm] string UICulture, - [FromForm] string MetadataCountryCode, - [FromForm] string PreferredMetadataLanguage) + [FromForm] string uiCulture, + [FromForm] string metadataCountryCode, + [FromForm] string preferredMetadataLanguage) { - _config.Configuration.UICulture = UICulture; - _config.Configuration.MetadataCountryCode = MetadataCountryCode; - _config.Configuration.PreferredMetadataLanguage = PreferredMetadataLanguage; + _config.Configuration.UICulture = uiCulture; + _config.Configuration.MetadataCountryCode = metadataCountryCode; + _config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage; _config.SaveConfiguration(); } diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 8e124d0701..0347511e67 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -71,7 +71,7 @@ namespace Jellyfin.Server.Extensions // Clear app parts to avoid other assemblies being picked up .ConfigureApplicationPartManager(a => a.ApplicationParts.Clear()) .AddApplicationPart(typeof(StartupController).Assembly) - .AddJsonOptions(options => + .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; }) From 38dae51ccf93e3b3a266007a9aad1ab980d0bb05 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sat, 11 Apr 2020 19:36:28 +0200 Subject: [PATCH 179/411] 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 d6daac50645aacf682e15ea92393988272b1b227 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 15 Apr 2020 11:12:58 +0200 Subject: [PATCH 180/411] Fix build --- Emby.Server.Implementations/ApplicationHost.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 76416392f1..0282446848 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,16 @@ namespace Emby.Server.Implementations DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager); serviceCollection.AddSingleton(DtoService); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); + ChannelManager = new ChannelManager( + UserManager, + DtoService, + LibraryManager, + LoggerFactory.CreateLogger<ChannelManager>(), + ServerConfigurationManager, + FileSystemManager, + UserDataManager, + JsonSerializer, + ProviderManager); serviceCollection.AddSingleton(ChannelManager); SessionManager = new SessionManager( @@ -979,7 +988,7 @@ namespace Emby.Server.Implementations private IActivityRepository GetActivityLogRepository() { - var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager); + var repo = new ActivityRepository(LoggerFactory.CreateLogger<ActivityRepository>(), ServerConfigurationManager.ApplicationPaths, FileSystemManager); repo.Initialize(); From 10afa4509db6fc7e3c2e7dd6ccc8997dfe3b10f9 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 15 Apr 2020 11:14:54 +0200 Subject: [PATCH 181/411] Log exception --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d3b3f7b7af..aac0d8d38a 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1995,9 +1995,9 @@ namespace Emby.Server.Implementations.Data { chapter.ImageTag = ImageProcessor.GetImageCacheTag(item, chapter); } - catch + catch (Exception ex) { - + Logger.LogError(ex, "Failed to create image cache tag."); } } } From ed1ffcb603852cd7b3504aec47219aa973e1ef1a Mon Sep 17 00:00:00 2001 From: Mathias <Mathias.baungaard@gmail.com> Date: Wed, 15 Apr 2020 08:18:39 +0000 Subject: [PATCH 182/411] Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- .../Localization/Core/da.json | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 93b8052d3e..f5397b62cd 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -35,8 +35,8 @@ "Latest": "Seneste", "MessageApplicationUpdated": "Jellyfin Server er blevet opdateret", "MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurationsafsnit {0} er blevet opdateret", - "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret", + "MessageNamedServerConfigurationUpdatedWithValue": "Server konfiguration sektion {0} er blevet opdateret", + "MessageServerConfigurationUpdated": "Server konfigurationen er blevet opdateret", "MixedContent": "Blandet indhold", "Movies": "Film", "Music": "Musik", @@ -93,13 +93,13 @@ "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek", "ValueSpecialEpisodeName": "Special - {0}", "VersionNumber": "Version {0}", - "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiration.", + "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiguration.", "TaskDownloadMissingSubtitles": "Download manglende undertekster", "TaskUpdatePluginsDescription": "Downloader og installere opdateringer for plugins som er konfigureret til at opdatere automatisk.", "TaskUpdatePlugins": "Opdater Plugins", "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gammle.", "TaskCleanLogs": "Ryd Log Mappe", - "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdatere metadata.", + "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdaterer metadata.", "TaskRefreshLibrary": "Scan Medie Bibliotek", "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke har brug for længere.", "TaskCleanCache": "Ryd Cache Mappe", @@ -108,5 +108,11 @@ "TasksLibraryCategory": "Bibliotek", "TasksMaintenanceCategory": "Vedligeholdelse", "TaskRefreshChapterImages": "Udtræk Kapitel billeder", - "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler." + "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler.", + "TaskRefreshChannelsDescription": "Genopfrisker internet kanal information.", + "TaskRefreshChannels": "Genopfrisk Kanaler", + "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end en dag gammel.", + "TaskCleanTranscode": "Rengør Transcode Mappen", + "TaskRefreshPeople": "Genopfrisk Personer", + "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek." } From 72219795d1c2751828d902031bdb428027a039c4 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 15 Apr 2020 14:29:12 +0200 Subject: [PATCH 183/411] Remove dead function --- .../Data/SqliteExtensions.cs | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index e7c5270b9f..716e5071d5 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; -using MediaBrowser.Model.Serialization; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data @@ -109,25 +107,6 @@ namespace Emby.Server.Implementations.Data return null; } - /// <summary> - /// Serializes to bytes. - /// </summary> - /// <returns>System.Byte[][].</returns> - /// <exception cref="ArgumentNullException">obj</exception> - public static byte[] SerializeToBytes(this IJsonSerializer json, object obj) - { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } - - using (var stream = new MemoryStream()) - { - json.SerializeToStream(obj, stream); - return stream.ToArray(); - } - } - public static void Attach(SQLiteDatabaseConnection db, string path, string alias) { var commandText = string.Format( @@ -383,11 +362,11 @@ namespace Emby.Server.Implementations.Data } } - public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement This) + public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement statement) { - while (This.MoveNext()) + while (statement.MoveNext()) { - yield return This.Current; + yield return statement.Current; } } } From daed41815f23795aad7f54965a8ef7eae44a1e08 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Wed, 15 Apr 2020 08:56:00 -0400 Subject: [PATCH 184/411] Add missing punctuation in xml comment Co-Authored-By: Bond-009 <bond.009@outlook.com> --- .../Authentication/AuthenticationException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Authentication/AuthenticationException.cs b/MediaBrowser.Controller/Authentication/AuthenticationException.cs index 28209f6391..081f877f72 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationException.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationException.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Authentication /// <summary> /// Initializes a new instance of the <see cref="AuthenticationException"/> class. /// </summary> - /// <param name="message">The message that describes the error</param> + /// <param name="message">The message that describes the error.</param> /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param> public AuthenticationException(string message, Exception innerException) : base(message, innerException) From c4e6329e58f36c340a65fa216a058ce1fee5507f Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Wed, 15 Apr 2020 15:00:45 -0400 Subject: [PATCH 185/411] Clean up and document ChannelManager.cs and implement suggestions --- .../Activity/ActivityLogEntryPoint.cs | 1 - .../Activity/ActivityManager.cs | 4 +- .../Activity/ActivityRepository.cs | 4 +- .../ChannelDynamicMediaSourceProvider.cs | 3 +- .../Channels/ChannelManager.cs | 227 ++++++++++-------- .../Collections/CollectionManager.cs | 4 +- 6 files changed, 140 insertions(+), 103 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index e025417d13..2ea6789765 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -27,7 +27,6 @@ namespace Emby.Server.Implementations.Activity { /// <summary> /// The activity log entry point. - /// <see cref="IServerEntryPoint"/>. /// </summary> public sealed class ActivityLogEntryPoint : IServerEntryPoint { diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index 2d2dc8e61e..5c04c0e51a 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Querying; namespace Emby.Server.Implementations.Activity { - /// <inheritdoc /> + /// <summary> + /// The activity log manager. + /// </summary> public class ActivityManager : IActivityManager { /// <inheritdoc /> diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 697ad7fcc0..3aa1f03976 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -13,7 +13,9 @@ using SQLitePCL.pretty; namespace Emby.Server.Implementations.Activity { - /// <inheritdoc cref="IActivityRepository" /> + /// <summary> + /// The activity log repository. + /// </summary> public class ActivityRepository : BaseSqliteRepository, IActivityRepository { private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US")); diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index c677e9fbc3..3e149cc82c 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Channels; @@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.Channels { return item.SourceType == SourceType.Channel ? _channelManager.GetDynamicMediaSources(item, cancellationToken) - : Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>()); + : Task.FromResult(Enumerable.Empty<MediaSourceInfo>()); } /// <inheritdoc /> diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 6e1baddfed..8beb5866f1 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -29,6 +27,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Channels { + /// <summary> + /// The LiveTV channel manager. + /// </summary> public class ChannelManager : IChannelManager { internal IChannel[] Channels { get; private set; } @@ -43,6 +44,18 @@ namespace Emby.Server.Implementations.Channels private readonly IJsonSerializer _jsonSerializer; private readonly IProviderManager _providerManager; + /// <summary> + /// Initializes a new instance of the <see cref="ChannelManager"/> class. + /// </summary> + /// <param name="userManager">The user manager.</param> + /// <param name="dtoService">The dto service.</param> + /// <param name="libraryManager">The library manager.</param> + /// <param name="loggerFactory">The logger factory.</param> + /// <param name="config">The server configuration manager.</param> + /// <param name="fileSystem">The filesystem.</param> + /// <param name="userDataManager">The user data manager.</param> + /// <param name="jsonSerializer">The JSON serializer.</param> + /// <param name="providerManager">The provider manager.</param> public ChannelManager( IUserManager userManager, IDtoService dtoService, @@ -67,11 +80,13 @@ namespace Emby.Server.Implementations.Channels private static TimeSpan CacheLength => TimeSpan.FromHours(3); + /// <inheritdoc /> public void AddParts(IEnumerable<IChannel> channels) { Channels = channels.ToArray(); } + /// <inheritdoc /> public bool EnableMediaSourceDisplay(BaseItem item) { var internalChannel = _libraryManager.GetItemById(item.ChannelId); @@ -80,15 +95,16 @@ namespace Emby.Server.Implementations.Channels return !(channel is IDisableMediaSourceDisplay); } + /// <inheritdoc /> public bool CanDelete(BaseItem item) { var internalChannel = _libraryManager.GetItemById(item.ChannelId); var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); - var supportsDelete = channel as ISupportsDelete; - return supportsDelete != null && supportsDelete.CanDelete(item); + return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item); } + /// <inheritdoc /> public bool EnableMediaProbe(BaseItem item) { var internalChannel = _libraryManager.GetItemById(item.ChannelId); @@ -97,6 +113,7 @@ namespace Emby.Server.Implementations.Channels return channel is ISupportsMediaProbe; } + /// <inheritdoc /> public Task DeleteItem(BaseItem item) { var internalChannel = _libraryManager.GetItemById(item.ChannelId); @@ -123,11 +140,16 @@ namespace Emby.Server.Implementations.Channels .OrderBy(i => i.Name); } + /// <summary> + /// Returns an <see cref="IEnumerable{T}"/> containing installed channel ID's. + /// </summary> + /// <returns>An <see cref="IEnumerable{T}"/> containing installed channel ID's.</returns> public IEnumerable<Guid> GetInstalledChannelIds() { return GetAllChannels().Select(i => GetInternalChannelId(i.Name)); } + /// <inheritdoc /> public QueryResult<Channel> GetChannelsInternal(ChannelQuery query) { var user = query.UserId.Equals(Guid.Empty) @@ -146,15 +168,13 @@ namespace Emby.Server.Implementations.Channels { try { - var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes; - - return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; + return (GetChannelProvider(i) is IHasFolderAttributes hasAttributes + && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; } catch { return false; } - }).ToList(); } @@ -171,7 +191,6 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } @@ -188,9 +207,9 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } + if (query.IsFavorite.HasValue) { var val = query.IsFavorite.Value; @@ -215,7 +234,6 @@ namespace Emby.Server.Implementations.Channels { return false; } - }).ToList(); } @@ -226,6 +244,7 @@ namespace Emby.Server.Implementations.Channels { all = all.Skip(query.StartIndex.Value).ToList(); } + if (query.Limit.HasValue) { all = all.Take(query.Limit.Value).ToList(); @@ -248,6 +267,7 @@ namespace Emby.Server.Implementations.Channels }; } + /// <inheritdoc /> public QueryResult<BaseItemDto> GetChannels(ChannelQuery query) { var user = query.UserId.Equals(Guid.Empty) @@ -272,6 +292,12 @@ namespace Emby.Server.Implementations.Channels return result; } + /// <summary> + /// Refreshes the associated channels. + /// </summary> + /// <param name="progress">The progress.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The completed task.</returns> public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken) { var allChannelsList = GetAllChannels().ToList(); @@ -305,14 +331,7 @@ namespace Emby.Server.Implementations.Channels private Channel GetChannelEntity(IChannel channel) { - var item = GetChannel(GetInternalChannelId(channel.Name)); - - if (item == null) - { - item = GetChannel(channel, CancellationToken.None).Result; - } - - return item; + return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result; } private List<MediaSourceInfo> GetSavedMediaSources(BaseItem item) @@ -351,6 +370,7 @@ namespace Emby.Server.Implementations.Channels _jsonSerializer.SerializeToFile(mediaSources, path); } + /// <inheritdoc /> public IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken) { IEnumerable<MediaSourceInfo> results = GetSavedMediaSources(item); @@ -360,6 +380,12 @@ namespace Emby.Server.Implementations.Channels .ToList(); } + /// <summary> + /// Gets the dynamic media sources based on the provided item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The completed task.</returns> public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken) { var channel = GetChannel(item.ChannelId); @@ -409,7 +435,7 @@ namespace Emby.Server.Implementations.Channels private static MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info) { - info.RunTimeTicks = info.RunTimeTicks ?? item.RunTimeTicks; + info.RunTimeTicks ??= item.RunTimeTicks; return info; } @@ -482,41 +508,43 @@ namespace Emby.Server.Implementations.Channels private static string GetOfficialRating(ChannelParentalRating rating) { - switch (rating) - { - case ChannelParentalRating.Adult: - return "XXX"; - case ChannelParentalRating.UsR: - return "R"; - case ChannelParentalRating.UsPG13: - return "PG-13"; - case ChannelParentalRating.UsPG: - return "PG"; - default: - return null; - } + return rating switch + { + ChannelParentalRating.Adult => "XXX", + ChannelParentalRating.UsR => "R", + ChannelParentalRating.UsPG13 => "PG-13", + ChannelParentalRating.UsPG => "PG", + _ => null + }; } + /// <summary> + /// Gets a channel with the provided Guid. + /// </summary> + /// <param name="id">The Guid.</param> + /// <returns>The corresponding channel.</returns> public Channel GetChannel(Guid id) { return _libraryManager.GetItemById(id) as Channel; } + /// <inheritdoc /> public Channel GetChannel(string id) { return _libraryManager.GetItemById(id) as Channel; } + /// <inheritdoc /> public ChannelFeatures[] GetAllChannelFeatures() { return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Channel).Name }, OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) } - }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray(); } + /// <inheritdoc /> public ChannelFeatures GetChannelFeatures(string id) { if (string.IsNullOrEmpty(id)) @@ -530,6 +558,11 @@ namespace Emby.Server.Implementations.Channels return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures()); } + /// <summary> + /// Checks whether the provided Guid supports external transfer. + /// </summary> + /// <param name="channelId">The Guid.</param> + /// <returns>Whether or not the provided Guid supports external transfer.</returns> public bool SupportsExternalTransfer(Guid channelId) { //var channel = GetChannel(channelId); @@ -538,6 +571,13 @@ namespace Emby.Server.Implementations.Channels return channelProvider.GetChannelFeatures().SupportsContentDownloading; } + /// <summary> + /// Gets the provided channel's supported features. + /// </summary> + /// <param name="channel">The channel.</param> + /// <param name="provider">The provider.</param> + /// <param name="features">The features.</param> + /// <returns>The supported features.</returns> public ChannelFeatures GetChannelFeaturesDto(Channel channel, IChannel provider, InternalChannelFeatures features) @@ -570,6 +610,7 @@ namespace Emby.Server.Implementations.Channels return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel)); } + /// <inheritdoc /> public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken) { var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false); @@ -588,6 +629,7 @@ namespace Emby.Server.Implementations.Channels return result; } + /// <inheritdoc /> public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken) { var channels = GetAllChannels().Where(i => i is ISupportsLatestMedia).ToArray(); @@ -662,6 +704,7 @@ namespace Emby.Server.Implementations.Channels } } + /// <inheritdoc /> public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken) { // Get the internal channel entity @@ -711,7 +754,6 @@ namespace Emby.Server.Implementations.Channels { DeleteFileLocation = false, DeleteFromExternalProvider = false - }, parentItem, false); } } @@ -720,6 +762,7 @@ namespace Emby.Server.Implementations.Channels return _libraryManager.GetItemsResult(query); } + /// <inheritdoc /> public async Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken) { var internalResult = await GetChannelItemsInternal(query, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false); @@ -743,7 +786,7 @@ namespace Emby.Server.Implementations.Channels bool sortDescending, CancellationToken cancellationToken) { - var userId = user == null ? null : user.Id.ToString("N", CultureInfo.InvariantCulture); + var userId = user?.Id.ToString("N", CultureInfo.InvariantCulture); var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending); @@ -794,7 +837,7 @@ namespace Emby.Server.Implementations.Channels var query = new InternalChannelItemQuery { - UserId = user == null ? Guid.Empty : user.Id, + UserId = user?.Id ?? Guid.Empty, SortBy = sortField, SortDescending = sortDescending, FolderId = externalFolderId @@ -843,8 +886,7 @@ namespace Emby.Server.Implementations.Channels var userCacheKey = string.Empty; - var hasCacheKey = channel as IHasCacheKey; - if (hasCacheKey != null) + if (channel is IHasCacheKey hasCacheKey) { userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty; } @@ -919,59 +961,55 @@ namespace Emby.Server.Implementations.Channels if (info.Type == ChannelItemType.Folder) { - if (info.FolderType == ChannelFolderType.MusicAlbum) - { - item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew); - } - else if (info.FolderType == ChannelFolderType.MusicArtist) + switch (info.FolderType) { - item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew); - } - else if (info.FolderType == ChannelFolderType.PhotoAlbum) - { - item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew); - } - else if (info.FolderType == ChannelFolderType.Series) - { - item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew); - } - else if (info.FolderType == ChannelFolderType.Season) - { - item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew); - } - else - { - item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew); + case ChannelFolderType.MusicAlbum: + item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew); + break; + case ChannelFolderType.MusicArtist: + item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew); + break; + case ChannelFolderType.PhotoAlbum: + item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew); + break; + case ChannelFolderType.Series: + item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew); + break; + case ChannelFolderType.Season: + item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew); + break; + default: + item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew); + break; } } else if (info.MediaType == ChannelMediaType.Audio) { - if (info.ContentType == ChannelMediaContentType.Podcast) - { - item = GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew); - } - else - { - item = GetItemById<Audio>(info.Id, channelProvider.Name, out isNew); - } + item = info.ContentType == ChannelMediaContentType.Podcast + ? GetItemById<AudioBook>(info.Id, channelProvider.Name, out isNew) + : GetItemById<Audio>(info.Id, channelProvider.Name, out isNew); } else { - if (info.ContentType == ChannelMediaContentType.Episode) - { - item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew); - } - else if (info.ContentType == ChannelMediaContentType.Movie) - { - item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew); - } - else if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer) - { - item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew); - } - else + switch (info.ContentType) { - item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew); + case ChannelMediaContentType.Episode: + item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew); + break; + case ChannelMediaContentType.Movie: + item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew); + break; + default: + if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer) + { + item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew); + } + else + { + item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew); + } + + break; } } @@ -981,7 +1019,6 @@ namespace Emby.Server.Implementations.Channels { item.RunTimeTicks = null; } - else if (isNew || !enableMediaProbe) { item.RunTimeTicks = info.RunTimeTicks; @@ -1014,20 +1051,17 @@ namespace Emby.Server.Implementations.Channels } } - var hasArtists = item as IHasArtist; - if (hasArtists != null) + if (item is IHasArtist hasArtists) { hasArtists.Artists = info.Artists.ToArray(); } - var hasAlbumArtists = item as IHasAlbumArtist; - if (hasAlbumArtists != null) + if (item is IHasAlbumArtist hasAlbumArtists) { hasAlbumArtists.AlbumArtists = info.AlbumArtists.ToArray(); } - var trailer = item as Trailer; - if (trailer != null) + if (item is Trailer trailer) { if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes)) { @@ -1066,8 +1100,7 @@ namespace Emby.Server.Implementations.Channels } item.ParentId = parentFolderId; - var hasSeries = item as IHasSeries; - if (hasSeries != null) + if (item is IHasSeries hasSeries) { if (!string.Equals(hasSeries.SeriesName, info.SeriesName, StringComparison.OrdinalIgnoreCase)) { @@ -1084,22 +1117,20 @@ namespace Emby.Server.Implementations.Channels } item.ExternalId = info.Id; - var channelAudioItem = item as Audio; - if (channelAudioItem != null) + if (item is Audio channelAudioItem) { channelAudioItem.ExtraType = info.ExtraType; var mediaSource = info.MediaSources.FirstOrDefault(); - item.Path = mediaSource == null ? null : mediaSource.Path; + item.Path = mediaSource?.Path; } - var channelVideoItem = item as Video; - if (channelVideoItem != null) + if (item is Video channelVideoItem) { channelVideoItem.ExtraType = info.ExtraType; var mediaSource = info.MediaSources.FirstOrDefault(); - item.Path = mediaSource == null ? null : mediaSource.Path; + item.Path = mediaSource?.Path; } if (!string.IsNullOrEmpty(info.ImageUrl) && !item.HasImage(ImageType.Primary)) diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 297e8327a5..4b04accbbd 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -21,7 +21,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Collections { - /// <inheritdoc /> + /// <summary> + /// The collection manager. + /// </summary> public class CollectionManager : ICollectionManager { private readonly ILibraryManager _libraryManager; From f8a4525fd5ba907f7804aeb1dbaef24c48912e51 Mon Sep 17 00:00:00 2001 From: gnehs <jayuiop@gmail.com> Date: Wed, 15 Apr 2020 15:05:07 +0000 Subject: [PATCH 186/411] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 21034b76f3..c423c7ea73 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -20,7 +20,7 @@ "HeaderContinueWatching": "繼續觀賞", "HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteArtists": "最愛演出者", - "HeaderFavoriteEpisodes": "最愛級數", + "HeaderFavoriteEpisodes": "最愛影集", "HeaderFavoriteShows": "最愛節目", "HeaderFavoriteSongs": "最愛歌曲", "HeaderLiveTV": "電視直播", From 30b860fb827366ddbb5d3cbcdb4565ed996df917 Mon Sep 17 00:00:00 2001 From: WWWesten <wwwesten@gmail.com> Date: Wed, 15 Apr 2020 19:48:08 +0000 Subject: [PATCH 187/411] Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- .../Localization/Core/ru.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index c46aa5c30d..442e09791c 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -92,5 +92,18 @@ "UserStoppedPlayingItemWithValues": "{0} - воспр. «{1}» ост-но на {2}", "ValueHasBeenAddedToLibrary": "{0} (добавлено в медиатеку)", "ValueSpecialEpisodeName": "Спецэпизод - {0}", - "VersionNumber": "Версия {0}" + "VersionNumber": "Версия {0}", + "TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров", + "TaskRefreshChannels": "Подновить каналы", + "TaskCleanTranscode": "Очистка каталога перекодировки", + "TaskUpdatePlugins": "Обновление плагинов", + "TaskRefreshPeople": "Подновить людей", + "TaskCleanLogs": "Очистка каталога журналов", + "TaskRefreshLibrary": "Сканирование медиатеки", + "TaskRefreshChapterImages": "Извлечение рисунков сцен", + "TaskCleanCache": "Очистка каталога кеша", + "TasksChannelsCategory": "Интернет-каналы", + "TasksApplicationCategory": "Приложение", + "TasksLibraryCategory": "Медиатека", + "TasksMaintenanceCategory": "Обслуживание" } From 95dc99fdbda1067d9392649a47110448bc6b9187 Mon Sep 17 00:00:00 2001 From: Vasily <JustAMan@users.noreply.github.com> Date: Thu, 16 Apr 2020 01:03:29 +0300 Subject: [PATCH 188/411] Update Emby.Server.Implementations/HttpServer/HttpResultFactory.cs Co-Authored-By: Bond-009 <bond.009@outlook.com> --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 3b1563fdda..d394c56ad2 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.HttpServer // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\""; // We specifically use en-US culture because both day of week and month names must be in it9 - private static readonly CultureInfo _enUSculture = CultureInfo.CreateSpecificCulture("en-US"); + private static readonly CultureInfo _enUSculture = new CultureInfo("en-US", false); /// <summary> /// The logger. From bb288e16cce65fdf48b673204b7e67b94b21c034 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Wed, 15 Apr 2020 17:02:43 -0600 Subject: [PATCH 189/411] Document JSON options change --- Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 0347511e67..71ef9a69a2 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -73,6 +73,7 @@ namespace Jellyfin.Server.Extensions .AddApplicationPart(typeof(StartupController).Assembly) .AddJsonOptions(options => { + // Setting the naming policy to null leaves the property names as-is when serializing objects to JSON. options.JsonSerializerOptions.PropertyNamingPolicy = null; }) .AddControllersAsServices(); From 1c38983ab4625daf9e51684ead7c894abc236cc2 Mon Sep 17 00:00:00 2001 From: WWWesten <wwwesten@gmail.com> Date: Thu, 16 Apr 2020 18:36:12 +0000 Subject: [PATCH 190/411] Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 442e09791c..9560cc998a 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -105,5 +105,14 @@ "TasksChannelsCategory": "Интернет-каналы", "TasksApplicationCategory": "Приложение", "TasksLibraryCategory": "Медиатека", - "TasksMaintenanceCategory": "Обслуживание" + "TasksMaintenanceCategory": "Обслуживание", + "TaskDownloadMissingSubtitlesDescription": "Выполняется поиск в Интернете отсутствующих субтитров на основе конфигурации метаданных.", + "TaskRefreshChannelsDescription": "Подновляются данные интернет-канала.", + "TaskCleanTranscodeDescription": "Удаляются файлы перекодировки старше одного дня.", + "TaskUpdatePluginsDescription": "Загружаются и устанавливаются обновления для плагинов, настроенных обновляться автоматически.", + "TaskRefreshPeopleDescription": "Обновляются метаданные актеров и режиссёров в медиатеке.", + "TaskCleanLogsDescription": "Удаляются файлы журнала, возраст которых превышает {0} дн(я/ей).", + "TaskRefreshLibraryDescription": "Сканируется медиатека на новые файлы и обновляются метаданные.", + "TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые имеют сцены.", + "TaskCleanCacheDescription": "Удаляются файлы кэша, которые больше не нужны системе." } From fee76097f44d0f11356e4a559df9178063b1bf29 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Thu, 16 Apr 2020 21:45:00 -0400 Subject: [PATCH 191/411] 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; /// <inheritdoc/> - public bool CanConnectWithHttps => ListenWithHttps || ServerConfigurationManager.Configuration.IsBehindProxy; - public async Task<string> 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 /// </summary> bool ListenWithHttps { get; } - /// <summary> - /// Gets a value indicating whether a client can connect to the server over HTTPS, either directly or via a - /// reverse proxy. - /// </summary> - bool CanConnectWithHttps { get; } - /// <summary> /// Gets a value indicating whether this instance has update available. /// </summary> From 00a0e013c695a3741218985afadd31265a6ddb40 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Thu, 16 Apr 2020 21:46:49 -0400 Subject: [PATCH 192/411] 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()); } - /// <inheritdoc /> + /// <inheritdoc/> public string GetLocalApiUrl(ReadOnlySpan<char> 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<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase); - private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken) + private async Task<bool> 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; } /// <summary> - /// 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. /// </summary> - /// <value>The local ip address.</value> + /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param> + /// <returns>A list containing all the local IP addresses of the server.</returns> Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken); /// <summary> - /// 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 <see cref="GetLocalIpAddresses"/>. /// </summary> - /// <value>The local API URL.</value> + /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param> + /// <returns>The server URL.</returns> Task<string> GetLocalApiUrl(CancellationToken cancellationToken); /// <summary> - /// Gets the local API URL. + /// Gets a local (LAN) URL that can be used to access the API. /// </summary> - /// <param name="hostname">The hostname.</param> - /// <returns>The local API URL.</returns> + /// <param name="hostname">The hostname to use in the URL.</param> + /// <returns>The API URL.</returns> string GetLocalApiUrl(ReadOnlySpan<char> hostname); /// <summary> - /// Gets the local API URL. + /// Gets a local (LAN) URL that can be used to access the API. /// </summary> - /// <param name="address">The IP address.</param> - /// <returns>The local API URL.</returns> + /// <param name="address">The IP address to use as the hostname in the URL.</param> + /// <returns>The API URL.</returns> string GetLocalApiUrl(IPAddress address); /// <summary> From c78413cf7cee018d50bce8e02d9cb7e3e5626d90 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 6 Apr 2020 00:03:56 -0400 Subject: [PATCH 193/411] Disable UPnP by default --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 3107ec2426..9983aa0e09 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -254,7 +254,7 @@ namespace MediaBrowser.Model.Configuration AutoRunWebApp = true; EnableRemoteAccess = true; - EnableUPnP = true; + EnableUPnP = false; MinResumePct = 5; MaxResumePct = 90; From 26afb42a72ec111675e079be7bdee13ea05c9713 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 6 Apr 2020 00:05:47 -0400 Subject: [PATCH 194/411] Cleanup port forwarding service - Use a concurrent collection instead of manually locking - Do not forward HTTPS port when it is not enabled - Created multiple rules (HTTP/HTTPS) in parallel instead of in sync --- .../EntryPoints/ExternalPortForwarding.cs | 79 ++++++++----------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index e290c62e16..5525401c5e 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Text; @@ -26,8 +27,8 @@ namespace Emby.Server.Implementations.EntryPoints private readonly IServerConfigurationManager _config; private readonly IDeviceDiscovery _deviceDiscovery; - private readonly object _createdRulesLock = new object(); - private List<IPEndPoint> _createdRules = new List<IPEndPoint>(); + private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new ConcurrentDictionary<IPEndPoint, byte>(); + private Timer _timer; private string _lastConfigIdentifier; @@ -98,7 +99,7 @@ namespace Emby.Server.Implementations.EntryPoints NatUtility.DeviceFound += OnNatUtilityDeviceFound; NatUtility.StartDiscovery(); - _timer = new Timer(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); + _timer = new Timer((_) => _createdRules.Clear(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered; @@ -117,26 +118,16 @@ namespace Emby.Server.Implementations.EntryPoints _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered; } - private void ClearCreatedRules(object state) - { - lock (_createdRulesLock) - { - _createdRules.Clear(); - } - } - private void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e) { NatUtility.Search(e.Argument.LocalIpAddress, NatProtocol.Upnp); } - private void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e) + private async void OnNatUtilityDeviceFound(object sender, DeviceEventArgs e) { try { - var device = e.Device; - - CreateRules(device); + await CreateRules(e.Device).ConfigureAwait(false); } catch (Exception ex) { @@ -144,7 +135,7 @@ namespace Emby.Server.Implementations.EntryPoints } } - private async void CreateRules(INatDevice device) + private Task CreateRules(INatDevice device) { if (_disposed) { @@ -153,50 +144,46 @@ namespace Emby.Server.Implementations.EntryPoints // On some systems the device discovered event seems to fire repeatedly // This check will help ensure we're not trying to port map the same device over and over - var address = device.DeviceEndpoint; - - lock (_createdRulesLock) + if (!_createdRules.TryAdd(device.DeviceEndpoint, 0)) { - if (!_createdRules.Contains(address)) - { - _createdRules.Add(address); - } - else - { - return; - } + return Task.CompletedTask; } - try - { - await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error creating http port map"); - return; - } + return Task.WhenAll(CreatePortMaps(device)); + } - try - { - await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false); - } - catch (Exception ex) + private IEnumerable<Task> CreatePortMaps(INatDevice device) + { + yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort); + + if (_appHost.EnableHttps) { - _logger.LogError(ex, "Error creating https port map"); + yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort); } } - private Task<Mapping> CreatePortMap(INatDevice device, int privatePort, int publicPort) + private async Task CreatePortMap(INatDevice device, int privatePort, int publicPort) { _logger.LogDebug( - "Creating port map on local port {0} to public port {1} with device {2}", + "Creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}", privatePort, publicPort, device.DeviceEndpoint); - return device.CreatePortMapAsync( - new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name)); + try + { + var mapping = new Mapping(Protocol.Tcp, privatePort, publicPort, 0, _appHost.Name); + await device.CreatePortMapAsync(mapping).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Error creating port map on local port {LocalPort} to public port {PublicPort} with device {DeviceEndpoint}.", + privatePort, + publicPort, + device.DeviceEndpoint); + } } /// <inheritdoc /> From 78d9b9894c5ad9478852196213b8585c37456128 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 6 Apr 2020 00:22:27 -0400 Subject: [PATCH 195/411] Respond to config changes correctly for external port forwarding --- .../EntryPoints/ExternalPortForwarding.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 5525401c5e..8dbc6494d5 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.EntryPoints private readonly ConcurrentDictionary<IPEndPoint, byte> _createdRules = new ConcurrentDictionary<IPEndPoint, byte>(); private Timer _timer; - private string _lastConfigIdentifier; + private string _configIdentifier; private bool _disposed = false; @@ -70,7 +70,10 @@ namespace Emby.Server.Implementations.EntryPoints private void OnConfigurationUpdated(object sender, EventArgs e) { - if (!string.Equals(_lastConfigIdentifier, GetConfigIdentifier(), StringComparison.OrdinalIgnoreCase)) + var oldConfigIdentifier = _configIdentifier; + _configIdentifier = GetConfigIdentifier(); + + if (!string.Equals(_configIdentifier, oldConfigIdentifier, StringComparison.OrdinalIgnoreCase)) { Stop(); Start(); @@ -94,7 +97,7 @@ namespace Emby.Server.Implementations.EntryPoints return; } - _logger.LogDebug("Starting NAT discovery"); + _logger.LogInformation("Starting NAT discovery"); NatUtility.DeviceFound += OnNatUtilityDeviceFound; NatUtility.StartDiscovery(); @@ -102,13 +105,11 @@ namespace Emby.Server.Implementations.EntryPoints _timer = new Timer((_) => _createdRules.Clear(), null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10)); _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered; - - _lastConfigIdentifier = GetConfigIdentifier(); } private void Stop() { - _logger.LogDebug("Stopping NAT discovery"); + _logger.LogInformation("Stopping NAT discovery"); NatUtility.StopDiscovery(); NatUtility.DeviceFound -= OnNatUtilityDeviceFound; From 8a81bcd7425ebf93eb688bbc754179dfc20787ec Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Thu, 16 Apr 2020 22:49:23 -0400 Subject: [PATCH 196/411] Restart port forwarding when public https port changes --- .../EntryPoints/ExternalPortForwarding.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 8dbc6494d5..37d7fd4799 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -61,6 +61,7 @@ namespace Emby.Server.Implementations.EntryPoints return new StringBuilder(32) .Append(config.EnableUPnP).Append(Separator) .Append(config.PublicPort).Append(Separator) + .Append(config.PublicHttpsPort).Append(Separator) .Append(_appHost.HttpPort).Append(Separator) .Append(_appHost.HttpsPort).Append(Separator) .Append(_appHost.EnableHttps).Append(Separator) From 1666f3ca148c38609c85cb81e1e8b69e3473e319 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Thu, 16 Apr 2020 23:40:32 -0400 Subject: [PATCH 197/411] 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 /// <summary> /// Execute the migration routine. /// </summary> - /// <param name="host">Host that hosts current version.</param> - /// <param name="logger">Host logger.</param> - 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 /// <summary> /// The list of known migrations, in order of applicability. /// </summary> - internal static readonly IMigrationRoutine[] Migrations = + private static readonly Type[] _migrationTypes = { - new Routines.DisableTranscodingThrottling(), - new Routines.CreateUserLoggingConfigFile() + typeof(Routines.DisableTranscodingThrottling), + typeof(Routines.CreateUserLoggingConfigFile) }; /// <summary> @@ -27,6 +28,10 @@ namespace Jellyfin.Server.Migrations public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { var logger = loggerFactory.CreateLogger<MigrationRunner>(); + var migrations = _migrationTypes + .Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m)) + .OfType<IMigrationRoutine>() + .ToArray(); var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration<MigrationOptions>(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; + } + /// <inheritdoc/> public Guid Id => Guid.Parse("{EF103419-8451-40D8-9F34-D1A8E93A1679}"); @@ -43,9 +50,9 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "CreateLoggingConfigHeirarchy"; /// <inheritdoc/> - public void Perform(CoreAppHost host, ILogger logger) + public void Perform() { - var logDirectory = host.Resolve<IApplicationPaths>().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 /// </summary> internal class DisableTranscodingThrottling : IMigrationRoutine { + private readonly ILogger _logger; + private readonly IConfigurationManager _configManager; + + public DisableTranscodingThrottling(ILogger<DisableTranscodingThrottling> logger, IConfigurationManager configManager) + { + _logger = logger; + _configManager = configManager; + } + /// <inheritdoc/> public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); @@ -17,16 +26,16 @@ namespace Jellyfin.Server.Migrations.Routines public string Name => "DisableTranscodingThrottling"; /// <inheritdoc/> - 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<EncodingOptions>("encoding"); + var encoding = _configManager.GetConfiguration<EncodingOptions>("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 bcf1ef319a228dc6383b1730e8222c1b2294e697 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Fri, 17 Apr 2020 00:27:18 -0400 Subject: [PATCH 198/411] Update documentation for EnableUPnP --- MediaBrowser.Model/Configuration/ServerConfiguration.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 9983aa0e09..b5e8d5589a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -15,9 +15,8 @@ namespace MediaBrowser.Model.Configuration private string _baseUrl; /// <summary> - /// Gets or sets a value indicating whether [enable u pn p]. + /// Gets or sets a value indicating whether to enable automatic port forwarding. /// </summary> - /// <value><c>true</c> if [enable u pn p]; otherwise, <c>false</c>.</value> public bool EnableUPnP { get; set; } /// <summary> From e7fde6aacaf46183d04777c511c72d49300c09cd Mon Sep 17 00:00:00 2001 From: Matt Lyons <lyonzy@fastmail.com> Date: Fri, 17 Apr 2020 16:13:56 +1000 Subject: [PATCH 199/411] Handle null outputFileExtension with null-conditional operator --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b92ea6d1f4..7705393576 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -131,12 +131,10 @@ namespace MediaBrowser.Api.Playback /// </summary> private string GetOutputFilePath(StreamState state, EncodingOptions encodingOptions, string outputFileExtension) { - if (outputFileExtension == null) outputFileExtension = ""; - var data = $"{state.MediaPath}-{state.UserAgent}-{state.Request.DeviceId}-{state.Request.PlaySessionId}"; var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture); - var ext = outputFileExtension.ToLowerInvariant(); + var ext = outputFileExtension?.ToLowerInvariant(); var folder = ServerConfigurationManager.GetTranscodePath(); return EnableOutputInSubFolder From eba781eac565b0639cba04d6ba98e2210d6cd35a Mon Sep 17 00:00:00 2001 From: a1 <a1@G4.lan> Date: Thu, 16 Apr 2020 23:36:43 -0700 Subject: [PATCH 200/411] Fix DLNA clients displaying wrong album art. --- Emby.Dlna/Didl/DidlBuilder.cs | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 88cc00e30a..59951f6d9b 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -1018,19 +1018,58 @@ namespace Emby.Dlna.Didl } } - item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary)); + // For audio tracks without art use album art if available. + if (item is Audio audioItem) + { + var album = audioItem.AlbumEntity; + return album != null && album.HasImage(ImageType.Primary) + ? GetImageInfo(album, ImageType.Primary) + : null; + } - if (item != null) + // Don't look beyond album/playlist level. Metadata service may assign an image from a different album/show to the parent folder. + if (item is MusicAlbum || item is Playlist) { - if (item.HasImage(ImageType.Primary)) - { - return GetImageInfo(item, ImageType.Primary); - } + return null; + } + + // For other item types check parents, but be aware that image retrieved from a parent may be not suitable for this media item. + var parentWithImage = GetFirstParentWithImageBelowUserRoot(item); + if (parentWithImage != null) + { + return GetImageInfo(parentWithImage, ImageType.Primary); } return null; } + private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item) + { + if (item == null) + { + return null; + } + + if (item.HasImage(ImageType.Primary)) + { + return item; + } + + var parent = item.GetParent(); + if (parent is UserRootFolder) + { + return null; + } + + // terminate in case we went past user root folder (unlikely?) + if (parent is Folder folder && folder.IsRoot) + { + return null; + } + + return GetFirstParentWithImageBelowUserRoot(parent); + } + private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type) { var imageInfo = item.GetImageInfo(type, 0); From 7529402cc9cf697915dd703b9d3841bef4b37df9 Mon Sep 17 00:00:00 2001 From: Andrey Sinitsyn <s1nav@outlook.com> Date: Fri, 17 Apr 2020 07:14:47 +0000 Subject: [PATCH 201/411] Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- .../Localization/Core/ru.json | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 9560cc998a..71ee6446ce 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -9,8 +9,8 @@ "Channels": "Каналы", "ChapterNameValue": "Сцена {0}", "Collections": "Коллекции", - "DeviceOfflineWithName": "{0} - подкл. разъ-но", - "DeviceOnlineWithName": "{0} - подкл. уст-но", + "DeviceOfflineWithName": "{0} - отключено", + "DeviceOnlineWithName": "{0} - подключено", "FailedLoginAttemptWithUserName": "{0} - попытка входа неудачна", "Favorites": "Избранное", "Folders": "Папки", @@ -26,30 +26,30 @@ "HeaderLiveTV": "Эфир", "HeaderNextUp": "Очередное", "HeaderRecordingGroups": "Группы записей", - "HomeVideos": "Дом. видео", + "HomeVideos": "Домашнее видео", "Inherit": "Наследуемое", "ItemAddedWithName": "{0} - добавлено в медиатеку", "ItemRemovedWithName": "{0} - изъято из медиатеки", "LabelIpAddressValue": "IP-адрес: {0}", "LabelRunningTimeValue": "Длительность: {0}", - "Latest": "Новейшее", + "Latest": "Последнее", "MessageApplicationUpdated": "Jellyfin Server был обновлён", "MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Конфиг-ия сервера (раздел {0}) была обновлена", - "MessageServerConfigurationUpdated": "Конфиг-ия сервера была обновлена", + "MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена", + "MessageServerConfigurationUpdated": "Конфигурация сервера была обновлена", "MixedContent": "Смешанное содержимое", "Movies": "Кино", "Music": "Музыка", - "MusicVideos": "Муз. видео", + "MusicVideos": "Музыкальные клипы", "NameInstallFailed": "Установка {0} неудачна", "NameSeasonNumber": "Сезон {0}", "NameSeasonUnknown": "Сезон неопознан", "NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.", "NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения", "NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено", - "NotificationOptionAudioPlayback": "Воспр-ие аудио зап-но", - "NotificationOptionAudioPlaybackStopped": "Восп-ие аудио ост-но", - "NotificationOptionCameraImageUploaded": "Произведена выкладка отснятого с камеры", + "NotificationOptionAudioPlayback": "Воспроизведение аудио запущено", + "NotificationOptionAudioPlaybackStopped": "Воспроизведение аудио остановлено", + "NotificationOptionCameraImageUploaded": "Изображения с камеры загружены", "NotificationOptionInstallationFailed": "Сбой установки", "NotificationOptionNewLibraryContent": "Новое содержание добавлено", "NotificationOptionPluginError": "Сбой плагина", @@ -59,8 +59,8 @@ "NotificationOptionServerRestartRequired": "Требуется перезапуск сервера", "NotificationOptionTaskFailed": "Сбой назначенной задачи", "NotificationOptionUserLockedOut": "Пользователь заблокирован", - "NotificationOptionVideoPlayback": "Воспр-ие видео зап-но", - "NotificationOptionVideoPlaybackStopped": "Восп-ие видео ост-но", + "NotificationOptionVideoPlayback": "Воспроизведение видео запущено", + "NotificationOptionVideoPlaybackStopped": "Воспроизведение видео остановлено", "Photos": "Фото", "Playlists": "Плей-листы", "Plugin": "Плагин", @@ -76,43 +76,43 @@ "StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.", "SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить", "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}", - "Sync": "Синхро", + "Sync": "Синхронизация", "System": "Система", "TvShows": "ТВ", - "User": "Польз-ль", + "User": "Пользователь", "UserCreatedWithName": "Пользователь {0} был создан", "UserDeletedWithName": "Пользователь {0} был удалён", "UserDownloadingItemWithValues": "{0} загружает {1}", "UserLockedOutWithName": "Пользователь {0} был заблокирован", - "UserOfflineFromDevice": "{0} - подкл. с {1} разъ-но", - "UserOnlineFromDevice": "{0} - подкл. с {1} уст-но", - "UserPasswordChangedWithName": "Пароль польз-ля {0} был изменён", - "UserPolicyUpdatedWithName": "Польз-ие политики {0} были обновлены", - "UserStartedPlayingItemWithValues": "{0} - воспр. «{1}» на {2}", - "UserStoppedPlayingItemWithValues": "{0} - воспр. «{1}» ост-но на {2}", + "UserOfflineFromDevice": "{0} отключился с {1}", + "UserOnlineFromDevice": "{0} подключился с {1}", + "UserPasswordChangedWithName": "Пароль пользователя {0} был изменён", + "UserPolicyUpdatedWithName": "Политики пользователя {0} были обновлены", + "UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}", + "UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}", "ValueHasBeenAddedToLibrary": "{0} (добавлено в медиатеку)", - "ValueSpecialEpisodeName": "Спецэпизод - {0}", + "ValueSpecialEpisodeName": "Специальный эпизод - {0}", "VersionNumber": "Версия {0}", "TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров", - "TaskRefreshChannels": "Подновить каналы", + "TaskRefreshChannels": "Обновление каналов", "TaskCleanTranscode": "Очистка каталога перекодировки", "TaskUpdatePlugins": "Обновление плагинов", - "TaskRefreshPeople": "Подновить людей", + "TaskRefreshPeople": "Обновление метаданных людей", "TaskCleanLogs": "Очистка каталога журналов", "TaskRefreshLibrary": "Сканирование медиатеки", - "TaskRefreshChapterImages": "Извлечение рисунков сцен", + "TaskRefreshChapterImages": "Извлечение изображений сцен", "TaskCleanCache": "Очистка каталога кеша", "TasksChannelsCategory": "Интернет-каналы", "TasksApplicationCategory": "Приложение", "TasksLibraryCategory": "Медиатека", "TasksMaintenanceCategory": "Обслуживание", "TaskDownloadMissingSubtitlesDescription": "Выполняется поиск в Интернете отсутствующих субтитров на основе конфигурации метаданных.", - "TaskRefreshChannelsDescription": "Подновляются данные интернет-канала.", + "TaskRefreshChannelsDescription": "Обновляются данные интернет-каналов.", "TaskCleanTranscodeDescription": "Удаляются файлы перекодировки старше одного дня.", - "TaskUpdatePluginsDescription": "Загружаются и устанавливаются обновления для плагинов, настроенных обновляться автоматически.", + "TaskUpdatePluginsDescription": "Загружаются и устанавливаются обновления для плагинов, у которых включено автоматическое обновление.", "TaskRefreshPeopleDescription": "Обновляются метаданные актеров и режиссёров в медиатеке.", "TaskCleanLogsDescription": "Удаляются файлы журнала, возраст которых превышает {0} дн(я/ей).", "TaskRefreshLibraryDescription": "Сканируется медиатека на новые файлы и обновляются метаданные.", - "TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые имеют сцены.", + "TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые содержат сцены.", "TaskCleanCacheDescription": "Удаляются файлы кэша, которые больше не нужны системе." } From ecf49caf0d1392b0f2aa83bce1d08a006463566d Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Fri, 17 Apr 2020 10:24:46 -0400 Subject: [PATCH 202/411] Add back warning message when Skia encoder cannot be used --- Jellyfin.Server/CoreAppHost.cs | 11 ++++++++++- .../MediaEncoding/EncodingHelper.cs | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 0769bf844e..f678e714c1 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Reflection; using Emby.Drawing; @@ -42,11 +43,19 @@ namespace Jellyfin.Server /// <inheritdoc/> protected override void RegisterServices(IServiceCollection serviceCollection) { - var imageEncoderType = SkiaEncoder.IsNativeLibAvailable() + // Register an image encoder + bool useSkiaEncoder = SkiaEncoder.IsNativeLibAvailable(); + Type imageEncoderType = useSkiaEncoder ? typeof(SkiaEncoder) : typeof(NullImageEncoder); serviceCollection.AddSingleton(typeof(IImageEncoder), imageEncoderType); + // Log a warning if the Skia encoder could not be used + if (!useSkiaEncoder) + { + Logger.LogWarning($"Skia not available. Will fallback to {nameof(NullImageEncoder)}."); + } + base.RegisterServices(serviceCollection); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3552a45a23..5efb10d3a9 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -459,7 +459,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); var outputVideoCodec = GetVideoEncoder(state, encodingOptions); - + var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; if (!hasTextSubs) From 0bef4eef872c39fc9f747db7648b86f475c4a643 Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Fri, 17 Apr 2020 14:45:56 -0700 Subject: [PATCH 203/411] Fix InvalidOperationException while browsing via DLNA client. --- Emby.Dlna/Didl/DidlBuilder.cs | 102 ++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 88cc00e30a..f9e9de8ff2 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -425,57 +425,99 @@ namespace Emby.Dlna.Didl } } - if (item is Episode episode && context is Season season) + return item is Episode episode + ? GetEpisodeDisplayName(episode, context) + : item.Name; + } + + /// <summary> + /// Gets episode display name appropriate for the given context. + /// </summary> + /// <remarks> + /// If context is a season, this will return a string containing just episode number and name. + /// Otherwise the result will include series nams and season number. + /// </remarks> + /// <param name="episode">The episode.</param> + /// <param name="context">Current context.</param> + /// <returns>Formatted name of the episode.</returns> + private string GetEpisodeDisplayName(Episode episode, BaseItem context) + { + string[] components; + + if (context is Season season) { // This is a special embedded within a season - if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0 + if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value == 0 && season.IndexNumber.HasValue && season.IndexNumber.Value != 0) { return string.Format( CultureInfo.InvariantCulture, _localization.GetLocalizedString("ValueSpecialEpisodeName"), - item.Name); + episode.Name); } - if (item.IndexNumber.HasValue) - { - var number = item.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture); + // inside a season use simple format (ex. '12 - Episode Name') + var epNumberName = GetEpisodeIndexFullName(episode); + components = new[] { epNumberName, episode.Name }; + } + else + { + // outside a season include series and season details (ex. 'TV Show - S05E11 - Episode Name') + var epNumberName = GetEpisodeNumberDisplayName(episode); + components = new[] { episode.SeriesName, epNumberName, episode.Name }; + } - if (episode.IndexNumberEnd.HasValue) - { - number += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture); - } + return string.Join(" - ", components.Where(NotNullOrWhiteSpace)); + } - return number + " - " + item.Name; - } - } - else if (item is Episode ep) + /// <summary> + /// Gets complete episode number. + /// </summary> + /// <param name="episode">The episode.</param> + /// <returns>For single episodes returns just the number. For double episodes - current and ending numbers.</returns> + private string GetEpisodeIndexFullName(Episode episode) + { + var name = string.Empty; + if (episode.IndexNumber.HasValue) { - var parent = ep.GetParent(); - var name = parent.Name + " - "; + name += episode.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture); - if (ep.ParentIndexNumber.HasValue) - { - name += "S" + ep.ParentIndexNumber.Value.ToString("00", CultureInfo.InvariantCulture); - } - else if (!item.IndexNumber.HasValue) + if (episode.IndexNumberEnd.HasValue) { - return name + " - " + item.Name; + name += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture); } + } - name += "E" + ep.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture); - if (ep.IndexNumberEnd.HasValue) - { - name += "-" + ep.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture); - } + return name; + } - name += " - " + item.Name; - return name; + /// <summary> + /// Gets episode number formatted as 'S##E##'. + /// </summary> + /// <param name="episode">The episode.</param> + /// <returns>Formatted episode number.</returns> + private string GetEpisodeNumberDisplayName(Episode episode) + { + var name = string.Empty; + var seasonNumber = episode.Season?.IndexNumber; + + if (seasonNumber.HasValue) + { + name = "S" + seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture); + } + + var indexName = GetEpisodeIndexFullName(episode); + + if (!string.IsNullOrWhiteSpace(indexName)) + { + name += "E" + indexName; } - return item.Name; + return name; } + private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s); + private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null) { writer.WriteStartElement(string.Empty, "res", NS_DIDL); From 156998dd831c4c9c56326fe700197b4f7985fffb Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Fri, 17 Apr 2020 22:25:54 -0700 Subject: [PATCH 204/411] Add mime types for ape and wv files. --- MediaBrowser.Model/Net/MimeTypes.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 68bcc590c4..0418474cea 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -123,6 +123,8 @@ namespace MediaBrowser.Model.Net { ".xsp", "audio/xsp" }, { ".dsp", "audio/dsp" }, { ".flac", "audio/flac" }, + { ".ape", "audio/x-ape" }, + { ".wv", "audio/x-wavpack" }, }; private static readonly Dictionary<string, string> _extensionLookup = CreateExtensionLookup(); From 25da2cb2d7cfa762716a651aba87a53522b2d9f9 Mon Sep 17 00:00:00 2001 From: Julien Machiels <julien.machiels@protonmail.com> Date: Sat, 18 Apr 2020 05:42:24 +0000 Subject: [PATCH 205/411] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index d1403c494d..ec29d33748 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -15,7 +15,7 @@ "Favorites": "Favoris", "Folders": "Dossiers", "Genres": "Genres", - "HeaderAlbumArtists": "Artistes d'album", + "HeaderAlbumArtists": "Artistes de l'album", "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", "HeaderFavoriteAlbums": "Albums favoris", From 2e10d385f035bfdd9651a95a4dab18d849766522 Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Fri, 17 Apr 2020 23:11:52 -0700 Subject: [PATCH 206/411] Add mime type for .mpegts files. --- 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 0418474cea..06efedb300 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -106,6 +106,7 @@ namespace MediaBrowser.Model.Net { ".3g2", "video/3gpp2" }, { ".mpd", "video/vnd.mpeg.dash.mpd" }, { ".ts", "video/mp2t" }, + { ".mpegts", "video/mp2t" }, // Type audio { ".mp3", "audio/mpeg" }, From e333e6765a931904c8b3b06b8e3a95bb5f03b1ad Mon Sep 17 00:00:00 2001 From: Klanc <clank201@gmail.com> Date: Sat, 18 Apr 2020 16:24:18 +0000 Subject: [PATCH 207/411] Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 2d82993675..7464ac1c09 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -3,19 +3,19 @@ "AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}", "Application": "Aplicació", "Artists": "Artistes", - "AuthenticationSucceededWithUserName": "{0} s'ha autentificat correctament", + "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", "Books": "Llibres", - "CameraImageUploadedFrom": "Una nova imatge de la càmera ha sigut pujada des de {0}", + "CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}", "Channels": "Canals", - "ChapterNameValue": "Episodi {0}", + "ChapterNameValue": "Capítol {0}", "Collections": "Col·leccions", "DeviceOfflineWithName": "{0} s'ha desconnectat", "DeviceOnlineWithName": "{0} està connectat", "FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}", "Favorites": "Preferits", - "Folders": "Directoris", + "Folders": "Carpetes", "Genres": "Gèneres", - "HeaderAlbumArtists": "Artistes dels Àlbums", + "HeaderAlbumArtists": "Artistes del Àlbum", "HeaderCameraUploads": "Pujades de Càmera", "HeaderContinueWatching": "Continua Veient", "HeaderFavoriteAlbums": "Àlbums Preferits", From d7a71cee3ced19b43bdc1ee1523f236de15de26a Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Sat, 18 Apr 2020 17:26:22 -0700 Subject: [PATCH 208/411] Fix imdbid regex --- Emby.Server.Implementations/Library/PathExtensions.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 4fdf73b773..5e863b82d4 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library // for imdbid we also accept pattern matching if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) { - var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase); + var m = Regex.Match(str, "tt\\d{7,}", RegexOptions.IgnoreCase); return m.Success ? m.Value : null; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 36b9a9c1f8..82802041e9 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -208,8 +208,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { - // Look for a match for the Regex pattern "tt" followed by 7 digits - var m = Regex.Match(xml, @"tt([0-9]{7})", RegexOptions.IgnoreCase); + // Look for a match for the Regex pattern "tt" followed by 7 or more digits + var m = Regex.Match(xml, @"tt(\d{7,})", RegexOptions.IgnoreCase); if (m.Success) { item.SetProviderId(MetadataProviders.Imdb, m.Value); From 92f273cb0cd94923be25b6b69147eeb9b86749b0 Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Sat, 18 Apr 2020 18:18:48 -0700 Subject: [PATCH 209/411] Limit imdbid to 8 digits. --- Emby.Server.Implementations/Library/PathExtensions.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 5e863b82d4..67c0e4eae7 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library // for imdbid we also accept pattern matching if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) { - var m = Regex.Match(str, "tt\\d{7,}", RegexOptions.IgnoreCase); + var m = Regex.Match(str, "tt\\d{7,8}", RegexOptions.IgnoreCase); return m.Success ? m.Value : null; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 82802041e9..0ac94834ea 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -208,8 +208,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { - // Look for a match for the Regex pattern "tt" followed by 7 or more digits - var m = Regex.Match(xml, @"tt(\d{7,})", RegexOptions.IgnoreCase); + // Look for a match for the Regex pattern "tt" followed by 7 or 8 digits + var m = Regex.Match(xml, @"tt(\d{7,8})", RegexOptions.IgnoreCase); if (m.Success) { item.SetProviderId(MetadataProviders.Imdb, m.Value); From d62bd7fecd3789cacad6618c9992bb028a5049fa Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Sun, 19 Apr 2020 11:46:22 +0900 Subject: [PATCH 210/411] fix spelling error --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index d394c56ad2..464ca3a0b5 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.HttpServer // Last-Modified and If-Modified-Since must follow strict date format, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since private const string HttpDateFormat = "ddd, dd MMM yyyy HH:mm:ss \"GMT\""; - // We specifically use en-US culture because both day of week and month names must be in it9 + // We specifically use en-US culture because both day of week and month names require it private static readonly CultureInfo _enUSculture = new CultureInfo("en-US", false); /// <summary> From 16401ec7ae078b2b7c4c65e3792cb4c4159490d2 Mon Sep 17 00:00:00 2001 From: tayhr <lextayar@gmail.com> Date: Sat, 18 Apr 2020 23:03:04 +0000 Subject: [PATCH 211/411] Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 661ee8603e..25c5b9053f 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -95,5 +95,13 @@ "TaskCleanCache": "Limpar Diretório de Cache", "TasksApplicationCategory": "Aplicação", "TasksLibraryCategory": "Biblioteca", - "TasksMaintenanceCategory": "Manutenção" + "TasksMaintenanceCategory": "Manutenção", + "TaskRefreshChannels": "Atualizar Canais", + "TaskUpdatePlugins": "Atualizar Plugins", + "TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.", + "TaskCleanLogs": "Limpar diretório de log", + "TaskRefreshLibrary": "Escanear biblioteca de mídias", + "TaskRefreshChapterImagesDescription": "Criar miniaturas para videos que tem capítulos.", + "TaskCleanCacheDescription": "Deletar arquivos de cache que não são mais usados pelo sistema.", + "TasksChannelsCategory": "Canais de Internet" } From a73e1f18b670ac7c9f0f9c3049e7fbbeddb61942 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sun, 19 Apr 2020 11:16:09 +0200 Subject: [PATCH 212/411] Minor improvements --- MediaBrowser.Api/UserLibrary/ItemsService.cs | 4 ++-- MediaBrowser.Controller/Entities/BaseItem.cs | 5 ++--- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index ac59c30301..c4d44042b1 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -242,11 +242,11 @@ namespace MediaBrowser.Api.UserLibrary return folder.GetItems(GetItemsQuery(request, dtoOptions, user)); } - var itemsArray = folder.GetChildren(user, true).ToArray(); + var itemsArray = folder.GetChildren(user, true); return new QueryResult<BaseItem> { Items = itemsArray, - TotalRecordCount = itemsArray.Length, + TotalRecordCount = itemsArray.Count, StartIndex = 0 }; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 56a361e0ec..b9e1fe79d5 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2741,7 +2741,7 @@ namespace MediaBrowser.Controller.Entities { var list = GetEtagValues(user); - return string.Join("|", list.ToArray()).GetMD5().ToString("N", CultureInfo.InvariantCulture); + return string.Join("|", list).GetMD5().ToString("N", CultureInfo.InvariantCulture); } protected virtual List<string> GetEtagValues(User user) @@ -2784,8 +2784,7 @@ namespace MediaBrowser.Controller.Entities return true; } - var view = this as IHasCollectionType; - if (view != null) + if (this is IHasCollectionType view) { if (string.Equals(view.CollectionType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bb48605e55..a468e0c35a 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -864,7 +864,7 @@ namespace MediaBrowser.Controller.Entities return SortItemsByRequest(query, result); } - return result.ToArray(); + return result; } return GetItemsInternal(query).Items; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index c2bfac9d63..3593304491 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -478,7 +478,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } catch (Exception ex) { - _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {arguments}", inputArgument); + _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {Arguments}", inputArgument); } } @@ -969,7 +969,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public int? ExitCode { get; private set; } - void OnProcessExited(object sender, EventArgs e) + private void OnProcessExited(object sender, EventArgs e) { var process = (Process)sender; From d99536e99fe7e8d204ef93db39e519afbbfdad0a Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sun, 19 Apr 2020 11:57:03 +0200 Subject: [PATCH 213/411] Improved tests --- Emby.Naming/Audio/AlbumParser.cs | 15 ++-- Emby.Naming/Audio/AudioFileParser.cs | 3 +- Emby.Naming/Common/EpisodeExpression.cs | 7 +- Emby.Naming/Subtitles/SubtitleParser.cs | 12 +-- .../Music/MultiDiscAlbumTests.cs | 88 ++++++++----------- .../Subtitles/SubtitleParserTests.cs | 49 ++++++----- 6 files changed, 80 insertions(+), 94 deletions(-) diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 33f4468d9f..23ff8c3fa2 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -1,3 +1,4 @@ +#nullable enable #pragma warning disable CS1591 using System; @@ -22,7 +23,7 @@ namespace Emby.Naming.Audio { var filename = Path.GetFileName(path); - if (string.IsNullOrEmpty(filename)) + if (path.Length == 0) { return false; } @@ -39,18 +40,22 @@ namespace Emby.Naming.Audio filename = filename.Replace(')', ' '); filename = Regex.Replace(filename, @"\s+", " "); - filename = filename.TrimStart(); + ReadOnlySpan<char> trimmedFilename = filename.TrimStart(); foreach (var prefix in _options.AlbumStackingPrefixes) { - if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0) + if (!trimmedFilename.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { continue; } - var tmp = filename.Substring(prefix.Length); + var tmp = trimmedFilename.Slice(prefix.Length).Trim(); - tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty; + int index = tmp.IndexOf(' '); + if (index != -1) + { + tmp = tmp.Slice(0, index); + } if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) { diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs index 25d5f8735e..6b2f4be93e 100644 --- a/Emby.Naming/Audio/AudioFileParser.cs +++ b/Emby.Naming/Audio/AudioFileParser.cs @@ -1,3 +1,4 @@ +#nullable enable #pragma warning disable CS1591 using System; @@ -11,7 +12,7 @@ namespace Emby.Naming.Audio { public static bool IsAudioFile(string path, NamingOptions options) { - var extension = Path.GetExtension(path) ?? string.Empty; + var extension = Path.GetExtension(path); return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } } diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index 07de728514..ed6ba8881c 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -23,11 +23,6 @@ namespace Emby.Naming.Common { } - public EpisodeExpression() - : this(null) - { - } - public string Expression { get => _expression; @@ -48,6 +43,6 @@ namespace Emby.Naming.Common public string[] DateTimeFormats { get; set; } - public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); + public Regex Regex => _regex ??= new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled); } } diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs index 88ec3e2d60..542584bee9 100644 --- a/Emby.Naming/Subtitles/SubtitleParser.cs +++ b/Emby.Naming/Subtitles/SubtitleParser.cs @@ -1,3 +1,4 @@ +#nullable enable #pragma warning disable CS1591 using System; @@ -16,11 +17,11 @@ namespace Emby.Naming.Subtitles _options = options; } - public SubtitleInfo ParseFile(string path) + public SubtitleInfo? ParseFile(string path) { - if (string.IsNullOrEmpty(path)) + if (path.Length == 0) { - throw new ArgumentNullException(nameof(path)); + throw new ArgumentException("String can't be empty.", nameof(path)); } var extension = Path.GetExtension(path); @@ -52,11 +53,6 @@ namespace Emby.Naming.Subtitles private string[] GetFlags(string path) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - // Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _. var file = Path.GetFileName(path); diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs index 9a4b0b5422..45d5df09af 100644 --- a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs +++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs @@ -6,61 +6,43 @@ namespace Jellyfin.Naming.Tests.Music { public class MultiDiscAlbumTests { - [Fact] - public void TestMultiDiscAlbums() + private readonly NamingOptions _namingOptions = new NamingOptions(); + + [Theory] + [InlineData("", false)] + [InlineData(@"blah blah", false)] + [InlineData(@"D:/music/weezer/03 Pinkerton", false)] + [InlineData(@"D:/music/michael jackson/Bad (2012 Remaster)", false)] + [InlineData(@"cd1", true)] + [InlineData(@"disc18", true)] + [InlineData(@"disk10", true)] + [InlineData(@"vol7", true)] + [InlineData(@"volume1", true)] + [InlineData(@"cd 1", true)] + [InlineData(@"disc 1", true)] + [InlineData(@"disk 1", true)] + [InlineData(@"disk", false)] + [InlineData(@"disk ·", false)] + [InlineData(@"disk a", false)] + [InlineData(@"disk volume", false)] + [InlineData(@"disc disc", false)] + [InlineData(@"disk disc 6", false)] + [InlineData(@"cd - 1", true)] + [InlineData(@"disc- 1", true)] + [InlineData(@"disk - 1", true)] + [InlineData(@"Disc 01 (Hugo Wolf · 24 Lieder)", true)] + [InlineData(@"Disc 04 (Encores and Folk Songs)", true)] + [InlineData(@"Disc04 (Encores and Folk Songs)", true)] + [InlineData(@"Disc 04(Encores and Folk Songs)", true)] + [InlineData(@"Disc04(Encores and Folk Songs)", true)] + [InlineData(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2", true)] + [InlineData(@"[1985] Opportunities (Let's make lots of money) (1985)", false)] + [InlineData(@"Blah 04(Encores and Folk Songs)", false)] + public void TestMultiDiscAlbums(string path, bool result) { - Assert.False(IsMultiDiscAlbumFolder(@"blah blah")); - Assert.False(IsMultiDiscAlbumFolder(@"D:/music/weezer/03 Pinkerton")); - Assert.False(IsMultiDiscAlbumFolder(@"D:/music/michael jackson/Bad (2012 Remaster)")); + var parser = new AlbumParser(_namingOptions); - Assert.True(IsMultiDiscAlbumFolder(@"cd1")); - Assert.True(IsMultiDiscAlbumFolder(@"disc18")); - Assert.True(IsMultiDiscAlbumFolder(@"disk10")); - Assert.True(IsMultiDiscAlbumFolder(@"vol7")); - Assert.True(IsMultiDiscAlbumFolder(@"volume1")); - - Assert.True(IsMultiDiscAlbumFolder(@"cd 1")); - Assert.True(IsMultiDiscAlbumFolder(@"disc 1")); - Assert.True(IsMultiDiscAlbumFolder(@"disk 1")); - - Assert.False(IsMultiDiscAlbumFolder(@"disk")); - Assert.False(IsMultiDiscAlbumFolder(@"disk ·")); - Assert.False(IsMultiDiscAlbumFolder(@"disk a")); - - Assert.False(IsMultiDiscAlbumFolder(@"disk volume")); - Assert.False(IsMultiDiscAlbumFolder(@"disc disc")); - Assert.False(IsMultiDiscAlbumFolder(@"disk disc 6")); - - Assert.True(IsMultiDiscAlbumFolder(@"cd - 1")); - Assert.True(IsMultiDiscAlbumFolder(@"disc- 1")); - Assert.True(IsMultiDiscAlbumFolder(@"disk - 1")); - - Assert.True(IsMultiDiscAlbumFolder(@"Disc 01 (Hugo Wolf · 24 Lieder)")); - Assert.True(IsMultiDiscAlbumFolder(@"Disc 04 (Encores and Folk Songs)")); - Assert.True(IsMultiDiscAlbumFolder(@"Disc04 (Encores and Folk Songs)")); - Assert.True(IsMultiDiscAlbumFolder(@"Disc 04(Encores and Folk Songs)")); - Assert.True(IsMultiDiscAlbumFolder(@"Disc04(Encores and Folk Songs)")); - - Assert.True(IsMultiDiscAlbumFolder(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2")); - } - - [Fact] - public void TestMultiDiscAlbums1() - { - Assert.False(IsMultiDiscAlbumFolder(@"[1985] Opportunities (Let's make lots of money) (1985)")); - } - - [Fact] - public void TestMultiDiscAlbums2() - { - Assert.False(IsMultiDiscAlbumFolder(@"Blah 04(Encores and Folk Songs)")); - } - - private bool IsMultiDiscAlbumFolder(string path) - { - var parser = new AlbumParser(new NamingOptions()); - - return parser.IsMultiPart(path); + Assert.Equal(result, parser.IsMultiPart(path)); } } } diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index 41da889c28..8b9425e8e0 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -1,4 +1,5 @@ -using Emby.Naming.Common; +using System; +using Emby.Naming.Common; using Emby.Naming.Subtitles; using Xunit; @@ -6,28 +7,19 @@ namespace Jellyfin.Naming.Tests.Subtitles { public class SubtitleParserTests { - private SubtitleParser GetParser() - { - var options = new NamingOptions(); - - return new SubtitleParser(options); - } - - [Fact] - public void TestSubtitles() - { - Test("The Skin I Live In (2011).srt", null, false, false); - Test("The Skin I Live In (2011).eng.srt", "eng", false, false); - Test("The Skin I Live In (2011).eng.default.srt", "eng", true, false); - Test("The Skin I Live In (2011).eng.forced.srt", "eng", false, true); - Test("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true); - Test("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true); - Test("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true); - } + private readonly NamingOptions _namingOptions = new NamingOptions(); - private void Test(string input, string language, bool isDefault, bool isForced) + [Theory] + [InlineData("The Skin I Live In (2011).srt", null, false, false)] + [InlineData("The Skin I Live In (2011).eng.srt", "eng", false, false)] + [InlineData("The Skin I Live In (2011).eng.default.srt", "eng", true, false)] + [InlineData("The Skin I Live In (2011).eng.forced.srt", "eng", false, true)] + [InlineData("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true)] + [InlineData("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true)] + [InlineData("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true)] + public void TestSubtitles(string input, string language, bool isDefault, bool isForced) { - var parser = GetParser(); + var parser = new SubtitleParser(_namingOptions); var result = parser.ParseFile(input); @@ -35,5 +27,20 @@ namespace Jellyfin.Naming.Tests.Subtitles Assert.Equal(isDefault, result.IsDefault); Assert.Equal(isForced, result.IsForced); } + + [Theory] + [InlineData("The Skin I Live In (2011).mp4")] + public void TestNonSubtitles(string input) + { + var parser = new SubtitleParser(_namingOptions); + + Assert.Null(parser.ParseFile(input)); + } + + [Fact] + public void TestEmptySubtitlesPath() + { + Assert.Throws<ArgumentException>(() => new SubtitleParser(_namingOptions).ParseFile(string.Empty)); + } } } From fc3e2baccc8c32171df655043feb7afae6bab34c Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sun, 19 Apr 2020 18:27:07 +0200 Subject: [PATCH 214/411] Address comments --- Emby.Naming/Audio/AlbumParser.cs | 4 +--- Emby.Naming/Subtitles/SubtitleParser.cs | 2 +- .../Extensions/StringHelperTests.cs | 19 +++++++++++++++++ .../Jellyfin.Model.Tests.csproj | 21 +++++++++++++++++++ .../Music/MultiDiscAlbumTests.cs | 4 +++- .../Subtitles/SubtitleParserTests.cs | 6 +++--- 6 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs create mode 100644 tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 23ff8c3fa2..b63be3a647 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; using System.IO; -using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; @@ -22,8 +21,7 @@ namespace Emby.Naming.Audio public bool IsMultiPart(string path) { var filename = Path.GetFileName(path); - - if (path.Length == 0) + if (filename.Length == 0) { return false; } diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs index 542584bee9..24e59f90a3 100644 --- a/Emby.Naming/Subtitles/SubtitleParser.cs +++ b/Emby.Naming/Subtitles/SubtitleParser.cs @@ -21,7 +21,7 @@ namespace Emby.Naming.Subtitles { if (path.Length == 0) { - throw new ArgumentException("String can't be empty.", nameof(path)); + throw new ArgumentException("File path can't be empty.", nameof(path)); } var extension = Path.GetExtension(path); diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs new file mode 100644 index 0000000000..d68cf0f7c0 --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs @@ -0,0 +1,19 @@ +using System; +using MediaBrowser.Model.Extensions; +using Xunit; + +namespace Jellyfin.Model.Tests.Extensions +{ + public class StringHelperTests + { + [Theory] + [InlineData("", "")] + [InlineData("banana", "Banana")] + [InlineData("Banana", "Banana")] + [InlineData("ä", "Ä")] + public void FirstToUpperTest(string str, string result) + { + Assert.Equal(result, StringHelper.FirstToUpper(str)); + } + } +} diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj new file mode 100644 index 0000000000..f6c3274986 --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -0,0 +1,21 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> + <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> + <PackageReference Include="coverlet.collector" Version="1.2.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" /> + </ItemGroup> + +</Project> diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs index 45d5df09af..c9a295a4ce 100644 --- a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs +++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs @@ -10,6 +10,8 @@ namespace Jellyfin.Naming.Tests.Music [Theory] [InlineData("", false)] + [InlineData("C:/", false)] + [InlineData("/home/", false)] [InlineData(@"blah blah", false)] [InlineData(@"D:/music/weezer/03 Pinkerton", false)] [InlineData(@"D:/music/michael jackson/Bad (2012 Remaster)", false)] @@ -38,7 +40,7 @@ namespace Jellyfin.Naming.Tests.Music [InlineData(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2", true)] [InlineData(@"[1985] Opportunities (Let's make lots of money) (1985)", false)] [InlineData(@"Blah 04(Encores and Folk Songs)", false)] - public void TestMultiDiscAlbums(string path, bool result) + public void AlbumParser_MultidiscPath_Identifies(string path, bool result) { var parser = new AlbumParser(_namingOptions); diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index 8b9425e8e0..a438318ca7 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -17,7 +17,7 @@ namespace Jellyfin.Naming.Tests.Subtitles [InlineData("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true)] [InlineData("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true)] [InlineData("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true)] - public void TestSubtitles(string input, string language, bool isDefault, bool isForced) + public void SubtitleParser_ValidFileNames_Parses(string input, string language, bool isDefault, bool isForced) { var parser = new SubtitleParser(_namingOptions); @@ -30,7 +30,7 @@ namespace Jellyfin.Naming.Tests.Subtitles [Theory] [InlineData("The Skin I Live In (2011).mp4")] - public void TestNonSubtitles(string input) + public void SubtitleParser_InvalidFileNames_ReturnsNull(string input) { var parser = new SubtitleParser(_namingOptions); @@ -38,7 +38,7 @@ namespace Jellyfin.Naming.Tests.Subtitles } [Fact] - public void TestEmptySubtitlesPath() + public void SubtitleParser_EmptyFileNames_ThrowsArgumentException() { Assert.Throws<ArgumentException>(() => new SubtitleParser(_namingOptions).ParseFile(string.Empty)); } From f26f44acaf86bb35ff1d2be7cb37a57dee7a47cf Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Sun, 19 Apr 2020 13:39:12 -0400 Subject: [PATCH 215/411] Apply code review suggestions --- .../Branding/BrandingConfigurationFactory.cs | 2 +- .../Channels/ChannelManager.cs | 67 ++++++------------- .../Channels/ChannelPostScanTask.cs | 3 +- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs index 43c8cd5fa2..7ae26bd8b4 100644 --- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs +++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Branding; namespace Emby.Server.Implementations.Branding { /// <summary> - /// Branding configuration factory. + /// A configuration factory for <see cref="BrandingOptions"/>. /// </summary> public class BrandingConfigurationFactory : IConfigurationFactory { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 8beb5866f1..41eb58bab0 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -141,9 +141,9 @@ namespace Emby.Server.Implementations.Channels } /// <summary> - /// Returns an <see cref="IEnumerable{T}"/> containing installed channel ID's. + /// Get the installed channel IDs. /// </summary> - /// <returns>An <see cref="IEnumerable{T}"/> containing installed channel ID's.</returns> + /// <returns>An <see cref="IEnumerable{T}"/> containing installed channel IDs.</returns> public IEnumerable<Guid> GetInstalledChannelIds() { return GetAllChannels().Select(i => GetInternalChannelId(i.Name)); @@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.Channels /// Refreshes the associated channels. /// </summary> /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param> /// <returns>The completed task.</returns> public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken) { @@ -384,8 +384,8 @@ namespace Emby.Server.Implementations.Channels /// Gets the dynamic media sources based on the provided item. /// </summary> /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>The completed task.</returns> + /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param> + /// <returns>The task representing the operation to get the media sources.</returns> public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken) { var channel = GetChannel(item.ChannelId); @@ -578,7 +578,8 @@ namespace Emby.Server.Implementations.Channels /// <param name="provider">The provider.</param> /// <param name="features">The features.</param> /// <returns>The supported features.</returns> - public ChannelFeatures GetChannelFeaturesDto(Channel channel, + public ChannelFeatures GetChannelFeaturesDto( + Channel channel, IChannel provider, InternalChannelFeatures features) { @@ -961,27 +962,15 @@ namespace Emby.Server.Implementations.Channels if (info.Type == ChannelItemType.Folder) { - switch (info.FolderType) + item = info.FolderType switch { - case ChannelFolderType.MusicAlbum: - item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew); - break; - case ChannelFolderType.MusicArtist: - item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew); - break; - case ChannelFolderType.PhotoAlbum: - item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew); - break; - case ChannelFolderType.Series: - item = GetItemById<Series>(info.Id, channelProvider.Name, out isNew); - break; - case ChannelFolderType.Season: - item = GetItemById<Season>(info.Id, channelProvider.Name, out isNew); - break; - default: - item = GetItemById<Folder>(info.Id, channelProvider.Name, out isNew); - break; - } + ChannelFolderType.MusicAlbum => GetItemById<MusicAlbum>(info.Id, channelProvider.Name, out isNew), + ChannelFolderType.MusicArtist => GetItemById<MusicArtist>(info.Id, channelProvider.Name, out isNew), + ChannelFolderType.PhotoAlbum => GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, out isNew), + ChannelFolderType.Series => GetItemById<Series>(info.Id, channelProvider.Name, out isNew), + ChannelFolderType.Season => GetItemById<Season>(info.Id, channelProvider.Name, out isNew), + _ => GetItemById<Folder>(info.Id, channelProvider.Name, out isNew) + }; } else if (info.MediaType == ChannelMediaType.Audio) { @@ -991,26 +980,14 @@ namespace Emby.Server.Implementations.Channels } else { - switch (info.ContentType) + item = info.ContentType switch { - case ChannelMediaContentType.Episode: - item = GetItemById<Episode>(info.Id, channelProvider.Name, out isNew); - break; - case ChannelMediaContentType.Movie: - item = GetItemById<Movie>(info.Id, channelProvider.Name, out isNew); - break; - default: - if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer) - { - item = GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew); - } - else - { - item = GetItemById<Video>(info.Id, channelProvider.Name, out isNew); - } - - break; - } + ChannelMediaContentType.Episode => GetItemById<Episode>(info.Id, channelProvider.Name, out isNew), + ChannelMediaContentType.Movie => GetItemById<Movie>(info.Id, channelProvider.Name, out isNew), + var x when x == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer + => GetItemById<Trailer>(info.Id, channelProvider.Name, out isNew), + _ => GetItemById<Video>(info.Id, channelProvider.Name, out isNew) + }; } var enableMediaProbe = channelProvider is ISupportsMediaProbe; diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index f48b0a7faa..eeb49b8fef 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -10,8 +10,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Channels { /// <summary> - /// Channel post scan task. - /// This task removes all non-installed channels from the database. + /// A task to remove all non-installed channels from the database. /// </summary> public class ChannelPostScanTask { From d30fd3b3d2e935f865e7560629d72e87cb0c760d Mon Sep 17 00:00:00 2001 From: randrey <randrey-gh@outlook.com> Date: Sun, 19 Apr 2020 14:14:04 -0700 Subject: [PATCH 216/411] Changed '\d' to '[0-9]'. --- Emby.Server.Implementations/Library/PathExtensions.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 67c0e4eae7..1d61ed57eb 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library // for imdbid we also accept pattern matching if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) { - var m = Regex.Match(str, "tt\\d{7,8}", RegexOptions.IgnoreCase); + var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase); return m.Success ? m.Value : null; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 0ac94834ea..5c8de80f17 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -209,7 +209,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected void ParseProviderLinks(T item, string xml) { // Look for a match for the Regex pattern "tt" followed by 7 or 8 digits - var m = Regex.Match(xml, @"tt(\d{7,8})", RegexOptions.IgnoreCase); + var m = Regex.Match(xml, "tt([0-9]{7,8})", RegexOptions.IgnoreCase); if (m.Success) { item.SetProviderId(MetadataProviders.Imdb, m.Value); From 6039c6200f78e6ae174d33efc3c3437cb94fa633 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 19 Apr 2020 18:18:53 -0400 Subject: [PATCH 217/411] Update instructions for running with the dotnet cli --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6007aa3cb9..7a81db6552 100644 --- a/README.md +++ b/README.md @@ -125,9 +125,8 @@ After the required extensions are installed, you can can run the server by press To run the server from the command line you can use the `dotnet run` command. The example below shows how to do this if you have cloned the repository into a directory named `jellyfin` (the default directory name) and should work on all operating systems. ```bash -cd jellyfin # Move into the repository directory -cd Jellyfin.Server # Move into the server startup project directory -dotnet run # Run the server startup project +cd jellyfin # Move into the repository directory +dotnet run --project Jellyfin.Server # Run the server startup project ``` A second option is to build the project and then run the resulting executable file directly. When running the executable directly you can easily add command line options. Add the `--help` flag to list details on all the supported command line options. From 42781c4d4b54a010a185c71616090724f985631c Mon Sep 17 00:00:00 2001 From: Jay-Jay <jayjay40@gmx.de> Date: Sun, 19 Apr 2020 22:15:33 +0000 Subject: [PATCH 218/411] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 414430ff7b..82df43be11 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Gerät: {1}", "Application": "Anwendung", "Artists": "Interpreten", - "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifziert", + "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich authentifiziert", "Books": "Bücher", "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen", "Channels": "Kanäle", @@ -99,11 +99,11 @@ "TaskRefreshChannels": "Erneuere Kanäle", "TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.", "TaskCleanTranscode": "Lösche Transkodier Pfad", - "TaskUpdatePluginsDescription": "Läd Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.", + "TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.", "TaskUpdatePlugins": "Update Plugins", "TaskRefreshPeopleDescription": "Erneuert Metadaten für Schausteller und Regisseure in deinen Bibliotheken.", "TaskRefreshPeople": "Erneuere Schausteller", - "TaskCleanLogsDescription": "Lösche Log Datein die älter als {0} Tage sind.", + "TaskCleanLogsDescription": "Lösche Log Dateien die älter als {0} Tage sind.", "TaskCleanLogs": "Lösche Log Pfad", "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.", "TaskRefreshLibrary": "Scanne alle Bibliotheken", From e6ef680775be122c3fcfbe64d4b8d868197b6f5e Mon Sep 17 00:00:00 2001 From: dkanada <dkanada@users.noreply.github.com> Date: Mon, 20 Apr 2020 14:27:46 +0900 Subject: [PATCH 219/411] add code suggestions --- MediaBrowser.Common/Extensions/BaseExtensions.cs | 1 + MediaBrowser.Model/Updates/VersionInfo.cs | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index bc002e5233..08964420e7 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.Common.Extensions { return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str))); } + #pragma warning restore CA5351 } } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index e67e60d745..fe5826ad2f 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -1,6 +1,3 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - using System; namespace MediaBrowser.Model.Updates From ed1dc5c9222fdd1211caca2463f8bf5d6c4bb27d Mon Sep 17 00:00:00 2001 From: Anthony Lavado <anthonylavado@me.com> Date: Mon, 20 Apr 2020 02:35:47 -0400 Subject: [PATCH 220/411] Remove JsonIgnore from the DateLastSaved property of BaseItem --- MediaBrowser.Controller/Entities/BaseItem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index b9e1fe79d5..7ed8fa7671 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -549,7 +549,6 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public DateTime DateModified { get; set; } - [JsonIgnore] public DateTime DateLastSaved { get; set; } [JsonIgnore] From a8b59c5d216c5a79972162dd13b8319fa8986cc3 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Mon, 20 Apr 2020 09:53:36 +0200 Subject: [PATCH 221/411] Rename test --- tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs index d68cf0f7c0..7b37b49a96 100644 --- a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs +++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Model.Tests.Extensions [InlineData("banana", "Banana")] [InlineData("Banana", "Banana")] [InlineData("ä", "Ä")] - public void FirstToUpperTest(string str, string result) + public void StringHelper_ValidArgs_Success(string str, string result) { Assert.Equal(result, StringHelper.FirstToUpper(str)); } From 7f4a229cd2fee89fdd8329c9c9f907e381d45c46 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sun, 19 Apr 2020 15:18:28 +0200 Subject: [PATCH 222/411] Add some simple tests --- .../HttpServer/ResponseFilter.cs | 4 ++ .../Library/MediaStreamSelector.cs | 6 ++- .../Library/PathExtensions.cs | 22 ++++++----- .../Library/ResolverHelper.cs | 2 + .../Services/StringMapTypeDeserializer.cs | 16 +------- .../Services/UrlExtensions.cs | 20 ++-------- .../SocketSharp/WebSocketSharpRequest.cs | 16 ++------ .../Playback/BaseStreamingService.cs | 3 +- .../Extensions/BaseExtensions.cs | 2 + .../Extensions/CopyToExtensions.cs | 2 + .../Extensions/MethodNotAllowedException.cs | 2 + .../Extensions/ProcessExtensions.cs | 2 + .../Extensions/RateLimitExceededException.cs | 1 + .../Extensions/ResourceNotFoundException.cs | 2 + .../Extensions/ShuffleExtensions.cs | 2 + .../Extensions/StringExtensions.cs | 37 +++++++++++++++++++ .../Entities/ProviderIdsExtensions.cs | 8 ++-- .../Extensions/StringExtensionsTests.cs | 35 ++++++++++++++++++ .../HttpServer/ResponseFilterTests.cs | 16 ++++++++ .../Library/PathExtensionsTests.cs | 17 +++++++++ 20 files changed, 155 insertions(+), 60 deletions(-) create mode 100644 MediaBrowser.Common/Extensions/StringExtensions.cs create mode 100644 tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs diff --git a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs index 5e0466629d..4089aa578e 100644 --- a/Emby.Server.Implementations/HttpServer/ResponseFilter.cs +++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs @@ -82,6 +82,10 @@ namespace Emby.Server.Implementations.HttpServer { return null; } + else if (inString.Length == 0) + { + return inString; + } var newString = new StringBuilder(inString.Length); diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 6b9f4d052c..e27145a1d2 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -35,7 +35,8 @@ namespace Emby.Server.Implementations.Library return null; } - public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams, + public static int? GetDefaultSubtitleStreamIndex( + List<MediaStream> streams, string[] preferredLanguages, SubtitlePlaybackMode mode, string audioTrackLanguage) @@ -115,7 +116,8 @@ namespace Emby.Server.Implementations.Library .ThenBy(i => i.Index); } - public static void SetSubtitleStreamScores(List<MediaStream> streams, + public static void SetSubtitleStreamScores( + List<MediaStream> streams, string[] preferredLanguages, SubtitlePlaybackMode mode, string audioTrackLanguage) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 1d61ed57eb..b74cad067b 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Text.RegularExpressions; @@ -12,24 +14,24 @@ namespace Emby.Server.Implementations.Library /// Gets the attribute value. /// </summary> /// <param name="str">The STR.</param> - /// <param name="attrib">The attrib.</param> + /// <param name="attribute">The attrib.</param> /// <returns>System.String.</returns> - /// <exception cref="ArgumentNullException">attrib</exception> - public static string GetAttributeValue(this string str, string attrib) + /// <exception cref="ArgumentException"><c>str</c> or <c>attribute</c> is empty.</exception> + public static string? GetAttributeValue(this string str, string attribute) { - if (string.IsNullOrEmpty(str)) + if (str.Length == 0) { - throw new ArgumentNullException(nameof(str)); + throw new ArgumentException("String can't be empty.", nameof(str)); } - if (string.IsNullOrEmpty(attrib)) + if (attribute.Length == 0) { - throw new ArgumentNullException(nameof(attrib)); + throw new ArgumentException("String can't be empty.", nameof(attribute)); } - string srch = "[" + attrib + "="; + string srch = "[" + attribute + "="; int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - if (start > -1) + if (start != -1) { start += srch.Length; int end = str.IndexOf(']', start); @@ -37,7 +39,7 @@ namespace Emby.Server.Implementations.Library } // for imdbid we also accept pattern matching - if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase)) { var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase); return m.Success ? m.Value : null; diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 34dcbbe285..7ca15b4e55 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -118,10 +118,12 @@ namespace Emby.Server.Implementations.Library { throw new ArgumentNullException(nameof(fileSystem)); } + if (item == null) { throw new ArgumentNullException(nameof(item)); } + if (args == null) { throw new ArgumentNullException(nameof(args)); diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs index 23e22afd58..56e23d5492 100644 --- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs +++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using MediaBrowser.Common.Extensions; namespace Emby.Server.Implementations.Services { @@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Services if (propertySerializerEntry.PropertyType == typeof(bool)) { //InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value - propertyTextValue = LeftPart(propertyTextValue, ','); + propertyTextValue = StringExtensions.LeftPart(propertyTextValue, ',').ToString(); } var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue); @@ -95,19 +96,6 @@ namespace Emby.Server.Implementations.Services return instance; } - - public static string LeftPart(string strVal, char needle) - { - if (strVal == null) - { - return null; - } - - var pos = strVal.IndexOf(needle); - return pos == -1 - ? strVal - : strVal.Substring(0, pos); - } } internal static class TypeAccessor diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs index 5d4407f3b8..483c63ade7 100644 --- a/Emby.Server.Implementations/Services/UrlExtensions.cs +++ b/Emby.Server.Implementations/Services/UrlExtensions.cs @@ -1,4 +1,5 @@ using System; +using MediaBrowser.Common.Extensions; namespace Emby.Server.Implementations.Services { @@ -13,25 +14,12 @@ namespace Emby.Server.Implementations.Services public static string GetMethodName(this Type type) { var typeName = type.FullName != null // can be null, e.g. generic types - ? LeftPart(type.FullName, "[[") // Generic Fullname - .Replace(type.Namespace + ".", string.Empty) // Trim Namespaces - .Replace("+", ".") // Convert nested into normal type + ? StringExtensions.LeftPart(type.FullName, "[[", StringComparison.Ordinal).ToString() // Generic Fullname + .Replace(type.Namespace + ".", string.Empty, StringComparison.Ordinal) // Trim Namespaces + .Replace("+", ".", StringComparison.Ordinal) // Convert nested into normal type : type.Name; return type.IsGenericParameter ? "'" + typeName : typeName; } - - private static string LeftPart(string strVal, string needle) - { - if (strVal == null) - { - return null; - } - - var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase); - return pos == -1 - ? strVal - : strVal.Substring(0, pos); - } } } diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 1781df8b51..9c638f4395 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Mime; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -216,14 +217,14 @@ namespace Emby.Server.Implementations.SocketSharp pi = pi.Slice(1); } - format = LeftPart(pi, '/'); + format = pi.LeftPart('/'); if (format.Length > FormatMaxLength) { return null; } } - format = LeftPart(format, '.'); + format = format.LeftPart('.'); if (format.Contains("json", StringComparison.OrdinalIgnoreCase)) { return "application/json"; @@ -235,16 +236,5 @@ namespace Emby.Server.Implementations.SocketSharp return null; } - - public static ReadOnlySpan<char> LeftPart(ReadOnlySpan<char> strVal, char needle) - { - if (strVal == null) - { - return null; - } - - var pos = strVal.IndexOf(needle); - return pos == -1 ? strVal : strVal.Slice(0, pos); - } } } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 7705393576..928ca16128 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -321,7 +321,7 @@ namespace MediaBrowser.Api.Playback var encodingOptions = ServerConfigurationManager.GetEncodingOptions(); // enable throttling when NOT using hardware acceleration - if (encodingOptions.HardwareAccelerationType == string.Empty) + if (string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) { return state.InputProtocol == MediaProtocol.File && state.RunTimeTicks.HasValue && @@ -330,6 +330,7 @@ namespace MediaBrowser.Api.Playback state.VideoType == VideoType.VideoFile && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase); } + return false; } diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 08964420e7..40020093b6 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Security.Cryptography; using System.Text; diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs index 2ecbc6539b..94bf7c7401 100644 --- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs +++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Collections.Generic; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs index 48e758ee4c..258bd6662c 100644 --- a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs +++ b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/ProcessExtensions.cs b/MediaBrowser.Common/Extensions/ProcessExtensions.cs index c747871222..2f52ba196a 100644 --- a/MediaBrowser.Common/Extensions/ProcessExtensions.cs +++ b/MediaBrowser.Common/Extensions/ProcessExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Diagnostics; using System.Threading; diff --git a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs index 95802a4626..7c7bdaa92f 100644 --- a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs +++ b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs @@ -1,3 +1,4 @@ +#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs index 22130c5a1e..ebac9d8e6b 100644 --- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs +++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; namespace MediaBrowser.Common.Extensions diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs index 0432f36b57..459bec1105 100644 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Collections.Generic; diff --git a/MediaBrowser.Common/Extensions/StringExtensions.cs b/MediaBrowser.Common/Extensions/StringExtensions.cs new file mode 100644 index 0000000000..2ac29f8e52 --- /dev/null +++ b/MediaBrowser.Common/Extensions/StringExtensions.cs @@ -0,0 +1,37 @@ +#nullable enable + +using System; + +namespace MediaBrowser.Common.Extensions +{ + /// <summary> + /// Extensions methods to simplify string operations. + /// </summary> + public static class StringExtensions + { + /// <summary> + /// Returns the part left of the <c>needle</c>. + /// </summary> + /// <param name="str">The string to seek.</param> + /// <param name="needle">The needle to find.</param> + /// <returns>The part left of the <c>needle</c>.</returns> + public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> str, char needle) + { + var pos = str.IndexOf(needle); + return pos == -1 ? str : str[..pos]; + } + + /// <summary> + /// Returns the part left of the <c>needle</c>. + /// </summary> + /// <param name="str">The string to seek.</param> + /// <param name="needle">The needle to find.</param> + /// <param name="stringComparison">One of the enumeration values that specifies the rules for the search.</param> + /// <returns>The part left of the <c>needle</c>.</returns> + public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> str, ReadOnlySpan<char> needle, StringComparison stringComparison = default) + { + var pos = str.IndexOf(needle, stringComparison); + return pos == -1 ? str : str[..pos]; + } + } +} diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index cd387bd546..922eb4ca79 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Model.Entities } /// <summary> - /// Gets a provider id + /// Gets a provider id. /// </summary> /// <param name="instance">The instance.</param> /// <param name="provider">The provider.</param> @@ -31,7 +31,7 @@ namespace MediaBrowser.Model.Entities } /// <summary> - /// Gets a provider id + /// Gets a provider id. /// </summary> /// <param name="instance">The instance.</param> /// <param name="name">The name.</param> @@ -53,7 +53,7 @@ namespace MediaBrowser.Model.Entities } /// <summary> - /// Sets a provider id + /// Sets a provider id. /// </summary> /// <param name="instance">The instance.</param> /// <param name="name">The name.</param> @@ -89,7 +89,7 @@ namespace MediaBrowser.Model.Entities } /// <summary> - /// Sets a provider id + /// Sets a provider id. /// </summary> /// <param name="instance">The instance.</param> /// <param name="provider">The provider.</param> diff --git a/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs new file mode 100644 index 0000000000..89e7e8fde2 --- /dev/null +++ b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs @@ -0,0 +1,35 @@ +using System; +using MediaBrowser.Common.Extensions; +using Xunit; + +namespace Jellyfin.Common.Tests.Extensions +{ + public class StringExtensionsTests + { + [Theory] + [InlineData("Banana split", ' ', "Banana")] + [InlineData("Banana split", 'q', "Banana split")] + public void LeftPartCharTest(string str, char needle, string result) + { + Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); + } + + [Theory] + [InlineData("Banana split", " ", "Banana")] + [InlineData("Banana split test", " split", "Banana")] + public void LeftPartWithoutStringComparisonTest(string str, string needle, string result) + { + Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); + } + + [Theory] + [InlineData("Banana split", " ", StringComparison.Ordinal, "Banana")] + [InlineData("Banana split test", " split", StringComparison.Ordinal, "Banana")] + [InlineData("Banana split test", " Split", StringComparison.Ordinal, "Banana split test")] + [InlineData("Banana split test", " Splït", StringComparison.InvariantCultureIgnoreCase, "Banana split test")] + public void LeftPartTest(string str, string needle, StringComparison stringComparison, string result) + { + Assert.Equal(result, str.AsSpan().LeftPart(needle, stringComparison).ToString()); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs new file mode 100644 index 0000000000..42d128dc68 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs @@ -0,0 +1,16 @@ +using Emby.Server.Implementations.HttpServer; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.HttpServer +{ + public class HttpServerTests + { + [Theory] + [InlineData("This is a clean string.", "This is a clean string.")] + [InlineData("This isn't \n\ra clean string.", "This isn't a clean string.")] + public void RemoveControlCharactersTest(string input, string result) + { + Assert.Equal(result, ResponseFilter.RemoveControlCharacters(input)); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs new file mode 100644 index 0000000000..7053ed3297 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -0,0 +1,17 @@ +using Emby.Server.Implementations.Library; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Library +{ + public class PathExtensionsTests + { + [Theory] + [InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")] + [InlineData("Superman: Red Son", "imdbid", null)] + public void GetAttributeValueTest(string input, string attribute, string? result) + { + Assert.Equal(result, PathExtensions.GetAttributeValue(input, attribute)); + } + } +} From 958681cdffddc7ea24d92d126badb3372cfa5d41 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Mon, 20 Apr 2020 10:16:22 +0200 Subject: [PATCH 223/411] Cover more branches --- .../Extensions/StringExtensionsTests.cs | 6 +++--- .../HttpServer/ResponseFilterTests.cs | 6 ++++-- .../Library/PathExtensionsTests.cs | 12 +++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs index 89e7e8fde2..0ed7673fdb 100644 --- a/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs +++ b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs @@ -9,7 +9,7 @@ namespace Jellyfin.Common.Tests.Extensions [Theory] [InlineData("Banana split", ' ', "Banana")] [InlineData("Banana split", 'q', "Banana split")] - public void LeftPartCharTest(string str, char needle, string result) + public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string result) { Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); } @@ -17,7 +17,7 @@ namespace Jellyfin.Common.Tests.Extensions [Theory] [InlineData("Banana split", " ", "Banana")] [InlineData("Banana split test", " split", "Banana")] - public void LeftPartWithoutStringComparisonTest(string str, string needle, string result) + public void LeftPart_ValidArgsWithoutStringComparison_Correct(string str, string needle, string result) { Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); } @@ -27,7 +27,7 @@ namespace Jellyfin.Common.Tests.Extensions [InlineData("Banana split test", " split", StringComparison.Ordinal, "Banana")] [InlineData("Banana split test", " Split", StringComparison.Ordinal, "Banana split test")] [InlineData("Banana split test", " Splït", StringComparison.InvariantCultureIgnoreCase, "Banana split test")] - public void LeftPartTest(string str, string needle, StringComparison stringComparison, string result) + public void LeftPart_ValidArgs_Correct(string str, string needle, StringComparison stringComparison, string result) { Assert.Equal(result, str.AsSpan().LeftPart(needle, stringComparison).ToString()); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs index 42d128dc68..39bd94b598 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/HttpServer/ResponseFilterTests.cs @@ -3,12 +3,14 @@ using Xunit; namespace Jellyfin.Server.Implementations.Tests.HttpServer { - public class HttpServerTests + public class ResponseFilterTests { [Theory] + [InlineData(null, null)] + [InlineData("", "")] [InlineData("This is a clean string.", "This is a clean string.")] [InlineData("This isn't \n\ra clean string.", "This isn't a clean string.")] - public void RemoveControlCharactersTest(string input, string result) + public void RemoveControlCharacters_ValidArgs_Correct(string? input, string? result) { Assert.Equal(result, ResponseFilter.RemoveControlCharacters(input)); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index 7053ed3297..d2ed0d9257 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -1,3 +1,4 @@ +using System; using Emby.Server.Implementations.Library; using Xunit; @@ -9,9 +10,18 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")] [InlineData("Superman: Red Son", "imdbid", null)] - public void GetAttributeValueTest(string input, string attribute, string? result) + public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? result) { Assert.Equal(result, PathExtensions.GetAttributeValue(input, attribute)); } + + [Theory] + [InlineData("", "")] + [InlineData("Superman: Red Son [imdbid=tt10985510]", "")] + [InlineData("", "imdbid")] + public void GetAttributeValue_EmptyString_ThrowsArgumentException(string input, string attribute) + { + Assert.Throws<ArgumentException>(() => PathExtensions.GetAttributeValue(input, attribute)); + } } } From 80f5dd1e59036c0dd0c75d0311d02842921c4737 Mon Sep 17 00:00:00 2001 From: Unknown <balugyanszky.gabor@gmail.com> Date: Mon, 20 Apr 2020 13:52:50 +0100 Subject: [PATCH 224/411] Fixed missing colons some colons were missing from the default ProtocolInfo string --- Emby.Dlna/Profiles/DefaultProfile.cs | 2 +- Emby.Dlna/Profiles/Xml/Default.xml | 2 +- Emby.Dlna/Profiles/Xml/Denon AVR.xml | 2 +- Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml | 2 +- Emby.Dlna/Profiles/Xml/LG Smart TV.xml | 2 +- Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml | 2 +- Emby.Dlna/Profiles/Xml/Marantz.xml | 2 +- Emby.Dlna/Profiles/Xml/MediaMonkey.xml | 2 +- Emby.Dlna/Profiles/Xml/Panasonic Viera.xml | 2 +- Emby.Dlna/Profiles/Xml/Popcorn Hour.xml | 2 +- Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml | 2 +- Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml | 2 +- Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml | 2 +- Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml | 2 +- Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml | 2 +- Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml | 2 +- Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml | 2 +- Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml | 2 +- Emby.Dlna/Profiles/Xml/WDTV Live.xml | 2 +- Emby.Dlna/Profiles/Xml/Xbox One.xml | 2 +- Emby.Dlna/Profiles/Xml/foobar2000.xml | 2 +- 21 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index d10804b228..2347ebd0d3 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -12,7 +12,7 @@ namespace Emby.Dlna.Profiles { Name = "Generic Device"; - ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*"; + ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*"; Manufacturer = "Jellyfin"; ModelDescription = "UPnP/AV 1.0 Compliant Media Server"; diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml index daac4135a2..9460f9d5a1 100644 --- a/Emby.Dlna/Profiles/Xml/Default.xml +++ b/Emby.Dlna/Profiles/Xml/Default.xml @@ -21,7 +21,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml index c76cd9a898..571786906c 100644 --- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml +++ b/Emby.Dlna/Profiles/Xml/Denon AVR.xml @@ -26,7 +26,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml index f2ce68ab5d..eea0febfdc 100644 --- a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml +++ b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>10</TimelineOffsetSeconds> <RequiresPlainVideoItems>true</RequiresPlainVideoItems> <RequiresPlainFolders>true</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml index a0f0e0ee8a..20f5ba79bf 100644 --- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>10</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml index 55910c77f2..d01e3a145e 100644 --- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -25,7 +25,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Marantz.xml b/Emby.Dlna/Profiles/Xml/Marantz.xml index a6345ab3f3..0cc9c86e87 100644 --- a/Emby.Dlna/Profiles/Xml/Marantz.xml +++ b/Emby.Dlna/Profiles/Xml/Marantz.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml index 2c2c3a1de4..9d5ddc3d1a 100644 --- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml index 934f0550d3..8f766853bb 100644 --- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -28,7 +28,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>10</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml index eab220fae9..aa881d0147 100644 --- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml +++ b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml @@ -21,7 +21,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml index 3e6f56e5bb..7160a9c2eb 100644 --- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml index 74240b8435..c9b907e580 100644 --- a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml +++ b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>true</RequiresPlainVideoItems> <RequiresPlainFolders>true</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 49819ccfdb..e516ff512d 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml index aaad7b342c..88bd1c2f53 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml index 8e2e8dbaa4..3ca9893cdc 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml index 17a6135e1f..8804a75dfa 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml +++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml index df385135cd..bafa44b828 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 20f50f6b63..eb8e645b31 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml @@ -29,7 +29,7 @@ <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> <SonyAggregationFlags>10</SonyAggregationFlags> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml index 05380e33a6..ccb74ee646 100644 --- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml +++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml @@ -28,7 +28,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>5</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml index 5f5cf1af31..26a65bbd44 100644 --- a/Emby.Dlna/Profiles/Xml/Xbox One.xml +++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml @@ -28,7 +28,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>40</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml index f3eedb35c6..5ce75ace55 100644 --- a/Emby.Dlna/Profiles/Xml/foobar2000.xml +++ b/Emby.Dlna/Profiles/Xml/foobar2000.xml @@ -27,7 +27,7 @@ <MaxStaticBitrate>140000000</MaxStaticBitrate> <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate> <MaxStaticMusicBitrate xsi:nil="true" /> - <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*image/jpeg:*,http-get:*image/png:*,http-get:*image/gif:*,http-get:*image/tiff:*</ProtocolInfo> + <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo> <TimelineOffsetSeconds>0</TimelineOffsetSeconds> <RequiresPlainVideoItems>false</RequiresPlainVideoItems> <RequiresPlainFolders>false</RequiresPlainFolders> From 32ccab32bf49cd87a13aa3e6344a0736c51b0ff7 Mon Sep 17 00:00:00 2001 From: ejalal <ejalal@gmail.com> Date: Mon, 20 Apr 2020 11:05:48 +0000 Subject: [PATCH 225/411] Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- .../Localization/Core/fr.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index ec29d33748..150952d8ba 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -11,7 +11,7 @@ "Collections": "Collections", "DeviceOfflineWithName": "{0} s'est déconnecté", "DeviceOnlineWithName": "{0} est connecté", - "FailedLoginAttemptWithUserName": "Échec de connexion de {0}", + "FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}", "Favorites": "Favoris", "Folders": "Dossiers", "Genres": "Genres", @@ -69,7 +69,7 @@ "PluginUpdatedWithName": "{0} a été mis à jour", "ProviderValue": "Fournisseur : {0}", "ScheduledTaskFailedWithName": "{0} a échoué", - "ScheduledTaskStartedWithName": "{0} a commencé", + "ScheduledTaskStartedWithName": "{0} a démarré", "ServerNameNeedsToBeRestarted": "{0} doit être redémarré", "Shows": "Émissions", "Songs": "Chansons", @@ -95,21 +95,21 @@ "VersionNumber": "Version {0}", "TasksChannelsCategory": "Chaines en ligne", "TaskDownloadMissingSubtitlesDescription": "Cherche les sous-titres manquant sur internet en se basant sur la configuration des métadonnées.", - "TaskDownloadMissingSubtitles": "Télécharge les sous-titres manquant", + "TaskDownloadMissingSubtitles": "Télécharger les sous-titres manquant", "TaskRefreshChannelsDescription": "Rafraîchit les informations des chaines en ligne.", - "TaskRefreshChannels": "Rafraîchit les chaines", + "TaskRefreshChannels": "Rafraîchir les chaines", "TaskCleanTranscodeDescription": "Supprime les fichiers transcodés de plus d'un jour.", - "TaskCleanTranscode": "Nettoie les dossier des transcodages", - "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des plugins configurés pour être mis à jour automatiquement.", - "TaskUpdatePlugins": "Mettre à jour les plugins", - "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et directeurs dans votre bibliothèque.", - "TaskRefreshPeople": "Rafraîchit les acteurs", + "TaskCleanTranscode": "Nettoyer les dossier des transcodages", + "TaskUpdatePluginsDescription": "Télécharge et installe les mises à jours des extensions configurés pour être mises à jour automatiquement.", + "TaskUpdatePlugins": "Mettre à jour les extensions", + "TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.", + "TaskRefreshPeople": "Rafraîchir les acteurs", "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.", - "TaskCleanLogs": "Nettoie le répertoire des journaux", + "TaskCleanLogs": "Nettoyer le répertoire des journaux", "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.", - "TaskRefreshLibrary": "Scanne toute les Bibliothèques", + "TaskRefreshLibrary": "Scanner toute les Bibliothèques", "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.", - "TaskRefreshChapterImages": "Extrait les images de chapitre", + "TaskRefreshChapterImages": "Extraire les images de chapitre", "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.", "TaskCleanCache": "Vider le répertoire cache", "TasksApplicationCategory": "Application", From 3cf2fce983fafbe0efbf8fc21ac267186600837d Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 13:59:02 -0400 Subject: [PATCH 226/411] 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 bd81825d2d2abba551d97d5c75890358d961fd27 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 14:48:12 -0400 Subject: [PATCH 227/411] Respect AutoRunWebApp and NoAutoRunWebApp settings when HostWebClient is false --- .../EntryPoints/StartupWizard.cs | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 8e97719311..a0a653d753 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs @@ -31,31 +31,41 @@ namespace Emby.Server.Implementations.EntryPoints /// <inheritdoc /> public Task RunAsync() + { + Run(); + return Task.CompletedTask; + } + + private void Run() { if (!_appHost.CanLaunchWebBrowser) { - return Task.CompletedTask; + return; } - if (!_appConfig.HostWebClient()) + // Always launch the startup wizard if possible when it has not been completed + if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient()) { - BrowserLauncher.OpenSwaggerPage(_appHost); + BrowserLauncher.OpenWebApp(_appHost); + return; + } + + // Do nothing if the web app is configured to not run automatically + var options = ((ApplicationHost)_appHost).StartupOptions; + if (!_config.Configuration.AutoRunWebApp || options.NoAutoRunWebApp) + { + return; } - else if (!_config.Configuration.IsStartupWizardCompleted) + + // Launch the swagger page if the web client is not hosted, otherwise open the web client + if (_appConfig.HostWebClient()) { BrowserLauncher.OpenWebApp(_appHost); } - else if (_config.Configuration.AutoRunWebApp) + else { - var options = ((ApplicationHost)_appHost).StartupOptions; - - if (!options.NoAutoRunWebApp) - { - BrowserLauncher.OpenWebApp(_appHost); - } + BrowserLauncher.OpenSwaggerPage(_appHost); } - - return Task.CompletedTask; } /// <inheritdoc /> From 51b610b999035fac571d0faf24e5cd558ecaacb5 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 14:58:00 -0400 Subject: [PATCH 228/411] 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( + /// <summary> + /// Call static initialization methods for the application. + /// </summary> + 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"); + } + } + + /// <summary> + /// Configure the web host builder. + /// </summary> + /// <param name="builder">The builder to configure.</param> + /// <param name="appHost">The application host.</param> + /// <param name="serviceCollection">The application service collection.</param> + /// <param name="commandLineOpts">The command line options passed to the application.</param> + /// <param name="startupConfig">The application configuration.</param> + /// <param name="appPaths">The application paths.</param> + /// <returns>The configured web host builder.</returns> + 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. /// </summary> - private static async Task InitLoggingConfigFile(IApplicationPaths appPaths) + /// <param name="appPaths">The application paths.</param> + /// <returns>A task representing the creation of the configuration file, or a completed task if the file already exists.</returns> + 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) + /// <summary> + /// Create the application configuration. + /// </summary> + /// <param name="commandLineOpts">The command line options passed to the program.</param> + /// <param name="appPaths">The application paths.</param> + /// <returns>The application configuration.</returns> + 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 <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 15:39:55 -0400 Subject: [PATCH 229/411] 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 +{ + /// <summary> + /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. + /// </summary> + public class JellyfinApplicationFactory : WebApplicationFactory<Startup> + { + private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + private static readonly ConcurrentBag<ApplicationHost> _appHosts = new ConcurrentBag<ApplicationHost>(); + + /// <summary> + /// Initializes a new instance of <see cref="JellyfinApplicationFactory"/>. + /// </summary> + public JellyfinApplicationFactory() + { + // Perform static initialization that only needs to happen once per test-run + Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger(); + Program.PerformStaticInitialization(); + } + + /// <inheritdoc/> + protected override IWebHostBuilder CreateWebHostBuilder() + { + return new WebHostBuilder(); + } + + /// <inheritdoc/> + 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<ManagedFileSystem>(), appPaths), + new SkiaEncoder(loggerFactory.CreateLogger<SkiaEncoder>(), appPaths), + new NetworkManager(loggerFactory.CreateLogger<NetworkManager>())); + _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); + } + + /// <inheritdoc/> + 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<IApplicationHost>(); + appHost.ServiceProvider = testServer.Services; + appHost.InitializeServices(); + appHost.FindParts(); + appHost.RunStartupTasksAsync().Wait(); + + return testServer; + } + + /// <inheritdoc/> + 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 @@ +<Project Sdk="Microsoft.NET.Sdk.Web"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.3" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> + <PackageReference Include="xunit" Version="2.4.1" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\Jellyfin.Server\Jellyfin.Server.csproj" /> + <ProjectReference Include="..\..\MediaBrowser.Api\MediaBrowser.Api.csproj" /> + </ItemGroup> + +</Project> From e43e6af405dcc8cd5cff71ca39e671de7dba5ce6 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 15:47:36 -0400 Subject: [PATCH 230/411] 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<JellyfinApplicationFactory> + { + 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<BrandingOptions>(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 <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 15:54:54 -0400 Subject: [PATCH 231/411] 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 <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 16:01:33 -0400 Subject: [PATCH 232/411] 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 <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 16:23:57 -0400 Subject: [PATCH 233/411] 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 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> @@ -9,9 +9,10 @@ <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.3" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> + <PackageReference Include="coverlet.collector" Version="1.2.1" /> </ItemGroup> <ItemGroup> From bc4e72b29b7e43242028549df1d2471e65b045bc Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Mon, 20 Apr 2020 20:20:39 -0400 Subject: [PATCH 234/411] Create ApplicationHost logger correctly --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 28aecf55b1..33aec1a06b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -255,7 +255,7 @@ namespace Emby.Server.Implementations ConfigurationManager = new ServerConfigurationManager(ApplicationPaths, LoggerFactory, _xmlSerializer, _fileSystemManager); - Logger = LoggerFactory.CreateLogger("App"); + Logger = LoggerFactory.CreateLogger<ApplicationHost>(); _startupOptions = options; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7b606c9f40..0b86b2db7e 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -3091,7 +3091,7 @@ namespace Emby.Server.Implementations.Library { _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes .Except(removeList) - .ToArray(); + .ToArray(); _configurationManager.SaveConfiguration(); } From c430a7ed8faa40788c32b89852310981b7c1cf83 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Tue, 21 Apr 2020 10:18:26 +0200 Subject: [PATCH 235/411] Address comments --- .../Library/PathExtensions.cs | 2 +- .../Extensions/StringExtensions.cs | 22 +++++++++---------- .../Extensions/StringExtensionsTests.cs | 20 ++++++++++++----- .../Library/PathExtensionsTests.cs | 4 ++-- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index b74cad067b..06ff3e611b 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library /// <param name="str">The STR.</param> /// <param name="attribute">The attrib.</param> /// <returns>System.String.</returns> - /// <exception cref="ArgumentException"><c>str</c> or <c>attribute</c> is empty.</exception> + /// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception> public static string? GetAttributeValue(this string str, string attribute) { if (str.Length == 0) diff --git a/MediaBrowser.Common/Extensions/StringExtensions.cs b/MediaBrowser.Common/Extensions/StringExtensions.cs index 2ac29f8e52..7643017412 100644 --- a/MediaBrowser.Common/Extensions/StringExtensions.cs +++ b/MediaBrowser.Common/Extensions/StringExtensions.cs @@ -10,28 +10,28 @@ namespace MediaBrowser.Common.Extensions public static class StringExtensions { /// <summary> - /// Returns the part left of the <c>needle</c>. + /// Returns the part on the left of the <c>needle</c>. /// </summary> - /// <param name="str">The string to seek.</param> + /// <param name="haystack">The string to seek.</param> /// <param name="needle">The needle to find.</param> - /// <returns>The part left of the <c>needle</c>.</returns> - public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> str, char needle) + /// <returns>The part left of the <paramref name="needle" />.</returns> + public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, char needle) { - var pos = str.IndexOf(needle); - return pos == -1 ? str : str[..pos]; + var pos = haystack.IndexOf(needle); + return pos == -1 ? haystack : haystack[..pos]; } /// <summary> - /// Returns the part left of the <c>needle</c>. + /// Returns the part on the left of the <c>needle</c>. /// </summary> - /// <param name="str">The string to seek.</param> + /// <param name="haystack">The string to seek.</param> /// <param name="needle">The needle to find.</param> /// <param name="stringComparison">One of the enumeration values that specifies the rules for the search.</param> /// <returns>The part left of the <c>needle</c>.</returns> - public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> str, ReadOnlySpan<char> needle, StringComparison stringComparison = default) + public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, ReadOnlySpan<char> needle, StringComparison stringComparison = default) { - var pos = str.IndexOf(needle, stringComparison); - return pos == -1 ? str : str[..pos]; + var pos = haystack.IndexOf(needle, stringComparison); + return pos == -1 ? haystack : haystack[..pos]; } } } diff --git a/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs index 0ed7673fdb..8bf613f05f 100644 --- a/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs +++ b/tests/Jellyfin.Common.Tests/Extensions/StringExtensionsTests.cs @@ -7,29 +7,37 @@ namespace Jellyfin.Common.Tests.Extensions public class StringExtensionsTests { [Theory] + [InlineData("", 'q', "")] [InlineData("Banana split", ' ', "Banana")] [InlineData("Banana split", 'q', "Banana split")] - public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string result) + public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult) { - Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); + var result = str.AsSpan().LeftPart(needle).ToString(); + Assert.Equal(expectedResult, result); } [Theory] + [InlineData("", "", "")] + [InlineData("", "q", "")] + [InlineData("Banana split", "", "")] [InlineData("Banana split", " ", "Banana")] [InlineData("Banana split test", " split", "Banana")] - public void LeftPart_ValidArgsWithoutStringComparison_Correct(string str, string needle, string result) + public void LeftPart_ValidArgsWithoutStringComparison_Correct(string str, string needle, string expectedResult) { - Assert.Equal(result, str.AsSpan().LeftPart(needle).ToString()); + var result = str.AsSpan().LeftPart(needle).ToString(); + Assert.Equal(expectedResult, result); } [Theory] + [InlineData("", "", StringComparison.Ordinal, "")] [InlineData("Banana split", " ", StringComparison.Ordinal, "Banana")] [InlineData("Banana split test", " split", StringComparison.Ordinal, "Banana")] [InlineData("Banana split test", " Split", StringComparison.Ordinal, "Banana split test")] [InlineData("Banana split test", " Splït", StringComparison.InvariantCultureIgnoreCase, "Banana split test")] - public void LeftPart_ValidArgs_Correct(string str, string needle, StringComparison stringComparison, string result) + public void LeftPart_ValidArgs_Correct(string str, string needle, StringComparison stringComparison, string expectedResult) { - Assert.Equal(result, str.AsSpan().LeftPart(needle, stringComparison).ToString()); + var result = str.AsSpan().LeftPart(needle, stringComparison).ToString(); + Assert.Equal(expectedResult, result); } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index d2ed0d9257..c771f5f4ae 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -10,9 +10,9 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData("Superman: Red Son [imdbid=tt10985510]", "imdbid", "tt10985510")] [InlineData("Superman: Red Son - tt10985510", "imdbid", "tt10985510")] [InlineData("Superman: Red Son", "imdbid", null)] - public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? result) + public void GetAttributeValue_ValidArgs_Correct(string input, string attribute, string? expectedResult) { - Assert.Equal(result, PathExtensions.GetAttributeValue(input, attribute)); + Assert.Equal(expectedResult, PathExtensions.GetAttributeValue(input, attribute)); } [Theory] From e21d6160c1e77843620c70551256a386ae53072c Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Tue, 21 Apr 2020 10:21:20 +0200 Subject: [PATCH 236/411] Address comments --- tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs | 4 ++-- .../Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs index 7b37b49a96..51633e157c 100644 --- a/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs +++ b/tests/Jellyfin.Model.Tests/Extensions/StringHelperTests.cs @@ -11,9 +11,9 @@ namespace Jellyfin.Model.Tests.Extensions [InlineData("banana", "Banana")] [InlineData("Banana", "Banana")] [InlineData("ä", "Ä")] - public void StringHelper_ValidArgs_Success(string str, string result) + public void StringHelper_ValidArgs_Success(string input, string expectedResult) { - Assert.Equal(result, StringHelper.FirstToUpper(str)); + Assert.Equal(expectedResult, StringHelper.FirstToUpper(input)); } } } diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index a438318ca7..40d80607c8 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -17,7 +17,7 @@ namespace Jellyfin.Naming.Tests.Subtitles [InlineData("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true)] [InlineData("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true)] [InlineData("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true)] - public void SubtitleParser_ValidFileNames_Parses(string input, string language, bool isDefault, bool isForced) + public void SubtitleParser_ValidFileName_Parses(string input, string language, bool isDefault, bool isForced) { var parser = new SubtitleParser(_namingOptions); @@ -30,7 +30,7 @@ namespace Jellyfin.Naming.Tests.Subtitles [Theory] [InlineData("The Skin I Live In (2011).mp4")] - public void SubtitleParser_InvalidFileNames_ReturnsNull(string input) + public void SubtitleParser_InvalidFileName_ReturnsNull(string input) { var parser = new SubtitleParser(_namingOptions); @@ -38,7 +38,7 @@ namespace Jellyfin.Naming.Tests.Subtitles } [Fact] - public void SubtitleParser_EmptyFileNames_ThrowsArgumentException() + public void SubtitleParser_EmptyFileName_ThrowsArgumentException() { Assert.Throws<ArgumentException>(() => new SubtitleParser(_namingOptions).ParseFile(string.Empty)); } From 974a4e79f647356caf6e497852fe799ade403930 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Tue, 21 Apr 2020 10:33:41 +0200 Subject: [PATCH 237/411] 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 @@ <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> </PropertyGroup> </Project> 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 <bond.009@outlook.com> Date: Tue, 21 Apr 2020 12:11:55 +0200 Subject: [PATCH 238/411] 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 | 461 ++++++++---------- 10 files changed, 242 insertions(+), 311 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<char> 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() - { - var parser = GetParser(); - - var result = - parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).mkv"); - - Assert.Equal(2006, result.Year); - Assert.False(result.IsStub); - Assert.False(result.Is3D); - Assert.Equal("Brave", result.Name); - Assert.Null(result.ExtraType); - } - - // FIXME - // [Fact] - public void TestSimpleFile2() - { - var parser = GetParser(); - - 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); + private readonly NamingOptions _namingOptions = new NamingOptions(); + + private class ResolveFileTestData : IEnumerable<object?[]> + { + public IEnumerator<object?[]> 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, + } + }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [Theory] + [ClassData(typeof(ResolveFileTestData))] + public void ResolveFile_ValidFileName_Success(VideoFileInfo? expectedResult) + { + var result = new VideoResolver(_namingOptions).ResolveFile(expectedResult.Path); + + 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 <bond.009@outlook.com> Date: Tue, 21 Apr 2020 12:54:04 +0200 Subject: [PATCH 239/411] 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<object?[]> + private class ResolveFileTestData : IEnumerable<object[]> { - public IEnumerator<object?[]> GetEnumerator() + public IEnumerator<object[]> 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 166a4e8129d51f508e0e8a7566381c22fa6cf239 Mon Sep 17 00:00:00 2001 From: Mehdi Khosravi <m-khosravi@outlook.com> Date: Tue, 21 Apr 2020 09:21:56 +0000 Subject: [PATCH 240/411] Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fa/ --- Emby.Server.Implementations/Localization/Core/fa.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index be6f87ee3f..500c292170 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -23,7 +23,7 @@ "HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه", "HeaderFavoriteShows": "سریال‌های مورد علاقه", "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه", - "HeaderLiveTV": "تلویزیون زنده", + "HeaderLiveTV": "پخش زنده", "HeaderNextUp": "قسمت بعدی", "HeaderRecordingGroups": "گروه‌های ضبط", "HomeVideos": "ویدیوهای خانگی", From b88a94116bb7ac0857bffb38a3eb8bfe88a40d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <andre15andre@hotmail.com> Date: Tue, 21 Apr 2020 09:20:22 +0000 Subject: [PATCH 241/411] Translated using Weblate (Portuguese (Portugal)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- .../Localization/Core/pt-PT.json | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index ebf35c4920..c1fb65743d 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -26,7 +26,7 @@ "HeaderLiveTV": "TV em Direto", "HeaderNextUp": "A Seguir", "HeaderRecordingGroups": "Grupos de Gravação", - "HomeVideos": "Home videos", + "HomeVideos": "Videos caseiros", "Inherit": "Herdar", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca", @@ -92,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", "ValueSpecialEpisodeName": "Especial - {0}", - "VersionNumber": "Versão {0}" + "VersionNumber": "Versão {0}", + "TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.", + "TaskDownloadMissingSubtitles": "Fazer download de legendas em falta", + "TaskRefreshChannelsDescription": "Atualizar informação sobre canais da Internet.", + "TaskRefreshChannels": "Atualizar Canais", + "TaskCleanTranscodeDescription": "Apagar ficheiros de transcode com mais de um dia.", + "TaskCleanTranscode": "Limpar a Diretoria de Transcode", + "TaskUpdatePluginsDescription": "Faz o download e instala updates para os plugins que estão configurados para atualizar automaticamente.", + "TaskUpdatePlugins": "Atualizar Plugins", + "TaskRefreshPeopleDescription": "Atualizar metadados para atores e diretores na biblioteca.", + "TaskRefreshPeople": "Atualizar Pessoas", + "TaskCleanLogsDescription": "Apagar ficheiros de log que têm mais de {0} dias.", + "TaskCleanLogs": "Limpar a Diretoria de Logs", + "TaskRefreshLibraryDescription": "Scannear a biblioteca de música para novos ficheiros e atualizar os metadados.", + "TaskRefreshLibrary": "Scannear Biblioteca de Música", + "TaskRefreshChapterImagesDescription": "Criar thumbnails para os vídeos que têm capítulos.", + "TaskRefreshChapterImages": "Extrair Imagens dos Capítulos", + "TaskCleanCacheDescription": "Apagar ficheiros em cache que já não são necessários.", + "TaskCleanCache": "Limpar Cache", + "TasksChannelsCategory": "Canais da Internet", + "TasksApplicationCategory": "Aplicação", + "TasksLibraryCategory": "Biblioteca", + "TasksMaintenanceCategory": "Manutenção" } From a2f19eadf739297cbbc99c9082b0175e8b881054 Mon Sep 17 00:00:00 2001 From: "Chen-Tai,Peng" <qwer2003tw@gmail.com> Date: Tue, 21 Apr 2020 17:18:29 +0000 Subject: [PATCH 242/411] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- .../Localization/Core/zh-TW.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index c423c7ea73..766e338dab 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -91,5 +91,18 @@ "VersionNumber": "版本 {0}", "HeaderRecordingGroups": "錄製組", "Inherit": "繼承", - "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕" + "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕", + "TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。", + "TaskDownloadMissingSubtitles": "下載遺失的字幕", + "TaskRefreshChannels": "重新整理頻道", + "TaskUpdatePlugins": "更新插件", + "TaskRefreshPeople": "重新整理人員", + "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。", + "TaskCleanLogs": "清空紀錄資料夾", + "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。", + "TaskRefreshLibrary": "掃描媒體庫", + "TaskRefreshChapterImages": "擷取章節圖片", + "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。", + "TaskCleanCache": "清除快取資料夾", + "TasksLibraryCategory": "媒體庫" } From 66364eba922bd3e9c14fd5dde276928c1750f880 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 12 Apr 2020 19:36:42 -0400 Subject: [PATCH 243/411] Add tasks required for SonarCloud integration --- .ci/azure-pipelines-test.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index a5d29fb619..bdc1838fbc 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -28,12 +28,26 @@ jobs: submodules: true persistCredentials: false + # This is required for the SonarCloud analyzer + - task: UseDotNet@2 + displayName: "Install .NET Core SDK 2.1" + inputs: + packageType: sdk + version: '2.1.805' + - task: UseDotNet@2 displayName: "Update DotNet" inputs: packageType: sdk version: ${{ parameters.DotNetSdkVersion }} + - task: SonarCloudPrepare@1 + displayName: 'Prepare analysis on SonarCloud' + inputs: + SonarCloud: 'Sonarcloud for Jellyfin' + organization: 'jellyfin' + projectKey: 'jellyfin_jellyfin' + - task: DotNetCoreCLI@2 displayName: 'Run CLI Tests' inputs: @@ -44,6 +58,12 @@ jobs: testRunTitle: $(Agent.JobName) workingDirectory: "$(Build.SourcesDirectory)" + - task: SonarCloudAnalyze@1 + displayName: 'Run Code Analysis' + + - task: SonarCloudPublish@1 + displayName: 'Publish Quality Gate Result' + - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging displayName: 'Run ReportGenerator' @@ -62,3 +82,4 @@ jobs: summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml" pathToSources: $(Build.SourcesDirectory) failIfCoverageEmpty: true + From c5f163293fb29145245393b976f02aae53217944 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Tue, 21 Apr 2020 16:21:09 -0400 Subject: [PATCH 244/411] Add <ProjectGuid> properties to all project files This is required for SonarCloud analysis to run --- DvdLib/DvdLib.csproj | 5 +++++ Emby.Dlna/Emby.Dlna.csproj | 5 +++++ Emby.Drawing/Emby.Drawing.csproj | 5 +++++ Emby.Naming/Emby.Naming.csproj | 5 +++++ Emby.Notifications/Emby.Notifications.csproj | 5 +++++ Emby.Photos/Emby.Photos.csproj | 6 ++++++ .../Emby.Server.Implementations.csproj | 5 +++++ Jellyfin.Api/Jellyfin.Api.csproj | 5 +++++ Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 5 +++++ Jellyfin.Server/Jellyfin.Server.csproj | 5 +++++ MediaBrowser.Api/MediaBrowser.Api.csproj | 5 +++++ MediaBrowser.Common/MediaBrowser.Common.csproj | 5 +++++ MediaBrowser.Controller/MediaBrowser.Controller.csproj | 5 +++++ .../MediaBrowser.LocalMetadata.csproj | 5 +++++ .../MediaBrowser.MediaEncoding.csproj | 5 +++++ MediaBrowser.Model/MediaBrowser.Model.csproj | 5 +++++ MediaBrowser.Providers/MediaBrowser.Providers.csproj | 5 +++++ MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj | 5 +++++ MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj | 5 +++++ RSSDP/RSSDP.csproj | 5 +++++ tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 5 +++++ tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 5 +++++ .../Jellyfin.Controller.Tests.csproj | 5 +++++ .../Jellyfin.MediaEncoding.Tests.csproj | 5 +++++ tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 5 +++++ .../Jellyfin.Server.Implementations.Tests.csproj | 5 +++++ 26 files changed, 131 insertions(+) diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index f4df6a9f52..72a50124b8 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <Compile Include="..\SharedVersion.cs" /> </ItemGroup> diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 0cabe43d51..42a5f95c14 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <Compile Include="..\SharedVersion.cs" /> </ItemGroup> diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index b7090b2629..f48507b34c 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{08FFF49B-F175-4807-A2B5-73B0EBD9F716}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 4e08170a47..c017e76c74 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index e6bf785bff..1d430a5e58 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{2E030C33-6923-4530-9E54-FA29FA6AD1A9}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index cc3fbb43f9..dbe01257f4 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -1,4 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{89AB4548-770D-41FD-A891-8DAFF44F452C}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d46b9507ba..765aa1759c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{E383961B-9356-4D5D-8233-9A1079D03055}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" /> diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 8f23ef9d03..a582a209cb 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{DFBEFB4C-DA19-4143-98B7-27320C7F7163}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateDocumentationFile>true</GenerateDocumentationFile> diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index d0a99e1e28..6326278f59 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{154872D9-6C12-4007-96E3-8F70A58386CE}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 02ae202b47..270cdeaaf5 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{07E39F42-A2C6-4B32-AF8C-725F957A73FF}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <AssemblyName>jellyfin</AssemblyName> <OutputType>Exe</OutputType> diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 0d62cf8c59..d703bdb058 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 3b03478020..69864106c7 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <Authors>Jellyfin Contributors</Authors> <PackageId>Jellyfin.Common</PackageId> diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 662ab25356..4e7d027374 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <Authors>Jellyfin Contributors</Authors> <PackageId>Jellyfin.Controller</PackageId> diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 71eb62693c..24104d779d 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index a312dcd705..af8bee301c 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{960295EE-4AF4-4440-A525-B4C295B01A61}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 27486c68f1..b41d0af1d1 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <Authors>Jellyfin Contributors</Authors> <PackageId>Jellyfin.Model</PackageId> diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 330a4d1e53..1b3df63b63 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index da52b852a4..bcaee50f29 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index e262820958..45fd9add92 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{23499896-B135-4527-8574-C26E926EA99E}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index 9753ae9b1f..e3f3127b64 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</ProjectGuid> + </PropertyGroup> + <ItemGroup> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index b159db2bd8..fb76f34d0e 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 81a2242e7f..cd41c5604a 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{DF194677-DFD3-42AF-9F75-D44D5A416478}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 30994dee60..407fe2eda1 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 78a020ad58..276c50ca31 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{28464062-0939-4AA7-9F7B-24DDDA61A7C0}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index f404b3e464..ac0c970c13 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{3998657B-1CCC-49DD-A19F-275DC8495F57}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index b7865439c7..ba7ecb3d13 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -1,5 +1,10 @@ <Project Sdk="Microsoft.NET.Sdk"> + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}</ProjectGuid> + </PropertyGroup> + <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> From 9a401c3728c2ac45ea98f9de5ffc75c037a8c12f Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Tue, 21 Apr 2020 17:55:19 -0400 Subject: [PATCH 245/411] Only run SonarCloud analysis for ubuntu tests --- .ci/azure-pipelines-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index bdc1838fbc..cb5338ac8c 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -31,6 +31,7 @@ jobs: # This is required for the SonarCloud analyzer - task: UseDotNet@2 displayName: "Install .NET Core SDK 2.1" + condition: eq(variables['ImageName'], 'ubuntu-latest') inputs: packageType: sdk version: '2.1.805' @@ -43,6 +44,7 @@ jobs: - task: SonarCloudPrepare@1 displayName: 'Prepare analysis on SonarCloud' + condition: eq(variables['ImageName'], 'ubuntu-latest') inputs: SonarCloud: 'Sonarcloud for Jellyfin' organization: 'jellyfin' @@ -60,9 +62,11 @@ jobs: - task: SonarCloudAnalyze@1 displayName: 'Run Code Analysis' + condition: eq(variables['ImageName'], 'ubuntu-latest') - task: SonarCloudPublish@1 displayName: 'Publish Quality Gate Result' + condition: eq(variables['ImageName'], 'ubuntu-latest') - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging From e943facebb12791cf01e9c64000c79f32aefc1f1 Mon Sep 17 00:00:00 2001 From: Wouter Kayser <wouterkayser@gmail.com> Date: Wed, 22 Apr 2020 07:37:10 +0000 Subject: [PATCH 246/411] 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 3bc9c2a779..22dcf1d2e8 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -1,11 +1,11 @@ { "Albums": "Albums", "AppDeviceValues": "App: {0}, Apparaat: {1}", - "Application": "Applicatie", + "Application": "Programma", "Artists": "Artiesten", - "AuthenticationSucceededWithUserName": "{0} succesvol geauthenticeerd", + "AuthenticationSucceededWithUserName": "{0} is succesvol geverifiëerd", "Books": "Boeken", - "CameraImageUploadedFrom": "Er is een nieuwe foto toegevoegd van {0}", + "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", From de328a46cdba2182c478761659e3b6fd3a8dc0c0 Mon Sep 17 00:00:00 2001 From: Michael Ong <ongmk.1997@gmail.com> Date: Wed, 22 Apr 2020 07:17:06 +0000 Subject: [PATCH 247/411] Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- .../Localization/Core/zh-TW.json | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 766e338dab..a22f66df90 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -50,10 +50,10 @@ "NotificationOptionCameraImageUploaded": "相機相片已上傳", "NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionNewLibraryContent": "已新增新內容", - "NotificationOptionPluginError": "擴充元件錯誤", - "NotificationOptionPluginInstalled": "擴充元件已安裝", - "NotificationOptionPluginUninstalled": "擴充元件已移除", - "NotificationOptionPluginUpdateInstalled": "已更新擴充元件", + "NotificationOptionPluginError": "插件安裝錯誤", + "NotificationOptionPluginInstalled": "插件已安裝", + "NotificationOptionPluginUninstalled": "插件已移除", + "NotificationOptionPluginUpdateInstalled": "插件已更新", "NotificationOptionServerRestartRequired": "伺服器需要重新啟動", "NotificationOptionTaskFailed": "排程任務失敗", "NotificationOptionUserLockedOut": "使用者已鎖定", @@ -61,7 +61,7 @@ "NotificationOptionVideoPlaybackStopped": "影片停止播放", "Photos": "相片", "Playlists": "播放清單", - "Plugin": "外掛", + "Plugin": "插件", "PluginInstalledWithName": "{0} 已安裝", "PluginUninstalledWithName": "{0} 已移除", "PluginUpdatedWithName": "{0} 已更新", @@ -104,5 +104,14 @@ "TaskRefreshChapterImages": "擷取章節圖片", "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。", "TaskCleanCache": "清除快取資料夾", - "TasksLibraryCategory": "媒體庫" + "TasksLibraryCategory": "媒體庫", + "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。", + "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。", + "TaskCleanTranscode": "清除轉碼資料夾", + "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。", + "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。", + "TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。", + "TasksChannelsCategory": "網絡頻道", + "TasksApplicationCategory": "應用程式", + "TasksMaintenanceCategory": "維修" } From f5f990154456af149b60f7436fbdf6ac0c2281f4 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Wed, 22 Apr 2020 09:55:35 -0400 Subject: [PATCH 248/411] Fixed build --- Emby.Server.Implementations/Activity/ActivityRepository.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 83471935d3..22796ba3f8 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -85,8 +85,6 @@ namespace Emby.Server.Implementations.Activity } } - private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; - /// <inheritdoc /> public void Create(ActivityLogEntry entry) { From eee02a355af0760c55dc9c1d42b9f5f623135b08 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Wed, 22 Apr 2020 10:06:37 -0600 Subject: [PATCH 249/411] Adds produces annotation to the base controller to indicate application/json as the response type for endpoints --- Jellyfin.Api/BaseJellyfinApiController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 1f4508e6cb..51bd384b3b 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -7,6 +7,7 @@ namespace Jellyfin.Api /// </summary> [ApiController] [Route("[controller]")] + [Produces("application/json")] public class BaseJellyfinApiController : ControllerBase { } From 8f02fb9a4f062e2b6d980b6645a85e2681298dfa Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Wed, 22 Apr 2020 13:09:59 -0400 Subject: [PATCH 250/411] Remove unused usings This addresses the new issues identified in SonarCloud analysis --- Emby.Drawing/ImageProcessor.cs | 1 - MediaBrowser.Common/IApplicationHost.cs | 2 -- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 - 3 files changed, 4 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 903b958a4f..ba14b4dcab 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -8,7 +8,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index eabf8f3d75..e8d9282e40 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; -using MediaBrowser.Model.Updates; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 2cc89b0dce..992ad146d8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -19,7 +19,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System.Diagnostics; From 7d22a3c394fa418699265da0aa289829df4f1e6d Mon Sep 17 00:00:00 2001 From: Unlimitediq <thomas199717@gmail.com> Date: Wed, 22 Apr 2020 13:22:29 +0000 Subject: [PATCH 251/411] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 22dcf1d2e8..baa12e98ec 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Apparaat: {1}", "Application": "Programma", "Artists": "Artiesten", - "AuthenticationSucceededWithUserName": "{0} is succesvol geverifiëerd", + "AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd", "Books": "Boeken", "CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}", "Channels": "Kanalen", From 964e27793253717bf2fde79f253bcb4050339bba Mon Sep 17 00:00:00 2001 From: tyaprak <tyaprak@gmail.com> Date: Wed, 22 Apr 2020 22:59:20 +0000 Subject: [PATCH 252/411] Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- .../Localization/Core/tr.json | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 62d205516d..3cf3482eba 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Ses çalma başladı", "NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu", "NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi", - "NotificationOptionInstallationFailed": "Yükleme başarısız oldu", + "NotificationOptionInstallationFailed": "Kurulum hatası", "NotificationOptionNewLibraryContent": "Yeni içerik eklendi", "NotificationOptionPluginError": "Eklenti hatası", "NotificationOptionPluginInstalled": "Eklenti yüklendi", @@ -95,7 +95,24 @@ "VersionNumber": "Versiyon {0}", "TaskCleanCache": "Geçici dosya klasörünü temizle", "TasksChannelsCategory": "İnternet kanalları", - "TasksApplicationCategory": "Yazılım", + "TasksApplicationCategory": "Uygulama", "TasksLibraryCategory": "Kütüphane", - "TasksMaintenanceCategory": "Onarım" + "TasksMaintenanceCategory": "Onarım", + "TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.", + "TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.", + "TaskDownloadMissingSubtitles": "Eksik altyazıları indir", + "TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.", + "TaskRefreshChannels": "Kanalları Yenile", + "TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.", + "TaskCleanTranscode": "Dönüşüm Dizinini Temizle", + "TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.", + "TaskUpdatePlugins": "Eklentileri Güncelle", + "TaskRefreshPeople": "Kullanıcıları Yenile", + "TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.", + "TaskCleanLogs": "Log Dizinini Temizle", + "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.", + "TaskRefreshLibrary": "Medya Kütüphanesini Tara", + "TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.", + "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar", + "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler." } From 97e383d108a4adb7e57c13d67f1d36bd1b5ce7b5 Mon Sep 17 00:00:00 2001 From: Miko Dela Cruz <mikomiks22@gmail.com> Date: Wed, 22 Apr 2020 17:57:29 +0000 Subject: [PATCH 253/411] Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 5e017d4c4c..a4d9f9ef6b 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -104,13 +104,14 @@ "TasksMaintenanceCategory": "メンテナンス", "TaskRefreshChannelsDescription": "ネットチャンネルの情報をリフレッシュします。", "TaskRefreshChannels": "チャンネルのリフレッシュ", - "TaskCleanTranscodeDescription": "一日以上前のトランスコードを消去します。", - "TaskCleanTranscode": "トランスコード用のディレクトリの掃除", + "TaskCleanTranscodeDescription": "1日以上経過したトランスコードファイルを削除します。", + "TaskCleanTranscode": "トランスコードディレクトリの削除", "TaskUpdatePluginsDescription": "自動更新可能なプラグインのアップデートをダウンロードしてインストールします。", "TaskUpdatePlugins": "プラグインの更新", - "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータをリフレッシュします。", - "TaskRefreshPeople": "俳優や監督のデータのリフレッシュ", + "TaskRefreshPeopleDescription": "メディアライブラリで俳優や監督のメタデータを更新します。", + "TaskRefreshPeople": "俳優や監督のデータの更新", "TaskDownloadMissingSubtitlesDescription": "メタデータ構成に基づいて、欠落している字幕をインターネットで検索します。", "TaskRefreshChapterImagesDescription": "チャプターのあるビデオのサムネイルを作成します。", - "TaskRefreshChapterImages": "チャプター画像を抽出する" + "TaskRefreshChapterImages": "チャプター画像を抽出する", + "TaskDownloadMissingSubtitles": "不足している字幕をダウンロードする" } From d1684b1053c9cfedc81e44ff698c5267ee88a0ef Mon Sep 17 00:00:00 2001 From: Admin <jimcartlidge> Date: Thu, 23 Apr 2020 18:30:48 +0100 Subject: [PATCH 254/411] 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 <cody@robibe.ro> Date: Thu, 23 Apr 2020 12:43:05 -0600 Subject: [PATCH 255/411] 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 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> From 2066b0f68f96a14d7b5a5e511ce9099c7a1cada7 Mon Sep 17 00:00:00 2001 From: ZadenRB <zaden.ruggieroboune@gmail.com> Date: Thu, 23 Apr 2020 16:15:59 -0600 Subject: [PATCH 256/411] Use builtin JSON Mime type constant --- Jellyfin.Api/BaseJellyfinApiController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 51bd384b3b..a34f9eb62f 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,3 +1,4 @@ +using System.Net.Mime; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api @@ -7,7 +8,7 @@ namespace Jellyfin.Api /// </summary> [ApiController] [Route("[controller]")] - [Produces("application/json")] + [Produces(MediaTypeNames.Application.Json)] public class BaseJellyfinApiController : ControllerBase { } From 01f49137fcb2c545f73d2d1295a7a7e0d8dc2000 Mon Sep 17 00:00:00 2001 From: Aragon <michael2210x@gmail.com> Date: Fri, 24 Apr 2020 20:50:06 +0000 Subject: [PATCH 257/411] 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 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 1ce8b08a0a..2662913621 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -1,7 +1,7 @@ { "Albums": "אלבומים", "AppDeviceValues": "יישום: {0}, מכשיר: {1}", - "Application": "אפליקציה", + "Application": "יישום", "Artists": "אומנים", "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה", "Books": "ספרים", @@ -92,5 +92,12 @@ "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueSpecialEpisodeName": "מיוחד- {0}", - "VersionNumber": "Version {0}" + "VersionNumber": "Version {0}", + "TaskRefreshLibrary": "סרוק ספריית מדיה", + "TaskRefreshChapterImages": "חלץ תמונות פרקים", + "TaskCleanCacheDescription": "מחק קבצי מטמון שלא בשימוש המערכת.", + "TaskCleanCache": "נקה תיקיית מטמון", + "TasksApplicationCategory": "יישום", + "TasksLibraryCategory": "ספרייה", + "TasksMaintenanceCategory": "תחזוקה" } From 153ea9f027d038ae86f644348eac7b43778d751d Mon Sep 17 00:00:00 2001 From: Andreas B <6439218+YouKnowBlom@users.noreply.github.com> Date: Sat, 25 Apr 2020 15:22:09 +0200 Subject: [PATCH 258/411] Fix error in HLS codecs field when level is null --- .../Playback/Hls/DynamicHlsService.cs | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index ce25676ff5..9071ef18fd 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -722,11 +722,37 @@ namespace MediaBrowser.Api.Playback.Hls //return state.VideoRequest.VideoBitRate.HasValue; } + /// <summary> + /// Get the H.26X level of the output video stream. + /// </summary> + /// <param name="state">StreamState of the current stream.</param> + /// <returns>H.26X level of the output video stream.</returns> + private int? GetOutputVideoCodecLevel(StreamState state) + { + string levelString; + if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) + && state.VideoStream.Level.HasValue) + { + levelString = state.VideoStream?.Level.ToString(); + } + else + { + levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec); + } + + if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel)) + { + return parsedLevel; + } + + return null; + } + /// <summary> /// Gets a formatted string of the output audio codec, for use in the CODECS field. /// </summary> /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/> - /// <seealso cref="GetPlaylistVideoCodecs(StreamState)"/> + /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/> /// <param name="state">StreamState of the current stream.</param> /// <returns>Formatted audio codec string.</returns> private string GetPlaylistAudioCodecs(StreamState state) @@ -761,18 +787,24 @@ namespace MediaBrowser.Api.Playback.Hls /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/> /// <param name="state">StreamState of the current stream.</param> /// <returns>Formatted video codec string.</returns> - private string GetPlaylistVideoCodecs(StreamState state) + private string GetPlaylistVideoCodecs(StreamState state, string codec, int level) { - int level = Convert.ToInt32(state.GetRequestedLevel(state.ActualOutputVideoCodec)); + if (level == 0) + { + // This is 0 when there's no requested H.26X level in the device profile + // and the source is not encoded in H.26X + Logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist"); + return string.Empty; + } - if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase)) { string profile = state.GetRequestedProfiles("h264").FirstOrDefault(); return HlsCodecStringFactory.GetH264String(profile, level); } - else if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase) - || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { string profile = state.GetRequestedProfiles("h265").FirstOrDefault(); @@ -787,7 +819,7 @@ namespace MediaBrowser.Api.Playback.Hls /// the active streams output video and audio codecs. /// </summary> /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/> - /// <seealso cref="GetPlaylistVideoCodecs(StreamState)"/> + /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/> /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/> /// <param name="builder">StringBuilder to append the field to.</param> /// <param name="state">StreamState of the current stream.</param> @@ -795,9 +827,10 @@ namespace MediaBrowser.Api.Playback.Hls { // Video string videoCodecs = string.Empty; - if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec)) + int? videoCodecLevel = GetOutputVideoCodecLevel(state); + if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue) { - videoCodecs = GetPlaylistVideoCodecs(state); + videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value); } // Audio @@ -807,26 +840,17 @@ namespace MediaBrowser.Api.Playback.Hls audioCodecs = GetPlaylistAudioCodecs(state); } - if (!string.IsNullOrEmpty(videoCodecs) || !string.IsNullOrEmpty(audioCodecs)) - { - builder.Append(",CODECS=\""); + StringBuilder codecs = new StringBuilder(); - if (!string.IsNullOrEmpty(videoCodecs) && !string.IsNullOrEmpty(audioCodecs)) - { - builder.Append(videoCodecs) - .Append(',') - .Append(audioCodecs); - } - else if (!string.IsNullOrEmpty(videoCodecs)) - { - builder.Append(videoCodecs); - } - else if (!string.IsNullOrEmpty(audioCodecs)) - { - builder.Append(audioCodecs); - } + codecs.Append(videoCodecs) + .Append(',') + .Append(audioCodecs); - builder.Append('"'); + if (codecs.Length > 1) + { + builder.Append(",CODECS=\"") + .Append(codecs) + .Append('"'); } } From a273ed9a574a342c1a40d42d820268ddf2fc2d99 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sat, 25 Apr 2020 15:29:59 +0200 Subject: [PATCH 259/411] 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<char> newName)) + && TryCleanString(name, out ReadOnlySpan<char> 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<object[]> + public static IEnumerable<object[]> GetResolveFileTestData() { - public IEnumerator<object[]> 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 <bond.009@outlook.com> Date: Sat, 25 Apr 2020 15:49:53 +0200 Subject: [PATCH 260/411] 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 <bond.009@outlook.com> Date: Sat, 25 Apr 2020 18:55:54 +0200 Subject: [PATCH 261/411] 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 <marknr.monteiro@protonmail.com> Date: Sat, 25 Apr 2020 18:36:09 -0400 Subject: [PATCH 262/411] 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<IApplicationHost>(); appHost.ServiceProvider = testServer.Services; - appHost.InitializeServices().Wait(); - appHost.RunStartupTasksAsync().Wait(); + appHost.InitializeServices().GetAwaiter().GetResult(); + appHost.RunStartupTasksAsync().GetAwaiter().GetResult(); return testServer; } From 233337256fc997c05bd6f0093a532cea0d54f227 Mon Sep 17 00:00:00 2001 From: sparky8251 <sparky@possumlodge.me> Date: Sat, 25 Apr 2020 21:35:51 -0400 Subject: [PATCH 263/411] Add prometheus exporters --- Jellyfin.Server/Jellyfin.Server.csproj | 3 +++ Jellyfin.Server/Program.cs | 4 ++++ Jellyfin.Server/Startup.cs | 3 +++ 3 files changed, 10 insertions(+) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 270cdeaaf5..c49fc41f46 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -43,6 +43,9 @@ <PackageReference Include="CommandLineParser" Version="2.7.82" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> + <PackageReference Include="prometheus-net" Version="3.5.0" /> + <PackageReference Include="prometheus-net.AspNetCore" Version="3.5.0" /> + <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" /> diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 193d30e3a7..be070f9d52 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -28,6 +28,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Prometheus.DotNetRuntime; using Serilog; using Serilog.Extensions.Logging; using SQLitePCL; @@ -161,6 +162,9 @@ namespace Jellyfin.Server ApplicationHost.LogEnvironmentInfo(_logger, appPaths); + // Initialize runtime stat collection + IDisposable collector = DotNetRuntimeStatsBuilder.Default().StartCollecting(); + // 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); diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 4d7d56e9d4..2e5f843e3a 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Prometheus; namespace Jellyfin.Server { @@ -69,9 +70,11 @@ namespace Jellyfin.Server app.UseJellyfinApiSwagger(); app.UseRouting(); app.UseAuthorization(); + app.UseHttpMetrics(); // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapMetrics(); }); app.Use(serverApplicationHost.ExecuteHttpHandlerAsync); From c689bf457c5f07774f74d2cdecabc1567ac16e77 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sat, 25 Apr 2020 22:12:19 -0400 Subject: [PATCH 264/411] Correct dpkg conditional logic Co-Authored-By: Vasily <JustAMan@users.noreply.github.com> --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index b4792a25ed..1db02af983 100755 --- a/build.sh +++ b/build.sh @@ -34,7 +34,7 @@ list_platforms() { } do_build_native() { - if [[ -f $( which dpkg ) && $( dpkg --print-architecture | head -1 ) != "${PLATFORM##*.}" ]]; then + if [[ ! -f $( which dpkg ) || $( dpkg --print-architecture | head -1 ) != "${PLATFORM##*.}" ]]; then echo "Cross-building is not supported for native builds, use 'docker' builds on amd64 for cross-building." exit 1 fi From a327e4ccac9937cd982f36ba582774c20e824814 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sat, 25 Apr 2020 22:13:21 -0400 Subject: [PATCH 265/411] Update fedora/jellyfin.spec Co-Authored-By: Vasily <JustAMan@users.noreply.github.com> --- fedora/jellyfin.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index 4e1045d740..9311864a63 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -28,7 +28,7 @@ BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, # COPR @dotnet-sig/dotnet or # https://packages.microsoft.com/rhel/7/prod/ BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 -Requires: %{name}-server = %{version}-%{release}, %{name}-web >= 1, %{name}-web < 2 +Requires: %{name}-server = %{version}-%{release}, %{name}-web >= 10.6, %{name}-web < 10.7 # Disable Automatic Dependency Processing AutoReqProv: no From 68c7a914c3acbd21a9ca879829bf6a670d4cf185 Mon Sep 17 00:00:00 2001 From: sparky8251 <sparky@possumlodge.me> Date: Sun, 26 Apr 2020 11:28:17 -0400 Subject: [PATCH 266/411] Added option to disable metrics collection and defaulted it to off --- .../ApplicationHost.cs | 7 ++++ .../Emby.Server.Implementations.csproj | 1 + Jellyfin.Server/Jellyfin.Server.csproj | 1 - .../Routines/DisableMetricsCollection.cs | 33 +++++++++++++++++++ Jellyfin.Server/Program.cs | 4 --- Jellyfin.Server/Startup.cs | 11 +++++-- .../Configuration/ServerConfiguration.cs | 6 ++++ 7 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 33aec1a06b..7e7b785d85 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -106,6 +106,7 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; +using Prometheus.DotNetRuntime; namespace Emby.Server.Implementations { @@ -259,6 +260,12 @@ namespace Emby.Server.Implementations _startupOptions = options; + // Initialize runtime stat collection + if (ServerConfigurationManager.Configuration.EnableMetrics) + { + IDisposable collector = DotNetRuntimeStatsBuilder.Default().StartCollecting(); + } + fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); _networkManager.NetworkChanged += OnNetworkChanged; diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index bf4a0d939f..44fc932e39 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -39,6 +39,7 @@ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" /> <PackageReference Include="Mono.Nat" Version="2.0.1" /> + <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> <PackageReference Include="sharpcompress" Version="0.25.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index c49fc41f46..88114d9994 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -45,7 +45,6 @@ <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> <PackageReference Include="prometheus-net" Version="3.5.0" /> <PackageReference Include="prometheus-net.AspNetCore" Version="3.5.0" /> - <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" /> diff --git a/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs b/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs new file mode 100644 index 0000000000..b5dc43614e --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs @@ -0,0 +1,33 @@ +using System; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// <summary> + /// Disable metrics collections for all installations since it can be a security risk if not properly secured. + /// </summary> + internal class DisableMetricsCollection : IMigrationRoutine + { + /// <inheritdoc/> + public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); + + /// <inheritdoc/> + public string Name => "DisableMetricsCollection"; + + /// <inheritdoc/> + public void Perform(CoreAppHost host, ILogger logger) + { + // Set EnableMetrics to false since it can leak sensitive information if not properly secured + var metrics = host.ServerConfigurationManager.Configuration.EnableMetrics; + if (metrics) + { + logger.LogInformation("Disabling metrics collection during migration"); + metrics = false; + + host.ServerConfigurationManager.SaveConfiguration("false", metrics); + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index be070f9d52..193d30e3a7 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -28,7 +28,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -using Prometheus.DotNetRuntime; using Serilog; using Serilog.Extensions.Logging; using SQLitePCL; @@ -162,9 +161,6 @@ namespace Jellyfin.Server ApplicationHost.LogEnvironmentInfo(_logger, appPaths); - // Initialize runtime stat collection - IDisposable collector = DotNetRuntimeStatsBuilder.Default().StartCollecting(); - // 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); diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 2e5f843e3a..8f85161c7d 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -70,11 +70,18 @@ namespace Jellyfin.Server app.UseJellyfinApiSwagger(); app.UseRouting(); app.UseAuthorization(); - app.UseHttpMetrics(); // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + if (_serverConfigurationManager.Configuration.EnableMetrics) + { + app.UseHttpMetrics(); // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + } + app.UseEndpoints(endpoints => { endpoints.MapControllers(); - endpoints.MapMetrics(); + if (_serverConfigurationManager.Configuration.EnableMetrics) + { + endpoints.MapMetrics(); + } }); app.Use(serverApplicationHost.ExecuteHttpHandlerAsync); diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index b5e8d5589a..063ccd9b9a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -19,6 +19,11 @@ namespace MediaBrowser.Model.Configuration /// </summary> public bool EnableUPnP { get; set; } + /// <summary> + /// Gets or sets a value indicating whether to enable prometheus metrics exporting. + /// </summary> + public bool EnableMetrics { get; set; } + /// <summary> /// Gets or sets the public mapped port. /// </summary> @@ -246,6 +251,7 @@ namespace MediaBrowser.Model.Configuration PublicHttpsPort = DefaultHttpsPort; HttpServerPortNumber = DefaultHttpPort; HttpsPortNumber = DefaultHttpsPort; + EnableMetrics = false; EnableHttps = false; EnableDashboardResponseCaching = true; EnableCaseSensitiveItemIds = true; From 997b71bbefa91f7a7fd9b499cb3d2ca656de466d Mon Sep 17 00:00:00 2001 From: sparky8251 <sparky@possumlodge.me> Date: Sun, 26 Apr 2020 11:52:01 -0400 Subject: [PATCH 267/411] Metrics endpoint now respects baseurl --- Jellyfin.Server/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 8f85161c7d..2cc7cff87e 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -80,7 +80,7 @@ namespace Jellyfin.Server endpoints.MapControllers(); if (_serverConfigurationManager.Configuration.EnableMetrics) { - endpoints.MapMetrics(); + endpoints.MapMetrics(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/') + "/metrics"); } }); From 57b5ec1d514ad8c5a0e8aced4b84b46b4c8923a7 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 26 Apr 2020 12:07:54 -0400 Subject: [PATCH 268/411] 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 /// </summary> public bool RequireHttps { get; set; } - /// <summary> - /// Gets or sets a value indicating whether the server is behind a reverse proxy. - /// </summary> - 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 /// <value>The transcode path.</value> public string TranscodingTempPath { get; set; } - /// <summary> - /// Gets or sets the HTTP server port number. - /// </summary> - /// <value>The HTTP server port number.</value> - public int HttpServerPortNumber { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether a client can connect to the server over HTTPS, either directly or - /// via a reverse proxy. - /// </summary> - public bool SupportsHttps { get; set; } - - /// <summary> - /// Gets or sets the HTTPS server port number. - /// </summary> - /// <value>The HTTPS server port number.</value> - public int HttpsPortNumber { get; set; } - /// <summary> /// Gets or sets a value indicating whether this instance has update available. /// </summary> From b8d1419d9a09e86914fe0ab9e61ffadc7b7eb514 Mon Sep 17 00:00:00 2001 From: Erwin de Haan <EraYaN@users.noreply.github.com> Date: Sat, 1 Jun 2019 22:40:01 +0200 Subject: [PATCH 269/411] Add basic new data model. Added maxlength to SourceId text field in Metadata entity. Added extra fields to Person entity and adjusted SourceId length to 255. Added Extra Nuget deps for Relational databases and added Default Sqlite connection string. Made LibraryItem and Metadata abstract. Added artwork, changed DbSet names, added Seasons, added Genres, removed Language enum Add MediaFIleKind, add CustomVideos, add Books. Add AdditionalStream Updated GUIDs. Remove merge artifacts. Updated Language to use ISO-639-3 3 letter language codes. Added collections and concurrency tokens. Added chapters. Added Photos and renamed CustomVideo to CustomItem. Started adding fields. Added extra fields and Company entities. Implement a first pass of user permissions for the new database schema Upgrade to v2 of the addon. Commit generated files. Update comment, rename namespace and remove superflous field. Un-ignore any generated code. Clean up the model files and other left overs. --- Jellyfin.Data/DbContexts/Jellyfin.cs | 1140 ++++++++++++++++++ Jellyfin.Data/Entities/Artwork.cs | 208 ++++ Jellyfin.Data/Entities/Book.cs | 84 ++ Jellyfin.Data/Entities/BookMetadata.cs | 123 ++ Jellyfin.Data/Entities/Chapter.cs | 274 +++++ Jellyfin.Data/Entities/Collection.cs | 131 ++ Jellyfin.Data/Entities/CollectionItem.cs | 151 +++ Jellyfin.Data/Entities/Company.cs | 147 +++ Jellyfin.Data/Entities/CompanyMetadata.cs | 234 ++++ Jellyfin.Data/Entities/CustomItem.cs | 84 ++ Jellyfin.Data/Entities/CustomItemMetadata.cs | 86 ++ Jellyfin.Data/Entities/Episode.cs | 127 ++ Jellyfin.Data/Entities/EpisodeMetadata.cs | 197 +++ Jellyfin.Data/Entities/Genre.cs | 163 +++ Jellyfin.Data/Entities/Group.cs | 115 ++ Jellyfin.Data/Entities/Library.cs | 158 +++ Jellyfin.Data/Entities/LibraryItem.cs | 180 +++ Jellyfin.Data/Entities/LibraryRoot.cs | 202 ++++ Jellyfin.Data/Entities/MediaFile.cs | 209 ++++ Jellyfin.Data/Entities/MediaFileStream.cs | 160 +++ Jellyfin.Data/Entities/Metadata.cs | 385 ++++++ Jellyfin.Data/Entities/MetadataProvider.cs | 158 +++ Jellyfin.Data/Entities/MetadataProviderId.cs | 189 +++ Jellyfin.Data/Entities/Movie.cs | 84 ++ Jellyfin.Data/Entities/MovieMetadata.cs | 239 ++++ Jellyfin.Data/Entities/MusicAlbum.cs | 84 ++ Jellyfin.Data/Entities/MusicAlbumMetadata.cs | 202 ++++ Jellyfin.Data/Entities/Permission.cs | 152 +++ Jellyfin.Data/Entities/PermissionKind.cs | 40 + Jellyfin.Data/Entities/Person.cs | 312 +++++ Jellyfin.Data/Entities/PersonRole.cs | 215 ++++ Jellyfin.Data/Entities/Photo.cs | 84 ++ Jellyfin.Data/Entities/PhotoMetadata.cs | 86 ++ Jellyfin.Data/Entities/Preference.cs | 117 ++ Jellyfin.Data/Entities/PreferenceKind.cs | 27 + Jellyfin.Data/Entities/ProviderMapping.cs | 133 ++ Jellyfin.Data/Entities/Rating.cs | 197 +++ Jellyfin.Data/Entities/RatingSource.cs | 242 ++++ Jellyfin.Data/Entities/Release.cs | 197 +++ Jellyfin.Data/Entities/Season.cs | 127 ++ Jellyfin.Data/Entities/SeasonMetadata.cs | 123 ++ Jellyfin.Data/Entities/Series.cs | 183 +++ Jellyfin.Data/Entities/SeriesMetadata.cs | 239 ++++ Jellyfin.Data/Entities/Track.cs | 127 ++ Jellyfin.Data/Entities/TrackMetadata.cs | 86 ++ Jellyfin.Data/Entities/User.cs | 242 ++++ Jellyfin.Data/Enums/ArtKind.cs | 25 + Jellyfin.Data/Enums/MediaFileKind.cs | 25 + Jellyfin.Data/Enums/PersonRoleType.cs | 32 + Jellyfin.Data/Enums/Weekday.cs | 27 + Jellyfin.Data/Jellyfin.Data.csproj | 12 + Jellyfin.Data/Structs/.gitkeep | 0 MediaBrowser.sln | 19 +- 53 files changed, 8571 insertions(+), 12 deletions(-) create mode 100644 Jellyfin.Data/DbContexts/Jellyfin.cs create mode 100644 Jellyfin.Data/Entities/Artwork.cs create mode 100644 Jellyfin.Data/Entities/Book.cs create mode 100644 Jellyfin.Data/Entities/BookMetadata.cs create mode 100644 Jellyfin.Data/Entities/Chapter.cs create mode 100644 Jellyfin.Data/Entities/Collection.cs create mode 100644 Jellyfin.Data/Entities/CollectionItem.cs create mode 100644 Jellyfin.Data/Entities/Company.cs create mode 100644 Jellyfin.Data/Entities/CompanyMetadata.cs create mode 100644 Jellyfin.Data/Entities/CustomItem.cs create mode 100644 Jellyfin.Data/Entities/CustomItemMetadata.cs create mode 100644 Jellyfin.Data/Entities/Episode.cs create mode 100644 Jellyfin.Data/Entities/EpisodeMetadata.cs create mode 100644 Jellyfin.Data/Entities/Genre.cs create mode 100644 Jellyfin.Data/Entities/Group.cs create mode 100644 Jellyfin.Data/Entities/Library.cs create mode 100644 Jellyfin.Data/Entities/LibraryItem.cs create mode 100644 Jellyfin.Data/Entities/LibraryRoot.cs create mode 100644 Jellyfin.Data/Entities/MediaFile.cs create mode 100644 Jellyfin.Data/Entities/MediaFileStream.cs create mode 100644 Jellyfin.Data/Entities/Metadata.cs create mode 100644 Jellyfin.Data/Entities/MetadataProvider.cs create mode 100644 Jellyfin.Data/Entities/MetadataProviderId.cs create mode 100644 Jellyfin.Data/Entities/Movie.cs create mode 100644 Jellyfin.Data/Entities/MovieMetadata.cs create mode 100644 Jellyfin.Data/Entities/MusicAlbum.cs create mode 100644 Jellyfin.Data/Entities/MusicAlbumMetadata.cs create mode 100644 Jellyfin.Data/Entities/Permission.cs create mode 100644 Jellyfin.Data/Entities/PermissionKind.cs create mode 100644 Jellyfin.Data/Entities/Person.cs create mode 100644 Jellyfin.Data/Entities/PersonRole.cs create mode 100644 Jellyfin.Data/Entities/Photo.cs create mode 100644 Jellyfin.Data/Entities/PhotoMetadata.cs create mode 100644 Jellyfin.Data/Entities/Preference.cs create mode 100644 Jellyfin.Data/Entities/PreferenceKind.cs create mode 100644 Jellyfin.Data/Entities/ProviderMapping.cs create mode 100644 Jellyfin.Data/Entities/Rating.cs create mode 100644 Jellyfin.Data/Entities/RatingSource.cs create mode 100644 Jellyfin.Data/Entities/Release.cs create mode 100644 Jellyfin.Data/Entities/Season.cs create mode 100644 Jellyfin.Data/Entities/SeasonMetadata.cs create mode 100644 Jellyfin.Data/Entities/Series.cs create mode 100644 Jellyfin.Data/Entities/SeriesMetadata.cs create mode 100644 Jellyfin.Data/Entities/Track.cs create mode 100644 Jellyfin.Data/Entities/TrackMetadata.cs create mode 100644 Jellyfin.Data/Entities/User.cs create mode 100644 Jellyfin.Data/Enums/ArtKind.cs create mode 100644 Jellyfin.Data/Enums/MediaFileKind.cs create mode 100644 Jellyfin.Data/Enums/PersonRoleType.cs create mode 100644 Jellyfin.Data/Enums/Weekday.cs create mode 100644 Jellyfin.Data/Jellyfin.Data.csproj create mode 100644 Jellyfin.Data/Structs/.gitkeep diff --git a/Jellyfin.Data/DbContexts/Jellyfin.cs b/Jellyfin.Data/DbContexts/Jellyfin.cs new file mode 100644 index 0000000000..fd488ce7d7 --- /dev/null +++ b/Jellyfin.Data/DbContexts/Jellyfin.cs @@ -0,0 +1,1140 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Jellyfin.Data.DbContexts +{ + /// <inheritdoc/> + public partial class Jellyfin : DbContext + { + #region DbSets + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Artwork> Artwork { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Book> Books { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.BookMetadata> BookMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Chapter> Chapters { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Collection> Collections { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CollectionItem> CollectionItems { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Company> Companies { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CompanyMetadata> CompanyMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItem> CustomItems { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItemMetadata> CustomItemMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Episode> Episodes { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.EpisodeMetadata> EpisodeMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Genre> Genres { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Group> Groups { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Library> Libraries { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryItem> LibraryItems { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryRoot> LibraryRoot { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFile> MediaFiles { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFileStream> MediaFileStream { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Metadata> Metadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProvider> MetadataProviders { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProviderId> MetadataProviderIds { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Movie> Movies { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MovieMetadata> MovieMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbum> MusicAlbums { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata> MusicAlbumMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Permission> Permissions { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Person> People { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PersonRole> PersonRoles { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Photo> Photo { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PhotoMetadata> PhotoMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Preference> Preferences { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Rating> Ratings { get; set; } + + /// <summary> + /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to + /// store review ratings, not age ratings + /// </summary> + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.RatingSource> RatingSources { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Release> Releases { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Season> Seasons { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeasonMetadata> SeasonMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Series> Series { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeriesMetadata> SeriesMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Track> Tracks { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.TrackMetadata> TrackMetadata { get; set; } + public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.User> Users { get; set; } + #endregion DbSets + + /// <summary> + /// Default connection string + /// </summary> + public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; + + /// <inheritdoc /> + public Jellyfin(DbContextOptions<Jellyfin> options) : base(options) + { + } + + partial void CustomInit(DbContextOptionsBuilder optionsBuilder); + + /// <inheritdoc /> + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + CustomInit(optionsBuilder); + } + + partial void OnModelCreatingImpl(ModelBuilder modelBuilder); + partial void OnModelCreatedImpl(ModelBuilder modelBuilder); + + /// <inheritdoc /> + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + OnModelCreatingImpl(modelBuilder); + + modelBuilder.HasDefaultSchema("jellyfin"); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() + .ToTable("Artwork") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() + .Property(t => t.Path) + .HasMaxLength(65535) + .IsRequired() + .HasField("_Path") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() + .Property(t => t.Kind) + .IsRequired() + .HasField("_Kind") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>().HasIndex(t => t.Kind); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() + .HasMany(x => x.BookMetadata) + .WithOne() + .HasForeignKey("BookMetadata_BookMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() + .Property(t => t.ISBN) + .HasField("_ISBN") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() + .HasMany(x => x.Publishers) + .WithOne() + .HasForeignKey("Company_Publishers_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .ToTable("Chapter") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.Name) + .HasMaxLength(1024) + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.Language) + .HasMaxLength(3) + .IsRequired() + .HasField("_Language") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.TimeStart) + .IsRequired() + .HasField("_TimeStart") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.TimeEnd) + .HasField("_TimeEnd") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() + .ToTable("Collection") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() + .Property(t => t.Name) + .HasMaxLength(1024) + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() + .HasMany(x => x.CollectionItem) + .WithOne() + .HasForeignKey("CollectionItem_CollectionItem_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .ToTable("CollectionItem") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .HasOne(x => x.LibraryItem) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("LibraryItem_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .HasOne(x => x.Next) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Next_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() + .HasOne(x => x.Previous) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Previous_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() + .ToTable("Company") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() + .HasMany(x => x.CompanyMetadata) + .WithOne() + .HasForeignKey("CompanyMetadata_CompanyMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() + .HasOne(x => x.Parent) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.Company>("Company_Parent_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() + .Property(t => t.Description) + .HasMaxLength(65535) + .HasField("_Description") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() + .Property(t => t.Headquarters) + .HasMaxLength(255) + .HasField("_Headquarters") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() + .Property(t => t.Country) + .HasMaxLength(2) + .HasField("_Country") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() + .Property(t => t.Homepage) + .HasMaxLength(1024) + .HasField("_Homepage") + .UsePropertyAccessMode(PropertyAccessMode.Property); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() + .HasMany(x => x.CustomItemMetadata) + .WithOne() + .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() + .Property(t => t.EpisodeNumber) + .HasField("_EpisodeNumber") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() + .HasMany(x => x.EpisodeMetadata) + .WithOne() + .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() + .Property(t => t.Outline) + .HasMaxLength(1024) + .HasField("_Outline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() + .Property(t => t.Plot) + .HasMaxLength(65535) + .HasField("_Plot") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() + .Property(t => t.Tagline) + .HasMaxLength(1024) + .HasField("_Tagline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() + .ToTable("Genre") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() + .Property(t => t.Name) + .HasMaxLength(255) + .IsRequired() + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>().HasIndex(t => t.Name) + .IsUnique(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .ToTable("Groups") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .Property(t => t.Id) + .IsRequired() + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .Property(t => t.Name) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>().Property<byte[]>("Timestamp").IsConcurrencyToken(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .HasMany(x => x.GroupPermissions) + .WithOne() + .HasForeignKey("Permission_GroupPermissions_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .HasMany(x => x.ProviderMappings) + .WithOne() + .HasForeignKey("ProviderMapping_ProviderMappings_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() + .HasMany(x => x.Preferences) + .WithOne() + .HasForeignKey("Preference_Preferences_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() + .ToTable("Library") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() + .Property(t => t.Name) + .HasMaxLength(1024) + .IsRequired() + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .ToTable("LibraryItem") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .Property(t => t.UrlId) + .IsRequired() + .HasField("_UrlId") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>().HasIndex(t => t.UrlId) + .IsUnique(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .Property(t => t.DateAdded) + .IsRequired() + .HasField("_DateAdded") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() + .HasOne(x => x.LibraryRoot) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.LibraryItem>("LibraryRoot_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .ToTable("LibraryRoot") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .Property(t => t.Path) + .HasMaxLength(65535) + .IsRequired() + .HasField("_Path") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .Property(t => t.NetworkPath) + .HasMaxLength(65535) + .HasField("_NetworkPath") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() + .HasOne(x => x.Library) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.LibraryRoot>("Library_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .ToTable("MediaFile") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .Property(t => t.Path) + .HasMaxLength(65535) + .IsRequired() + .HasField("_Path") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .Property(t => t.Kind) + .IsRequired() + .HasField("_Kind") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() + .HasMany(x => x.MediaFileStreams) + .WithOne() + .HasForeignKey("MediaFileStream_MediaFileStreams_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() + .ToTable("MediaFileStream") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() + .Property(t => t.StreamNumber) + .IsRequired() + .HasField("_StreamNumber") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .ToTable("Metadata") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.Title) + .HasMaxLength(1024) + .IsRequired() + .HasField("_Title") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.OriginalTitle) + .HasMaxLength(1024) + .HasField("_OriginalTitle") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.SortTitle) + .HasMaxLength(1024) + .HasField("_SortTitle") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.Language) + .HasMaxLength(3) + .IsRequired() + .HasField("_Language") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.ReleaseDate) + .HasField("_ReleaseDate") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.DateAdded) + .IsRequired() + .HasField("_DateAdded") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.DateModified) + .IsRequired() + .HasField("_DateModified") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .HasMany(x => x.PersonRoles) + .WithOne() + .HasForeignKey("PersonRole_PersonRoles_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .HasMany(x => x.Genres) + .WithOne() + .HasForeignKey("Genre_Genres_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .HasMany(x => x.Artwork) + .WithOne() + .HasForeignKey("Artwork_Artwork_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .HasMany(x => x.Ratings) + .WithOne() + .HasForeignKey("Rating_Ratings_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() + .HasMany(x => x.Sources) + .WithOne() + .HasForeignKey("MetadataProviderId_Sources_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() + .ToTable("MetadataProvider") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() + .Property(t => t.Name) + .HasMaxLength(1024) + .IsRequired() + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() + .ToTable("MetadataProviderId") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() + .Property(t => t.ProviderId) + .HasMaxLength(255) + .IsRequired() + .HasField("_ProviderId") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() + .HasOne(x => x.MetadataProvider) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.MetadataProviderId>("MetadataProvider_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() + .HasMany(x => x.MovieMetadata) + .WithOne() + .HasForeignKey("MovieMetadata_MovieMetadata_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() + .Property(t => t.Outline) + .HasMaxLength(1024) + .HasField("_Outline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() + .Property(t => t.Plot) + .HasMaxLength(65535) + .HasField("_Plot") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() + .Property(t => t.Tagline) + .HasMaxLength(1024) + .HasField("_Tagline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() + .Property(t => t.Country) + .HasMaxLength(2) + .HasField("_Country") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() + .HasMany(x => x.Studios) + .WithOne() + .HasForeignKey("Company_Studios_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() + .HasMany(x => x.MusicAlbumMetadata) + .WithOne() + .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() + .HasMany(x => x.Tracks) + .WithOne() + .HasForeignKey("Track_Tracks_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() + .Property(t => t.Barcode) + .HasMaxLength(255) + .HasField("_Barcode") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() + .Property(t => t.LabelNumber) + .HasMaxLength(255) + .HasField("_LabelNumber") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() + .Property(t => t.Country) + .HasMaxLength(2) + .HasField("_Country") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() + .HasMany(x => x.Labels) + .WithOne() + .HasForeignKey("Company_Labels_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() + .ToTable("Permissions") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() + .Property(t => t.Id) + .IsRequired() + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() + .Property(t => t.Kind) + .IsRequired() + .HasField("_Kind") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() + .Property(t => t.Value) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>().Property<byte[]>("Timestamp").IsConcurrencyToken(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .ToTable("Person") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.UrlId) + .IsRequired() + .HasField("_UrlId") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.Name) + .HasMaxLength(1024) + .IsRequired() + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.SourceId) + .HasMaxLength(255) + .HasField("_SourceId") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.DateAdded) + .IsRequired() + .HasField("_DateAdded") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.DateModified) + .IsRequired() + .HasField("_DateModified") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() + .HasMany(x => x.Sources) + .WithOne() + .HasForeignKey("MetadataProviderId_Sources_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .ToTable("PersonRole") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .Property(t => t.Role) + .HasMaxLength(1024) + .HasField("_Role") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .Property(t => t.Type) + .IsRequired() + .HasField("_Type") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .HasOne(x => x.Person) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Person_Id") + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .HasOne(x => x.Artwork) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Artwork_Artwork_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() + .HasMany(x => x.Sources) + .WithOne() + .HasForeignKey("MetadataProviderId_Sources_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() + .HasMany(x => x.PhotoMetadata) + .WithOne() + .HasForeignKey("PhotoMetadata_PhotoMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() + .ToTable("Preferences") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() + .Property(t => t.Id) + .IsRequired() + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() + .Property(t => t.Kind) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() + .Property(t => t.Value) + .HasMaxLength(65535) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>().Property<byte[]>("Timestamp").IsConcurrencyToken(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() + .ToTable("ProviderMappings") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() + .Property(t => t.Id) + .IsRequired() + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() + .Property(t => t.ProviderName) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() + .Property(t => t.ProviderSecrets) + .HasMaxLength(65535) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() + .Property(t => t.ProviderData) + .HasMaxLength(65535) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>().Property<byte[]>("Timestamp").IsConcurrencyToken(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .ToTable("Rating") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .Property(t => t.Value) + .IsRequired() + .HasField("_Value") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .Property(t => t.Votes) + .HasField("_Votes") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() + .HasOne(x => x.RatingType) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.Rating>("RatingSource_RatingType_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .ToTable("RatingType") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .Property(t => t.Name) + .HasMaxLength(1024) + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .Property(t => t.MaximumValue) + .IsRequired() + .HasField("_MaximumValue") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .Property(t => t.MinimumValue) + .IsRequired() + .HasField("_MinimumValue") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() + .HasOne(x => x.Source) + .WithOne() + .HasForeignKey<global::Jellyfin.Data.Entities.RatingSource>("MetadataProviderId_Source_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .ToTable("Release") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .Property(t => t.Id) + .IsRequired() + .HasField("_Id") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .Property(t => t.Name) + .HasMaxLength(1024) + .IsRequired() + .HasField("_Name") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .Property(t => t.Timestamp) + .IsRequired() + .HasField("_Timestamp") + .UsePropertyAccessMode(PropertyAccessMode.Property) + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .HasMany(x => x.MediaFiles) + .WithOne() + .HasForeignKey("MediaFile_MediaFiles_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() + .HasMany(x => x.Chapters) + .WithOne() + .HasForeignKey("Chapter_Chapters_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() + .Property(t => t.SeasonNumber) + .HasField("_SeasonNumber") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() + .HasMany(x => x.SeasonMetadata) + .WithOne() + .HasForeignKey("SeasonMetadata_SeasonMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() + .HasMany(x => x.Episodes) + .WithOne() + .HasForeignKey("Episode_Episodes_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeasonMetadata>() + .Property(t => t.Outline) + .HasMaxLength(1024) + .HasField("_Outline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() + .Property(t => t.AirsDayOfWeek) + .HasField("_AirsDayOfWeek") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() + .Property(t => t.AirsTime) + .HasField("_AirsTime") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() + .Property(t => t.FirstAired) + .HasField("_FirstAired") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() + .HasMany(x => x.SeriesMetadata) + .WithOne() + .HasForeignKey("SeriesMetadata_SeriesMetadata_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() + .HasMany(x => x.Seasons) + .WithOne() + .HasForeignKey("Season_Seasons_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() + .Property(t => t.Outline) + .HasMaxLength(1024) + .HasField("_Outline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() + .Property(t => t.Plot) + .HasMaxLength(65535) + .HasField("_Plot") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() + .Property(t => t.Tagline) + .HasMaxLength(1024) + .HasField("_Tagline") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() + .Property(t => t.Country) + .HasMaxLength(2) + .HasField("_Country") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() + .HasMany(x => x.Networks) + .WithOne() + .HasForeignKey("Company_Networks_Id") + .IsRequired(); + + modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() + .Property(t => t.TrackNumber) + .HasField("_TrackNumber") + .UsePropertyAccessMode(PropertyAccessMode.Property); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() + .HasMany(x => x.Releases) + .WithOne() + .HasForeignKey("Release_Releases_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() + .HasMany(x => x.TrackMetadata) + .WithOne() + .HasForeignKey("TrackMetadata_TrackMetadata_Id") + .IsRequired(); + + + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .ToTable("Users") + .HasKey(t => t.Id); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.Id) + .IsRequired() + .ValueGeneratedOnAdd(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.LastLoginTimestamp) + .IsRequired() + .IsRowVersion(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.Username) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.Password) + .HasMaxLength(65535); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.MustUpdatePassword) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.AudioLanguagePreference) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.AuthenticationProviderId) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.GroupedFolders) + .HasMaxLength(65535); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.InvalidLoginAttemptCount) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.LatestItemExcludes) + .HasMaxLength(65535); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.MyMediaExcludes) + .HasMaxLength(65535); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.OrderedViews) + .HasMaxLength(65535); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.SubtitleMode) + .HasMaxLength(255) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.PlayDefaultAudioTrack) + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .Property(t => t.SubtitleLanguagePrefernce) + .HasMaxLength(255); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .HasMany(x => x.Groups) + .WithOne() + .HasForeignKey("Group_Groups_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .HasMany(x => x.Permissions) + .WithOne() + .HasForeignKey("Permission_Permissions_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .HasMany(x => x.ProviderMappings) + .WithOne() + .HasForeignKey("ProviderMapping_ProviderMappings_Id") + .IsRequired(); + modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() + .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 new file mode 100644 index 0000000000..be13686dc2 --- /dev/null +++ b/Jellyfin.Data/Entities/Artwork.cs @@ -0,0 +1,208 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Artwork + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Artwork() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Artwork CreateArtworkUnsafe() + { + return new Artwork(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path"></param> + /// <param name="kind"></param> + /// <param name="_metadata0"></param> + /// <param name="_personrole1"></param> + 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; + + this.Kind = kind; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Artwork.Add(this); + + if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1)); + _personrole1.Artwork = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path"></param> + /// <param name="kind"></param> + /// <param name="_metadata0"></param> + /// <param name="_personrole1"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [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) + { + _Path = value; + } + } + } + + /// <summary> + /// Backing field for Kind + /// </summary> + internal global::Jellyfin.Data.Enums.ArtKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(global::Jellyfin.Data.Enums.ArtKind oldValue, ref global::Jellyfin.Data.Enums.ArtKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref global::Jellyfin.Data.Enums.ArtKind result); + + /// <summary> + /// Indexed, Required + /// </summary> + [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) + { + _Kind = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Book.cs b/Jellyfin.Data/Entities/Book.cs new file mode 100644 index 0000000000..30c89ae5c5 --- /dev/null +++ b/Jellyfin.Data/Entities/Book.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Book: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Book(): base() + { + BookMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.BookMetadata>(); + Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Book CreateBookUnsafe() + { + return new Book(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Book(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.BookMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.BookMetadata>(); + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Book Create(Guid urlid, DateTime dateadded) + { + return new Book(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.BookMetadata> BookMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/BookMetadata.cs b/Jellyfin.Data/Entities/BookMetadata.cs new file mode 100644 index 0000000000..3a28244d69 --- /dev/null +++ b/Jellyfin.Data/Entities/BookMetadata.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class BookMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected BookMetadata(): base() + { + Publishers = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static BookMetadata CreateBookMetadataUnsafe() + { + return new BookMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_book0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_book0 == null) throw new ArgumentNullException(nameof(_book0)); + _book0.BookMetadata.Add(this); + + this.Publishers = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_book0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for ISBN + /// </summary> + protected long? _ISBN; + /// <summary> + /// When provided in a partial class, allows value of ISBN to be changed before setting. + /// </summary> + partial void SetISBN(long? oldValue, ref long? newValue); + /// <summary> + /// When provided in a partial class, allows value of ISBN to be changed before returning. + /// </summary> + 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) + { + _ISBN = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Company> Publishers { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Chapter.cs b/Jellyfin.Data/Entities/Chapter.cs new file mode 100644 index 0000000000..21a5dd73ee --- /dev/null +++ b/Jellyfin.Data/Entities/Chapter.cs @@ -0,0 +1,274 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Chapter + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Chapter() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Chapter CreateChapterUnsafe() + { + return new Chapter(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="timestart"></param> + /// <param name="_release0"></param> + public Chapter(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) + { + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + this.TimeStart = timestart; + + if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); + _release0.Chapters.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="timestart"></param> + /// <param name="_release0"></param> + public static Chapter Create(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) + { + return new Chapter(language, timestart, _release0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Language + /// </summary> + protected string _Language; + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before setting. + /// </summary> + partial void SetLanguage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before returning. + /// </summary> + partial void GetLanguage(ref string result); + + /// <summary> + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for TimeStart + /// </summary> + protected long _TimeStart; + /// <summary> + /// When provided in a partial class, allows value of TimeStart to be changed before setting. + /// </summary> + partial void SetTimeStart(long oldValue, ref long newValue); + /// <summary> + /// When provided in a partial class, allows value of TimeStart to be changed before returning. + /// </summary> + partial void GetTimeStart(ref long result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for TimeEnd + /// </summary> + protected long? _TimeEnd; + /// <summary> + /// When provided in a partial class, allows value of TimeEnd to be changed before setting. + /// </summary> + partial void SetTimeEnd(long? oldValue, ref long? newValue); + /// <summary> + /// When provided in a partial class, allows value of TimeEnd to be changed before returning. + /// </summary> + 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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Collection.cs b/Jellyfin.Data/Entities/Collection.cs new file mode 100644 index 0000000000..68979eb2fe --- /dev/null +++ b/Jellyfin.Data/Entities/Collection.cs @@ -0,0 +1,131 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Collection + { + partial void Init(); + + /// <summary> + /// Default constructor + /// </summary> + public Collection() + { + CollectionItem = new System.Collections.Generic.LinkedList<global::Jellyfin.Data.Entities.CollectionItem>(); + + Init(); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.CollectionItem> CollectionItem { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/CollectionItem.cs b/Jellyfin.Data/Entities/CollectionItem.cs new file mode 100644 index 0000000000..8e575e0a28 --- /dev/null +++ b/Jellyfin.Data/Entities/CollectionItem.cs @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class CollectionItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CollectionItem CreateCollectionItemUnsafe() + { + return new CollectionItem(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="_collection0"></param> + /// <param name="_collectionitem1"></param> + /// <param name="_collectionitem2"></param> + 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. + + if (_collection0 == null) throw new ArgumentNullException(nameof(_collection0)); + _collection0.CollectionItem.Add(this); + + if (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1)); + _collectionitem1.Next = this; + + if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2)); + _collectionitem2.Previous = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="_collection0"></param> + /// <param name="_collectionitem1"></param> + /// <param name="_collectionitem2"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + public virtual global::Jellyfin.Data.Entities.LibraryItem LibraryItem { get; set; } + + /// <remarks> + /// TODO check if this properly updated dependant and has the proper principal relationship + /// </remarks> + public virtual global::Jellyfin.Data.Entities.CollectionItem Next { get; set; } + + /// <remarks> + /// TODO check if this properly updated dependant and has the proper principal relationship + /// </remarks> + public virtual global::Jellyfin.Data.Entities.CollectionItem Previous { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Company.cs b/Jellyfin.Data/Entities/Company.cs new file mode 100644 index 0000000000..444ae9c564 --- /dev/null +++ b/Jellyfin.Data/Entities/Company.cs @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Company + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Company() + { + CompanyMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CompanyMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Company CreateCompanyUnsafe() + { + return new Company(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="_moviemetadata0"></param> + /// <param name="_seriesmetadata1"></param> + /// <param name="_musicalbummetadata2"></param> + /// <param name="_bookmetadata3"></param> + /// <param name="_company4"></param> + 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); + + 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 (_bookmetadata3 == null) throw new ArgumentNullException(nameof(_bookmetadata3)); + _bookmetadata3.Publishers.Add(this); + + if (_company4 == null) throw new ArgumentNullException(nameof(_company4)); + _company4.Parent = this; + + this.CompanyMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CompanyMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="_moviemetadata0"></param> + /// <param name="_seriesmetadata1"></param> + /// <param name="_musicalbummetadata2"></param> + /// <param name="_bookmetadata3"></param> + /// <param name="_company4"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.CompanyMetadata> CompanyMetadata { get; protected 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 new file mode 100644 index 0000000000..6d636e8846 --- /dev/null +++ b/Jellyfin.Data/Entities/CompanyMetadata.cs @@ -0,0 +1,234 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class CompanyMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CompanyMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CompanyMetadata CreateCompanyMetadataUnsafe() + { + return new CompanyMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_company0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_company0 == null) throw new ArgumentNullException(nameof(_company0)); + _company0.CompanyMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_company0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Description + /// </summary> + protected string _Description; + /// <summary> + /// When provided in a partial class, allows value of Description to be changed before setting. + /// </summary> + partial void SetDescription(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Description to be changed before returning. + /// </summary> + partial void GetDescription(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [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) + { + _Description = value; + } + } + } + + /// <summary> + /// Backing field for Headquarters + /// </summary> + protected string _Headquarters; + /// <summary> + /// When provided in a partial class, allows value of Headquarters to be changed before setting. + /// </summary> + partial void SetHeadquarters(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Headquarters to be changed before returning. + /// </summary> + partial void GetHeadquarters(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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) + { + _Headquarters = value; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Homepage + /// </summary> + protected string _Homepage; + /// <summary> + /// When provided in a partial class, allows value of Homepage to be changed before setting. + /// </summary> + partial void SetHomepage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Homepage to be changed before returning. + /// </summary> + partial void GetHomepage(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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 new file mode 100644 index 0000000000..eb6d2752d3 --- /dev/null +++ b/Jellyfin.Data/Entities/CustomItem.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class CustomItem: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CustomItem(): base() + { + CustomItemMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CustomItemMetadata>(); + Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CustomItem CreateCustomItemUnsafe() + { + return new CustomItem(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public CustomItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.CustomItemMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CustomItemMetadata>(); + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static CustomItem Create(Guid urlid, DateTime dateadded) + { + return new CustomItem(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.CustomItemMetadata> CustomItemMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/CustomItemMetadata.cs b/Jellyfin.Data/Entities/CustomItemMetadata.cs new file mode 100644 index 0000000000..f2c15d3fe3 --- /dev/null +++ b/Jellyfin.Data/Entities/CustomItemMetadata.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class CustomItemMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CustomItemMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CustomItemMetadata CreateCustomItemMetadataUnsafe() + { + return new CustomItemMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_customitem0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_customitem0 == null) throw new ArgumentNullException(nameof(_customitem0)); + _customitem0.CustomItemMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_customitem0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Episode.cs b/Jellyfin.Data/Entities/Episode.cs new file mode 100644 index 0000000000..3a23f0976f --- /dev/null +++ b/Jellyfin.Data/Entities/Episode.cs @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Episode: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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<global::Jellyfin.Data.Entities.Release>(); + EpisodeMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.EpisodeMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Episode CreateEpisodeUnsafe() + { + return new Episode(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_season0"></param> + 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. + + this.UrlId = urlid; + + if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.Episodes.Add(this); + + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + this.EpisodeMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.EpisodeMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_season0"></param> + public static Episode Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Season _season0) + { + return new Episode(urlid, dateadded, _season0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for EpisodeNumber + /// </summary> + protected int? _EpisodeNumber; + /// <summary> + /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting. + /// </summary> + partial void SetEpisodeNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning. + /// </summary> + 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) + { + _EpisodeNumber = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.EpisodeMetadata> EpisodeMetadata { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/EpisodeMetadata.cs b/Jellyfin.Data/Entities/EpisodeMetadata.cs new file mode 100644 index 0000000000..963219140d --- /dev/null +++ b/Jellyfin.Data/Entities/EpisodeMetadata.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class EpisodeMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected EpisodeMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static EpisodeMetadata CreateEpisodeMetadataUnsafe() + { + return new EpisodeMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_episode0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_episode0 == null) throw new ArgumentNullException(nameof(_episode0)); + _episode0.EpisodeMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_episode0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Outline = value; + } + } + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [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) + { + _Plot = value; + } + } + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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 new file mode 100644 index 0000000000..982600553e --- /dev/null +++ b/Jellyfin.Data/Entities/Genre.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Genre + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Genre() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Genre CreateGenreUnsafe() + { + return new Genre(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_metadata0"></param> + public Genre(string name, global::Jellyfin.Data.Entities.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); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_metadata0"></param> + public static Genre Create(string name, global::Jellyfin.Data.Entities.Metadata _metadata0) + { + return new Genre(name, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + internal string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Indexed, Required, Max length = 255 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs new file mode 100644 index 0000000000..ff19e9b019 --- /dev/null +++ b/Jellyfin.Data/Entities/Group.cs @@ -0,0 +1,115 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Group + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Group() + { + GroupPermissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); + ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); + Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Group CreateGroupUnsafe() + { + return new Group(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_user0"></param> + public Group(string name, global::Jellyfin.Data.Entities.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); + + this.GroupPermissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); + this.ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); + this.Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_user0"></param> + public static Group Create(string name, global::Jellyfin.Data.Entities.User _user0) + { + return new Group(name, _user0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Name { get; set; } + + /// <summary> + /// Concurrency token + /// </summary> + [Timestamp] + public Byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Permission> GroupPermissions { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Preference> Preferences { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Library.cs b/Jellyfin.Data/Entities/Library.cs new file mode 100644 index 0000000000..19ca142947 --- /dev/null +++ b/Jellyfin.Data/Entities/Library.cs @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Library + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Library() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Library CreateLibraryUnsafe() + { + return new Library(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + public Library(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + public static Library Create(string name) + { + return new Library(name); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/LibraryItem.cs b/Jellyfin.Data/Entities/LibraryItem.cs new file mode 100644 index 0000000000..1987196d69 --- /dev/null +++ b/Jellyfin.Data/Entities/LibraryItem.cs @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public abstract partial class LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to being abstract. + /// </summary> + protected LibraryItem() + { + Init(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + protected LibraryItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + + Init(); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for UrlId + /// </summary> + internal Guid _UrlId; + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// </summary> + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// </summary> + partial void GetUrlId(ref Guid result); + + /// <summary> + /// Indexed, Required + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// </summary> + [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) + { + _UrlId = value; + } + } + } + + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + public virtual global::Jellyfin.Data.Entities.LibraryRoot LibraryRoot { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/LibraryRoot.cs b/Jellyfin.Data/Entities/LibraryRoot.cs new file mode 100644 index 0000000000..015fc4ea98 --- /dev/null +++ b/Jellyfin.Data/Entities/LibraryRoot.cs @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class LibraryRoot + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected LibraryRoot() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static LibraryRoot CreateLibraryRootUnsafe() + { + return new LibraryRoot(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path">Absolute Path</param> + public LibraryRoot(string path) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path">Absolute Path</param> + public static LibraryRoot Create(string path) + { + return new LibraryRoot(path); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// Absolute Path + /// </summary> + [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) + { + _Path = value; + } + } + } + + /// <summary> + /// Backing field for NetworkPath + /// </summary> + protected string _NetworkPath; + /// <summary> + /// When provided in a partial class, allows value of NetworkPath to be changed before setting. + /// </summary> + partial void SetNetworkPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of NetworkPath to be changed before returning. + /// </summary> + partial void GetNetworkPath(ref string result); + + /// <summary> + /// Max length = 65535 + /// Absolute network path, for example for transcoding sattelites. + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + public virtual global::Jellyfin.Data.Entities.Library Library { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MediaFile.cs b/Jellyfin.Data/Entities/MediaFile.cs new file mode 100644 index 0000000000..2a47a96325 --- /dev/null +++ b/Jellyfin.Data/Entities/MediaFile.cs @@ -0,0 +1,209 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MediaFile + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MediaFile() + { + MediaFileStreams = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFileStream>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MediaFile CreateMediaFileUnsafe() + { + return new MediaFile(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path">Relative to the LibraryRoot</param> + /// <param name="kind"></param> + /// <param name="_release0"></param> + 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; + + this.Kind = kind; + + if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); + _release0.MediaFiles.Add(this); + + this.MediaFileStreams = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFileStream>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path">Relative to the LibraryRoot</param> + /// <param name="kind"></param> + /// <param name="_release0"></param> + public static MediaFile Create(string path, global::Jellyfin.Data.Enums.MediaFileKind kind, global::Jellyfin.Data.Entities.Release _release0) + { + return new MediaFile(path, kind, _release0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// Relative to the LibraryRoot + /// </summary> + [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) + { + _Path = value; + } + } + } + + /// <summary> + /// Backing field for Kind + /// </summary> + protected global::Jellyfin.Data.Enums.MediaFileKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(global::Jellyfin.Data.Enums.MediaFileKind oldValue, ref global::Jellyfin.Data.Enums.MediaFileKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref global::Jellyfin.Data.Enums.MediaFileKind result); + + /// <summary> + /// Required + /// </summary> + [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) + { + _Kind = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.MediaFileStream> MediaFileStreams { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MediaFileStream.cs b/Jellyfin.Data/Entities/MediaFileStream.cs new file mode 100644 index 0000000000..6593d3cf75 --- /dev/null +++ b/Jellyfin.Data/Entities/MediaFileStream.cs @@ -0,0 +1,160 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MediaFileStream + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MediaFileStream() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MediaFileStream CreateMediaFileStreamUnsafe() + { + return new MediaFileStream(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="streamnumber"></param> + /// <param name="_mediafile0"></param> + public MediaFileStream(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) + { + this.StreamNumber = streamnumber; + + if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0)); + _mediafile0.MediaFileStreams.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="streamnumber"></param> + /// <param name="_mediafile0"></param> + public static MediaFileStream Create(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) + { + return new MediaFileStream(streamnumber, _mediafile0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for StreamNumber + /// </summary> + protected int _StreamNumber; + /// <summary> + /// When provided in a partial class, allows value of StreamNumber to be changed before setting. + /// </summary> + partial void SetStreamNumber(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of StreamNumber to be changed before returning. + /// </summary> + partial void GetStreamNumber(ref int result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Metadata.cs b/Jellyfin.Data/Entities/Metadata.cs new file mode 100644 index 0000000000..6057017e94 --- /dev/null +++ b/Jellyfin.Data/Entities/Metadata.cs @@ -0,0 +1,385 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public abstract partial class Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to being abstract. + /// </summary> + protected Metadata() + { + PersonRoles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PersonRole>(); + Genres = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Genre>(); + Artwork = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Artwork>(); + Ratings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Rating>(); + Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + 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; + + this.PersonRoles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PersonRole>(); + this.Genres = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Genre>(); + this.Artwork = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Artwork>(); + this.Ratings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Rating>(); + this.Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Title + /// </summary> + protected string _Title; + /// <summary> + /// When provided in a partial class, allows value of Title to be changed before setting. + /// </summary> + partial void SetTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Title to be changed before returning. + /// </summary> + partial void GetTitle(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// The title or name of the object + /// </summary> + [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) + { + _Title = value; + } + } + } + + /// <summary> + /// Backing field for OriginalTitle + /// </summary> + protected string _OriginalTitle; + /// <summary> + /// When provided in a partial class, allows value of OriginalTitle to be changed before setting. + /// </summary> + partial void SetOriginalTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of OriginalTitle to be changed before returning. + /// </summary> + partial void GetOriginalTitle(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _OriginalTitle = value; + } + } + } + + /// <summary> + /// Backing field for SortTitle + /// </summary> + protected string _SortTitle; + /// <summary> + /// When provided in a partial class, allows value of SortTitle to be changed before setting. + /// </summary> + partial void SetSortTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of SortTitle to be changed before returning. + /// </summary> + partial void GetSortTitle(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _SortTitle = value; + } + } + } + + /// <summary> + /// Backing field for Language + /// </summary> + protected string _Language; + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before setting. + /// </summary> + partial void SetLanguage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before returning. + /// </summary> + partial void GetLanguage(ref string result); + + /// <summary> + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for ReleaseDate + /// </summary> + protected DateTimeOffset? _ReleaseDate; + /// <summary> + /// When provided in a partial class, allows value of ReleaseDate to be changed before setting. + /// </summary> + partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of ReleaseDate to be changed before returning. + /// </summary> + 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) + { + _ReleaseDate = value; + } + } + } + + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for DateModified + /// </summary> + protected DateTime _DateModified; + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// </summary> + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// </summary> + partial void GetDateModified(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.PersonRole> PersonRoles { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Genre> Genres { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Artwork> Artwork { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Rating> Ratings { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MetadataProvider.cs b/Jellyfin.Data/Entities/MetadataProvider.cs new file mode 100644 index 0000000000..3a8f5854eb --- /dev/null +++ b/Jellyfin.Data/Entities/MetadataProvider.cs @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MetadataProvider + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MetadataProvider() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MetadataProvider CreateMetadataProviderUnsafe() + { + return new MetadataProvider(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + public MetadataProvider(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + public static MetadataProvider Create(string name) + { + return new MetadataProvider(name); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/MetadataProviderId.cs b/Jellyfin.Data/Entities/MetadataProviderId.cs new file mode 100644 index 0000000000..87ff19e26d --- /dev/null +++ b/Jellyfin.Data/Entities/MetadataProviderId.cs @@ -0,0 +1,189 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MetadataProviderId + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MetadataProviderId CreateMetadataProviderIdUnsafe() + { + return new MetadataProviderId(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="providerid"></param> + /// <param name="_metadata0"></param> + /// <param name="_person1"></param> + /// <param name="_personrole2"></param> + /// <param name="_ratingsource3"></param> + 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. + + 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 (_person1 == null) throw new ArgumentNullException(nameof(_person1)); + _person1.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; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="providerid"></param> + /// <param name="_metadata0"></param> + /// <param name="_person1"></param> + /// <param name="_personrole2"></param> + /// <param name="_ratingsource3"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for ProviderId + /// </summary> + protected string _ProviderId; + /// <summary> + /// When provided in a partial class, allows value of ProviderId to be changed before setting. + /// </summary> + partial void SetProviderId(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of ProviderId to be changed before returning. + /// </summary> + partial void GetProviderId(ref string result); + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + public virtual global::Jellyfin.Data.Entities.MetadataProvider MetadataProvider { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Movie.cs b/Jellyfin.Data/Entities/Movie.cs new file mode 100644 index 0000000000..dfcc05a943 --- /dev/null +++ b/Jellyfin.Data/Entities/Movie.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Movie: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Movie(): base() + { + Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + MovieMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MovieMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Movie CreateMovieUnsafe() + { + return new Movie(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Movie(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + this.MovieMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MovieMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Movie Create(Guid urlid, DateTime dateadded) + { + return new Movie(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.MovieMetadata> MovieMetadata { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MovieMetadata.cs b/Jellyfin.Data/Entities/MovieMetadata.cs new file mode 100644 index 0000000000..bd847da8fa --- /dev/null +++ b/Jellyfin.Data/Entities/MovieMetadata.cs @@ -0,0 +1,239 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MovieMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MovieMetadata(): base() + { + Studios = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MovieMetadata CreateMovieMetadataUnsafe() + { + return new MovieMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_movie0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); + _movie0.MovieMetadata.Add(this); + + this.Studios = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_movie0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Outline = value; + } + } + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [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) + { + _Plot = value; + } + } + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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 + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Company> Studios { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MusicAlbum.cs b/Jellyfin.Data/Entities/MusicAlbum.cs new file mode 100644 index 0000000000..417f2595bd --- /dev/null +++ b/Jellyfin.Data/Entities/MusicAlbum.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MusicAlbum: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MusicAlbum(): base() + { + MusicAlbumMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata>(); + Tracks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Track>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MusicAlbum CreateMusicAlbumUnsafe() + { + return new MusicAlbum(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public MusicAlbum(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.MusicAlbumMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata>(); + this.Tracks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Track>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static MusicAlbum Create(Guid urlid, DateTime dateadded) + { + return new MusicAlbum(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Track> Tracks { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs new file mode 100644 index 0000000000..cd72ecba51 --- /dev/null +++ b/Jellyfin.Data/Entities/MusicAlbumMetadata.cs @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class MusicAlbumMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MusicAlbumMetadata(): base() + { + Labels = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe() + { + return new MusicAlbumMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_musicalbum0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.MusicAlbumMetadata.Add(this); + + this.Labels = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_musicalbum0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Barcode + /// </summary> + protected string _Barcode; + /// <summary> + /// When provided in a partial class, allows value of Barcode to be changed before setting. + /// </summary> + partial void SetBarcode(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Barcode to be changed before returning. + /// </summary> + partial void GetBarcode(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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) + { + _Barcode = value; + } + } + } + + /// <summary> + /// Backing field for LabelNumber + /// </summary> + protected string _LabelNumber; + /// <summary> + /// When provided in a partial class, allows value of LabelNumber to be changed before setting. + /// </summary> + partial void SetLabelNumber(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of LabelNumber to be changed before returning. + /// </summary> + partial void GetLabelNumber(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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) + { + _LabelNumber = value; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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 + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Company> Labels { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs new file mode 100644 index 0000000000..a717fc83fe --- /dev/null +++ b/Jellyfin.Data/Entities/Permission.cs @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Permission + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Permission() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Permission CreatePermissionUnsafe() + { + return new Permission(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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; + + this.Value = value; + + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Permissions.Add(this); + + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.GroupPermissions.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + public int Id { get; protected set; } + + /// <summary> + /// Backing field for Kind + /// </summary> + protected global::Jellyfin.Data.Enums.PermissionKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(global::Jellyfin.Data.Enums.PermissionKind oldValue, ref global::Jellyfin.Data.Enums.PermissionKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref global::Jellyfin.Data.Enums.PermissionKind result); + + /// <summary> + /// Required + /// </summary> + [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) + { + _Kind = value; + OnPropertyChanged(); + } + } + } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool Value { get; set; } + + /// <summary> + /// Concurrency token + /// </summary> + [Timestamp] + public Byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + 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 new file mode 100644 index 0000000000..971298674a --- /dev/null +++ b/Jellyfin.Data/Entities/PermissionKind.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +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 new file mode 100644 index 0000000000..3437b9581d --- /dev/null +++ b/Jellyfin.Data/Entities/Person.cs @@ -0,0 +1,312 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Person + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Person() + { + Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Person CreatePersonUnsafe() + { + return new Person(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid"></param> + /// <param name="name"></param> + 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; + + this.Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid"></param> + /// <param name="name"></param> + public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) + { + return new Person(urlid, name, dateadded, datemodified); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for UrlId + /// </summary> + protected Guid _UrlId; + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// </summary> + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// </summary> + partial void GetUrlId(ref Guid result); + + /// <summary> + /// Required + /// </summary> + [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) + { + _UrlId = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for SourceId + /// </summary> + protected string _SourceId; + /// <summary> + /// When provided in a partial class, allows value of SourceId to be changed before setting. + /// </summary> + partial void SetSourceId(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of SourceId to be changed before returning. + /// </summary> + partial void GetSourceId(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for DateModified + /// </summary> + protected DateTime _DateModified; + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// </summary> + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// </summary> + partial void GetDateModified(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/PersonRole.cs b/Jellyfin.Data/Entities/PersonRole.cs new file mode 100644 index 0000000000..d8e2dbc11a --- /dev/null +++ b/Jellyfin.Data/Entities/PersonRole.cs @@ -0,0 +1,215 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class PersonRole + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static PersonRole CreatePersonRoleUnsafe() + { + return new PersonRole(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="type"></param> + /// <param name="_metadata0"></param> + 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. + + this.Type = type; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.PersonRoles.Add(this); + + this.Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="type"></param> + /// <param name="_metadata0"></param> + public static PersonRole Create(global::Jellyfin.Data.Enums.PersonRoleType type, global::Jellyfin.Data.Entities.Metadata _metadata0) + { + return new PersonRole(type, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Role + /// </summary> + protected string _Role; + /// <summary> + /// When provided in a partial class, allows value of Role to be changed before setting. + /// </summary> + partial void SetRole(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Role to be changed before returning. + /// </summary> + partial void GetRole(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Role = value; + } + } + } + + /// <summary> + /// Backing field for Type + /// </summary> + protected global::Jellyfin.Data.Enums.PersonRoleType _Type; + /// <summary> + /// When provided in a partial class, allows value of Type to be changed before setting. + /// </summary> + partial void SetType(global::Jellyfin.Data.Enums.PersonRoleType oldValue, ref global::Jellyfin.Data.Enums.PersonRoleType newValue); + /// <summary> + /// When provided in a partial class, allows value of Type to be changed before returning. + /// </summary> + partial void GetType(ref global::Jellyfin.Data.Enums.PersonRoleType result); + + /// <summary> + /// Required + /// </summary> + [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) + { + _Type = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + public virtual global::Jellyfin.Data.Entities.Person Person { get; set; } + + public virtual global::Jellyfin.Data.Entities.Artwork Artwork { get; set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Photo.cs b/Jellyfin.Data/Entities/Photo.cs new file mode 100644 index 0000000000..16c97fef54 --- /dev/null +++ b/Jellyfin.Data/Entities/Photo.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Photo: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Photo(): base() + { + PhotoMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PhotoMetadata>(); + Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Photo CreatePhotoUnsafe() + { + return new Photo(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Photo(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.PhotoMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PhotoMetadata>(); + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Photo Create(Guid urlid, DateTime dateadded) + { + return new Photo(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.PhotoMetadata> PhotoMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/PhotoMetadata.cs b/Jellyfin.Data/Entities/PhotoMetadata.cs new file mode 100644 index 0000000000..9c47d022e9 --- /dev/null +++ b/Jellyfin.Data/Entities/PhotoMetadata.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class PhotoMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected PhotoMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static PhotoMetadata CreatePhotoMetadataUnsafe() + { + return new PhotoMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_photo0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_photo0 == null) throw new ArgumentNullException(nameof(_photo0)); + _photo0.PhotoMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_photo0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs new file mode 100644 index 0000000000..3d69ea2f3a --- /dev/null +++ b/Jellyfin.Data/Entities/Preference.cs @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Preference + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Preference() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Preference CreatePreferenceUnsafe() + { + return new Preference(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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; + + 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 (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.Preferences.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + public int Id { get; protected set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public global::Jellyfin.Data.Enums.PreferenceKind Kind { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Value { get; set; } + + /// <summary> + /// Concurrency token + /// </summary> + [Timestamp] + public Byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/PreferenceKind.cs b/Jellyfin.Data/Entities/PreferenceKind.cs new file mode 100644 index 0000000000..e6673afb1b --- /dev/null +++ b/Jellyfin.Data/Entities/PreferenceKind.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +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 new file mode 100644 index 0000000000..e50a01489c --- /dev/null +++ b/Jellyfin.Data/Entities/ProviderMapping.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class ProviderMapping + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected ProviderMapping() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static ProviderMapping CreateProviderMappingUnsafe() + { + return new ProviderMapping(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="providername"></param> + /// <param name="providersecrets"></param> + /// <param name="providerdata"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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; + + 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 (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.ProviderMappings.Add(this); + + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.ProviderMappings.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="providername"></param> + /// <param name="providersecrets"></param> + /// <param name="providerdata"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string ProviderName { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderSecrets { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderData { get; set; } + + /// <summary> + /// Concurrency token + /// </summary> + [Timestamp] + public Byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Rating.cs b/Jellyfin.Data/Entities/Rating.cs new file mode 100644 index 0000000000..b1098a1d7d --- /dev/null +++ b/Jellyfin.Data/Entities/Rating.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Rating + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Rating() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Rating CreateRatingUnsafe() + { + return new Rating(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="value"></param> + /// <param name="_metadata0"></param> + public Rating(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) + { + this.Value = value; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Ratings.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="value"></param> + /// <param name="_metadata0"></param> + public static Rating Create(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) + { + return new Rating(value, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Value + /// </summary> + protected double _Value; + /// <summary> + /// When provided in a partial class, allows value of Value to be changed before setting. + /// </summary> + partial void SetValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of Value to be changed before returning. + /// </summary> + partial void GetValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [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) + { + _Value = value; + } + } + } + + /// <summary> + /// Backing field for Votes + /// </summary> + protected int? _Votes; + /// <summary> + /// When provided in a partial class, allows value of Votes to be changed before setting. + /// </summary> + partial void SetVotes(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of Votes to be changed before returning. + /// </summary> + partial void GetVotes(ref int? 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) + { + _Votes = value; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// If this is NULL it's the internal user rating. + /// </summary> + public virtual global::Jellyfin.Data.Entities.RatingSource RatingType { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/RatingSource.cs b/Jellyfin.Data/Entities/RatingSource.cs new file mode 100644 index 0000000000..32d5634c2b --- /dev/null +++ b/Jellyfin.Data/Entities/RatingSource.cs @@ -0,0 +1,242 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +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 +{ + /// <summary> + /// This is the entity to store review ratings, not age ratings + /// </summary> + public partial class RatingSource + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected RatingSource() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static RatingSource CreateRatingSourceUnsafe() + { + return new RatingSource(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="maximumvalue"></param> + /// <param name="minimumvalue"></param> + /// <param name="_rating0"></param> + public RatingSource(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) + { + this.MaximumValue = maximumvalue; + + this.MinimumValue = minimumvalue; + + if (_rating0 == null) throw new ArgumentNullException(nameof(_rating0)); + _rating0.RatingType = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="maximumvalue"></param> + /// <param name="minimumvalue"></param> + /// <param name="_rating0"></param> + public static RatingSource Create(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) + { + return new RatingSource(maximumvalue, minimumvalue, _rating0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for MaximumValue + /// </summary> + protected double _MaximumValue; + /// <summary> + /// When provided in a partial class, allows value of MaximumValue to be changed before setting. + /// </summary> + partial void SetMaximumValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of MaximumValue to be changed before returning. + /// </summary> + partial void GetMaximumValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for MinimumValue + /// </summary> + protected double _MinimumValue; + /// <summary> + /// When provided in a partial class, allows value of MinimumValue to be changed before setting. + /// </summary> + partial void SetMinimumValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of MinimumValue to be changed before returning. + /// </summary> + partial void GetMinimumValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual global::Jellyfin.Data.Entities.MetadataProviderId Source { get; set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Release.cs b/Jellyfin.Data/Entities/Release.cs new file mode 100644 index 0000000000..e02f70be89 --- /dev/null +++ b/Jellyfin.Data/Entities/Release.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Release + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Release() + { + MediaFiles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFile>(); + Chapters = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Chapter>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Release CreateReleaseUnsafe() + { + return new Release(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_movie0"></param> + /// <param name="_episode1"></param> + /// <param name="_track2"></param> + /// <param name="_customitem3"></param> + /// <param name="_book4"></param> + /// <param name="_photo5"></param> + 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; + + 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 (_track2 == null) throw new ArgumentNullException(nameof(_track2)); + _track2.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 (_photo5 == null) throw new ArgumentNullException(nameof(_photo5)); + _photo5.Releases.Add(this); + + this.MediaFiles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFile>(); + this.Chapters = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Chapter>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_movie0"></param> + /// <param name="_episode1"></param> + /// <param name="_track2"></param> + /// <param name="_customitem3"></param> + /// <param name="_book4"></param> + /// <param name="_photo5"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] Timestamp { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.MediaFile> MediaFiles { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Chapter> Chapters { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Season.cs b/Jellyfin.Data/Entities/Season.cs new file mode 100644 index 0000000000..fdfdf24091 --- /dev/null +++ b/Jellyfin.Data/Entities/Season.cs @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Season: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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<global::Jellyfin.Data.Entities.SeasonMetadata>(); + Episodes = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Episode>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Season CreateSeasonUnsafe() + { + return new Season(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_series0"></param> + 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. + + this.UrlId = urlid; + + if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.Seasons.Add(this); + + this.SeasonMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeasonMetadata>(); + this.Episodes = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Episode>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_series0"></param> + public static Season Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Series _series0) + { + return new Season(urlid, dateadded, _series0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for SeasonNumber + /// </summary> + protected int? _SeasonNumber; + /// <summary> + /// When provided in a partial class, allows value of SeasonNumber to be changed before setting. + /// </summary> + partial void SetSeasonNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of SeasonNumber to be changed before returning. + /// </summary> + 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) + { + _SeasonNumber = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.SeasonMetadata> SeasonMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Episode> Episodes { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/SeasonMetadata.cs b/Jellyfin.Data/Entities/SeasonMetadata.cs new file mode 100644 index 0000000000..5939cbbca1 --- /dev/null +++ b/Jellyfin.Data/Entities/SeasonMetadata.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class SeasonMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected SeasonMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static SeasonMetadata CreateSeasonMetadataUnsafe() + { + return new SeasonMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_season0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.SeasonMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_season0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Outline = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/Series.cs b/Jellyfin.Data/Entities/Series.cs new file mode 100644 index 0000000000..a57064824c --- /dev/null +++ b/Jellyfin.Data/Entities/Series.cs @@ -0,0 +1,183 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Series: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Series(): base() + { + SeriesMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeriesMetadata>(); + Seasons = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Season>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Series CreateSeriesUnsafe() + { + return new Series(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Series(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.SeriesMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeriesMetadata>(); + this.Seasons = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Season>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Series Create(Guid urlid, DateTime dateadded) + { + return new Series(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for AirsDayOfWeek + /// </summary> + protected global::Jellyfin.Data.Enums.Weekday? _AirsDayOfWeek; + /// <summary> + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting. + /// </summary> + partial void SetAirsDayOfWeek(global::Jellyfin.Data.Enums.Weekday? oldValue, ref global::Jellyfin.Data.Enums.Weekday? newValue); + /// <summary> + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning. + /// </summary> + partial void GetAirsDayOfWeek(ref global::Jellyfin.Data.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) + { + _AirsDayOfWeek = value; + } + } + } + + /// <summary> + /// Backing field for AirsTime + /// </summary> + protected DateTimeOffset? _AirsTime; + /// <summary> + /// When provided in a partial class, allows value of AirsTime to be changed before setting. + /// </summary> + partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of AirsTime to be changed before returning. + /// </summary> + partial void GetAirsTime(ref DateTimeOffset? result); + + /// <summary> + /// The time the show airs, ignore the date portion + /// </summary> + public DateTimeOffset? AirsTime + { + get + { + DateTimeOffset? value = _AirsTime; + GetAirsTime(ref value); + return (_AirsTime = value); + } + set + { + DateTimeOffset? oldValue = _AirsTime; + SetAirsTime(oldValue, ref value); + if (oldValue != value) + { + _AirsTime = value; + } + } + } + + /// <summary> + /// Backing field for FirstAired + /// </summary> + protected DateTimeOffset? _FirstAired; + /// <summary> + /// When provided in a partial class, allows value of FirstAired to be changed before setting. + /// </summary> + partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of FirstAired to be changed before returning. + /// </summary> + partial void GetFirstAired(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) + { + _FirstAired = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.SeriesMetadata> SeriesMetadata { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Season> Seasons { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/SeriesMetadata.cs b/Jellyfin.Data/Entities/SeriesMetadata.cs new file mode 100644 index 0000000000..9a91371dfe --- /dev/null +++ b/Jellyfin.Data/Entities/SeriesMetadata.cs @@ -0,0 +1,239 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class SeriesMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected SeriesMetadata(): base() + { + Networks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static SeriesMetadata CreateSeriesMetadataUnsafe() + { + return new SeriesMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_series0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.SeriesMetadata.Add(this); + + this.Networks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_series0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Outline = value; + } + } + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [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) + { + _Plot = value; + } + } + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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 + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Company> Networks { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/Track.cs b/Jellyfin.Data/Entities/Track.cs new file mode 100644 index 0000000000..1d3ad372fb --- /dev/null +++ b/Jellyfin.Data/Entities/Track.cs @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class Track: global::Jellyfin.Data.Entities.LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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<global::Jellyfin.Data.Entities.Release>(); + TrackMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.TrackMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Track CreateTrackUnsafe() + { + return new Track(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_musicalbum0"></param> + 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. + + this.UrlId = urlid; + + if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.Tracks.Add(this); + + this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + this.TrackMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.TrackMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_musicalbum0"></param> + public static Track Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) + { + return new Track(urlid, dateadded, _musicalbum0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for TrackNumber + /// </summary> + protected int? _TrackNumber; + /// <summary> + /// When provided in a partial class, allows value of TrackNumber to be changed before setting. + /// </summary> + partial void SetTrackNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of TrackNumber to be changed before returning. + /// </summary> + 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) + { + _TrackNumber = value; + } + } + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.TrackMetadata> TrackMetadata { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Entities/TrackMetadata.cs b/Jellyfin.Data/Entities/TrackMetadata.cs new file mode 100644 index 0000000000..f4c61459c8 --- /dev/null +++ b/Jellyfin.Data/Entities/TrackMetadata.cs @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class TrackMetadata: global::Jellyfin.Data.Entities.Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected TrackMetadata(): base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static TrackMetadata CreateTrackMetadataUnsafe() + { + return new TrackMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_track0"></param> + 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; + + if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); + this.Language = language; + + if (_track0 == null) throw new ArgumentNullException(nameof(_track0)); + _track0.TrackMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_track0"></param> + 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); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + } +} + diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs new file mode 100644 index 0000000000..2ee3c8f4f2 --- /dev/null +++ b/Jellyfin.Data/Entities/User.cs @@ -0,0 +1,242 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Jellyfin.Data.Entities +{ + public partial class User + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected User() + { + Groups = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Group>(); + Permissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); + ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); + Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static User CreateUserUnsafe() + { + return new User(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="username"></param> + /// <param name="mustupdatepassword"></param> + /// <param name="audiolanguagepreference"></param> + /// <param name="authenticationproviderid"></param> + /// <param name="invalidloginattemptcount"></param> + /// <param name="subtitlemode"></param> + /// <param name="playdefaultaudiotrack"></param> + 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; + + if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference)); + this.AudioLanguagePreference = audiolanguagepreference; + + if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid)); + this.AuthenticationProviderId = authenticationproviderid; + + this.InvalidLoginAttemptCount = invalidloginattemptcount; + + if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode)); + this.SubtitleMode = subtitlemode; + + this.PlayDefaultAudioTrack = playdefaultaudiotrack; + + this.Groups = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Group>(); + this.Permissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); + this.ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); + this.Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="username"></param> + /// <param name="mustupdatepassword"></param> + /// <param name="audiolanguagepreference"></param> + /// <param name="authenticationproviderid"></param> + /// <param name="invalidloginattemptcount"></param> + /// <param name="subtitlemode"></param> + /// <param name="playdefaultaudiotrack"></param> + 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 + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + public Guid Id { get; protected set; } + + /// <summary> + /// Required + /// </summary> + [ConcurrencyCheck] + [Required] + public byte[] LastLoginTimestamp { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Username { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string Password { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool MustUpdatePassword { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AudioLanguagePreference { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AuthenticationProviderId { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string GroupedFolders { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public int InvalidLoginAttemptCount { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string LatestItemExcludes { get; set; } + + public int? LoginAttemptsBeforeLockout { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string MyMediaExcludes { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string OrderedViews { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string SubtitleMode { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool PlayDefaultAudioTrack { get; set; } + + /// <summary> + /// Max length = 255 + /// </summary> + [MaxLength(255)] + [StringLength(255)] + public string SubtitleLanguagePrefernce { get; set; } + + public bool? DisplayMissingEpisodes { get; set; } + + public bool? DisplayCollectionsView { get; set; } + + public bool? HidePlayedInLatest { get; set; } + + public bool? RememberAudioSelections { get; set; } + + public bool? RememberSubtitleSelections { get; set; } + + public bool? EnableNextEpisodeAutoPlay { get; set; } + + public bool? EnableUserPreferenceAccess { get; set; } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + public virtual ICollection<global::Jellyfin.Data.Entities.Group> Groups { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Permission> Permissions { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; protected set; } + + public virtual ICollection<global::Jellyfin.Data.Entities.Preference> Preferences { get; protected set; } + + } +} + diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs new file mode 100644 index 0000000000..52e33048e2 --- /dev/null +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; + +namespace Jellyfin.Data.Enums +{ + public enum ArtKind : Int32 + { + Other, + Poster, + Banner, + Thumbnail, + Logo + } +} diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs new file mode 100644 index 0000000000..34d1b20f59 --- /dev/null +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; + +namespace Jellyfin.Data.Enums +{ + public enum MediaFileKind : Int32 + { + Main, + Sidecar, + AdditionalPart, + AlternativeFormat, + AdditionalStream + } +} diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs new file mode 100644 index 0000000000..f5c8f43c51 --- /dev/null +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; + +namespace Jellyfin.Data.Enums +{ + public enum PersonRoleType : Int32 + { + Other, + Director, + Artist, + OriginalArtist, + Actor, + VoiceActor, + Producer, + Remixer, + Conductor, + Composer, + Author, + Editor + } +} diff --git a/Jellyfin.Data/Enums/Weekday.cs b/Jellyfin.Data/Enums/Weekday.cs new file mode 100644 index 0000000000..ce0c6e4ce8 --- /dev/null +++ b/Jellyfin.Data/Enums/Weekday.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// 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 +// </auto-generated> +//------------------------------------------------------------------------------ + +using System; + +namespace Jellyfin.Data.Enums +{ + public enum Weekday : Int32 + { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday + } +} diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj new file mode 100644 index 0000000000..73ea593b0b --- /dev/null +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.4" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" /> + </ItemGroup> + +</Project> diff --git a/Jellyfin.Data/Structs/.gitkeep b/Jellyfin.Data/Structs/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1c84622ac0..0294ec7f3b 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}") = "Jellyfin.Data", "Jellyfin.Data\Jellyfin.Data.csproj", "{F03299F2-469F-40EF-A655-3766F97A5702}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -176,6 +178,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 + {F03299F2-469F-40EF-A655-3766F97A5702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -183,17 +189,6 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} EndGlobalSection - GlobalSection(AutomaticVersions) = postSolution - UpdateAssemblyVersion = True - UpdateAssemblyFileVersion = True - UpdateAssemblyInfoVersion = True - AssemblyVersionSettings = None.None.None.None - AssemblyFileVersionSettings = None.None.None.None - AssemblyInfoVersionSettings = None.None.None.None - UpdatePackageVersion = False - AssemblyInfoVersionType = SettingsVersion - InheritWinAppVersionFrom = None - EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.StandardHeader = $1 From a0a5512069a56c64d4cf2556f4c04633bda2d120 Mon Sep 17 00:00:00 2001 From: BaronGreenback <jimcartlidge@yahoo.co.uk> Date: Sun, 26 Apr 2020 19:35:36 +0100 Subject: [PATCH 270/411] 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 7a550d2c4efaedc6870f0486f45477808db43c16 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:57:31 -0400 Subject: [PATCH 271/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9af89112c5..cb3955b2cc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1458,7 +1458,7 @@ namespace Emby.Server.Implementations } /// <inheritdoc /> - public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp=false) + public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp = false) { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { From d92a3552b7add0e0c2010fe380cd29e0bac7cb26 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:57:45 -0400 Subject: [PATCH 272/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index cb3955b2cc..cdb06a7701 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1419,7 +1419,7 @@ namespace Emby.Server.Implementations public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; - public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp=false) + public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false) { try { From 015732635455c7ddd0e2b5fea28180424c4d56f3 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:58:00 -0400 Subject: [PATCH 273/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 09f6cb0431..3078103316 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Controller /// <param name="cancellationToken">Token to cancel the request if needed.</param> /// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param> /// <value>The local API URL.</value> - Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp=false); + Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false); /// <summary> /// Gets the local API URL. From 23c8ecff37636c3705eb5b3cf50b238c6e55e7c1 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:58:24 -0400 Subject: [PATCH 274/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index cdb06a7701..3bf9c6ea68 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1475,7 +1475,7 @@ namespace Emby.Server.Implementations } /// <inheritdoc /> - public string GetLocalApiUrl(ReadOnlySpan<char> host, bool forceHttp=false) + public string GetLocalApiUrl(ReadOnlySpan<char> host, bool forceHttp = false) { var url = new StringBuilder(64); bool useHttps = EnableHttps && !forceHttp; From 6ac723706c37200e922d07657300441567574740 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:58:34 -0400 Subject: [PATCH 275/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 3078103316..20c80fa47c 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Controller /// <param name="hostname">The hostname.</param> /// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param> /// <returns>The local API URL.</returns> - string GetLocalApiUrl(ReadOnlySpan<char> hostname, bool forceHttp=false); + string GetLocalApiUrl(ReadOnlySpan<char> hostname, bool forceHttp = false); /// <summary> /// Gets the local API URL. From cbeeeced759de0452d75b3d6daa11bb213ff2a26 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Sun, 26 Apr 2020 14:58:43 -0400 Subject: [PATCH 276/411] Apply style change Co-Authored-By: Bond-009 <bond.009@outlook.com> --- MediaBrowser.Controller/IServerApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 20c80fa47c..04ba0fabcb 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.Controller /// <param name="address">The IP address.</param> /// <param name="forceHttp">Whether to force usage of plain HTTP protocol.</param> /// <returns>The local API URL.</returns> - string GetLocalApiUrl(IPAddress address, bool forceHttp=false); + string GetLocalApiUrl(IPAddress address, bool forceHttp = false); /// <summary> /// Open a URL in an external browser window. From cbd62e00a4f0f63e7cc725f3bce000e9ae3c6a0d Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 26 Apr 2020 15:04:57 -0400 Subject: [PATCH 277/411] Ensure transcoding path is created when it is retrieved --- .../EncodingConfigurationExtensions.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Common/Configuration/EncodingConfigurationExtensions.cs b/MediaBrowser.Common/Configuration/EncodingConfigurationExtensions.cs index ccf9658988..89740ae084 100644 --- a/MediaBrowser.Common/Configuration/EncodingConfigurationExtensions.cs +++ b/MediaBrowser.Common/Configuration/EncodingConfigurationExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using MediaBrowser.Model.Configuration; @@ -17,18 +18,25 @@ namespace MediaBrowser.Common.Configuration => configurationManager.GetConfiguration<EncodingOptions>("encoding"); /// <summary> - /// Retrieves the transcoding temp path from the encoding configuration. + /// Retrieves the transcoding temp path from the encoding configuration, falling back to a default if no path + /// is specified in configuration. If the directory does not exist, it will be created. /// </summary> - /// <param name="configurationManager">The Configuration manager.</param> + /// <param name="configurationManager">The configuration manager.</param> /// <returns>The transcoding temp path.</returns> + /// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception> + /// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception> + /// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception> public static string GetTranscodePath(this IConfigurationManager configurationManager) { + // Get the configured path and fall back to a default var transcodingTempPath = configurationManager.GetEncodingOptions().TranscodingTempPath; if (string.IsNullOrEmpty(transcodingTempPath)) { - return Path.Combine(configurationManager.CommonApplicationPaths.ProgramDataPath, "transcodes"); + transcodingTempPath = Path.Combine(configurationManager.CommonApplicationPaths.ProgramDataPath, "transcodes"); } + // Make sure the directory exists + Directory.CreateDirectory(transcodingTempPath); return transcodingTempPath; } } From 7615cdc963cdfb16313e8704c047fd4108510742 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 26 Apr 2020 15:30:37 -0400 Subject: [PATCH 278/411] Ensure metadata path is created on app startup, and also each time it is updated --- .../Configuration/ServerConfigurationManager.cs | 6 +++++- .../ServerApplicationPaths.cs | 12 +++++------- MediaBrowser.Controller/IServerApplicationPaths.cs | 7 ++++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 0ff70decae..a6eaf2d0a3 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -67,11 +67,15 @@ namespace Emby.Server.Implementations.Configuration /// <summary> /// Updates the metadata path. /// </summary> + /// <exception cref="UnauthorizedAccessException">If the directory does not exist, and the caller does not have the required permission to create it.</exception> + /// <exception cref="NotSupportedException">If there is a custom path transcoding path specified, but it is invalid.</exception> + /// <exception cref="IOException">If the directory does not exist, and it also could not be created.</exception> private void UpdateMetadataPath() { ((ServerApplicationPaths)ApplicationPaths).InternalMetadataPath = string.IsNullOrWhiteSpace(Configuration.MetadataPath) - ? Path.Combine(ApplicationPaths.ProgramDataPath, "metadata") + ? ApplicationPaths.DefaultInternalMetadataPath : Configuration.MetadataPath; + Directory.CreateDirectory(ApplicationPaths.InternalMetadataPath); } /// <summary> diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index 2f57c97a13..dfdd4200e0 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -9,8 +9,6 @@ namespace Emby.Server.Implementations /// </summary> public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths { - private string _internalMetadataPath; - /// <summary> /// Initializes a new instance of the <see cref="ServerApplicationPaths" /> class. /// </summary> @@ -27,6 +25,7 @@ namespace Emby.Server.Implementations cacheDirectoryPath, webDirectoryPath) { + InternalMetadataPath = DefaultInternalMetadataPath; } /// <summary> @@ -98,12 +97,11 @@ namespace Emby.Server.Implementations /// <value>The user configuration directory path.</value> public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users"); + /// <inheritdoc/> + public string DefaultInternalMetadataPath => Path.Combine(ProgramDataPath, "metadata"); + /// <inheritdoc /> - public string InternalMetadataPath - { - get => _internalMetadataPath ?? (_internalMetadataPath = Path.Combine(DataPath, "metadata")); - set => _internalMetadataPath = value; - } + public string InternalMetadataPath { get; set; } /// <inheritdoc /> public string VirtualInternalMetadataPath { get; } = "%MetadataPath%"; diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index 5d7c60910a..c35a22ac70 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -71,7 +71,12 @@ namespace MediaBrowser.Controller string UserConfigurationDirectoryPath { get; } /// <summary> - /// Gets the internal metadata path. + /// Gets the default internal metadata path. + /// </summary> + string DefaultInternalMetadataPath { get; } + + /// <summary> + /// Gets the internal metadata path, either a custom path or the default. /// </summary> /// <value>The internal metadata path.</value> string InternalMetadataPath { get; } From 241dea6706dc79deb80e7416a1b0a1b58e93f403 Mon Sep 17 00:00:00 2001 From: oytal <oystein.talhaug@uib.no> Date: Sun, 26 Apr 2020 18:51:14 +0000 Subject: [PATCH 279/411] =?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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index e523ae90ba..50d0d083cb 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -96,5 +96,6 @@ "TasksChannelsCategory": "Internett kanaler", "TasksApplicationCategory": "Applikasjon", "TasksLibraryCategory": "Bibliotek", - "TasksMaintenanceCategory": "Vedlikehold" + "TasksMaintenanceCategory": "Vedlikehold", + "TaskCleanCache": "Tøm buffer katalog" } From cabdec87e8e946bfa5de0dd0f97129f1a23e7d98 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 26 Apr 2020 16:55:00 -0400 Subject: [PATCH 280/411] 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 <marknr.monteiro@protonmail.com> Date: Sun, 26 Apr 2020 18:04:34 -0400 Subject: [PATCH 281/411] 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 c38e414178d69803872948a0ef19c2957a84d05e Mon Sep 17 00:00:00 2001 From: Shillos <shillos5@gmail.com> Date: Sun, 26 Apr 2020 21:56:08 +0000 Subject: [PATCH 282/411] Translated using Weblate (Greek) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/ --- .../Localization/Core/el.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 53e2f58de8..08d755fc37 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -92,5 +92,18 @@ "UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}", "ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας", "ValueSpecialEpisodeName": "Σπέσιαλ - {0}", - "VersionNumber": "Έκδοση {0}" + "VersionNumber": "Έκδοση {0}", + "TaskRefreshPeople": "Ανανέωση Ατόμων", + "TaskCleanLogsDescription": "Διαγράφει τα αρχεία καταγραφής που είναι άνω των {0} ημερών.", + "TaskCleanLogs": "Καθαρισμός Καταλόγου Καταγραφής", + "TaskRefreshLibraryDescription": "Σαρώνει την βιβλιοθήκη πολυμέσων σας για νέα αρχεία και αναζωογονεί τα μεταδεδομένα.", + "TaskRefreshLibrary": "Βιβλιοθήκη Σάρωσης Πολυμέσων", + "TaskRefreshChapterImagesDescription": "Δημιουργεί μικρογραφίες για βίντεο με κεφάλαια.", + "TaskRefreshChapterImages": "Εξαγωγή Εικόνων Κεφαλαίου", + "TaskCleanCacheDescription": "Τα διαγραμμένα αρχεία προσωρινής μνήμης που δεν χρειάζονται πλέον από το σύστημα.", + "TaskCleanCache": "Καθαρισμός Καταλόγου Προσωρινής Μνήμης", + "TasksChannelsCategory": "Κανάλια Διαδικτύου", + "TasksApplicationCategory": "Εφαρμογή", + "TasksLibraryCategory": "Βιβλιοθήκη", + "TasksMaintenanceCategory": "Συντήρηση" } From 820cd7e6449d708eb8f272167c8da30754eea9b2 Mon Sep 17 00:00:00 2001 From: Shillos <shillos5@gmail.com> Date: Sun, 26 Apr 2020 22:08:43 +0000 Subject: [PATCH 283/411] Translated using Weblate (Greek) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/ --- .../Localization/Core/el.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 08d755fc37..0753ea39d4 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -1,5 +1,5 @@ { - "Albums": "Άλμπουμ", + "Albums": "Άλμπουμς", "AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}", "Application": "Εφαρμογή", "Artists": "Καλλιτέχνες", @@ -105,5 +105,14 @@ "TasksChannelsCategory": "Κανάλια Διαδικτύου", "TasksApplicationCategory": "Εφαρμογή", "TasksLibraryCategory": "Βιβλιοθήκη", - "TasksMaintenanceCategory": "Συντήρηση" + "TasksMaintenanceCategory": "Συντήρηση", + "TaskDownloadMissingSubtitlesDescription": "Αναζητήσεις στο διαδίκτυο όπου λείπουν υπότιτλους με βάση τη διαμόρφωση μεταδεδομένων.", + "TaskDownloadMissingSubtitles": "Λήψη υπότιτλων που λείπουν", + "TaskRefreshChannelsDescription": "Ανανεώνει τις πληροφορίες καναλιού στο διαδικτύου.", + "TaskRefreshChannels": "Ανανέωση Καναλιών", + "TaskCleanTranscodeDescription": "Διαγράφει αρχείου διακωδικοποιητή περισσότερο από μία ημέρα.", + "TaskCleanTranscode": "Καθαρισμός Kαταλόγου Διακωδικοποιητή", + "TaskUpdatePluginsDescription": "Κατεβάζει και εγκαθιστά ενημερώσεις για τις προσθήκες που έχουν ρυθμιστεί για αυτόματη ενημέρωση.", + "TaskUpdatePlugins": "Ενημέρωση Προσθηκών", + "TaskRefreshPeopleDescription": "Ενημερώνει μεταδεδομένα για ηθοποιούς και σκηνοθέτες στην βιβλιοθήκη των πολυμέσων σας." } From cee587d6e358cbb16b8cd27b55f492c145476c5a Mon Sep 17 00:00:00 2001 From: Max Git <rotvel@gmail.com> Date: Mon, 27 Apr 2020 03:25:57 +0200 Subject: [PATCH 284/411] Try harder to find ffmpeg in app directory. While here do some cleanup --- .../Encoder/MediaEncoder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 992ad146d8..1377502dd9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -113,7 +113,7 @@ namespace MediaBrowser.MediaEncoding.Encoder SetAvailableEncoders(validator.GetEncoders()); } - _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation, _ffmpegPath ?? string.Empty); + _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty); } /// <summary> @@ -126,7 +126,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { string newPath; - _logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty); + _logger.LogInformation("Attempting to update encoder path to {Path}. pathType: {PathType}", path ?? string.Empty, pathType ?? string.Empty); if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase)) { @@ -180,7 +180,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (!rc) { - _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location, path); + _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path); } // ToDo - Enable the ffmpeg validator. At the moment any version can be used. @@ -191,18 +191,18 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location, path); + _logger.LogWarning("FFmpeg: {Location}: File not found: {Path}", location, path); } } return rc; } - private string GetEncoderPathFromDirectory(string path, string filename) + private string GetEncoderPathFromDirectory(string path, string filename, bool recursive = false) { try { - var files = _fileSystem.GetFilePaths(path); + var files = _fileSystem.GetFilePaths(path, recursive); var excludeExtensions = new[] { ".c" }; @@ -223,7 +223,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <returns></returns> private string ExistsOnSystemPath(string fileName) { - string inJellyfinPath = GetEncoderPathFromDirectory(System.AppContext.BaseDirectory, fileName); + var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true); if (!string.IsNullOrEmpty(inJellyfinPath)) { return inJellyfinPath; @@ -892,7 +892,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName); } - _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", path); + _logger.LogWarning("Could not determine vob file list for {Path} using DvdLib. Will scan using file sizes.", path); } var files = allVobs From e3a42a8fe93727d43d02a9b560a53661f12230b8 Mon Sep 17 00:00:00 2001 From: sparky8251 <sparky@possumlodge.me> Date: Mon, 27 Apr 2020 08:42:46 -0400 Subject: [PATCH 285/411] Address reviews --- .../ApplicationHost.cs | 2 +- .../Routines/DisableMetricsCollection.cs | 33 ------------------- Jellyfin.Server/Startup.cs | 3 +- 3 files changed, 3 insertions(+), 35 deletions(-) delete mode 100644 Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7e7b785d85..b9aaaa2065 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -263,7 +263,7 @@ namespace Emby.Server.Implementations // Initialize runtime stat collection if (ServerConfigurationManager.Configuration.EnableMetrics) { - IDisposable collector = DotNetRuntimeStatsBuilder.Default().StartCollecting(); + DotNetRuntimeStatsBuilder.Default().StartCollecting(); } fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); diff --git a/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs b/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs deleted file mode 100644 index b5dc43614e..0000000000 --- a/Jellyfin.Server/Migrations/Routines/DisableMetricsCollection.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server.Migrations.Routines -{ - /// <summary> - /// Disable metrics collections for all installations since it can be a security risk if not properly secured. - /// </summary> - internal class DisableMetricsCollection : IMigrationRoutine - { - /// <inheritdoc/> - public Guid Id => Guid.Parse("{4124C2CD-E939-4FFB-9BE9-9B311C413638}"); - - /// <inheritdoc/> - public string Name => "DisableMetricsCollection"; - - /// <inheritdoc/> - public void Perform(CoreAppHost host, ILogger logger) - { - // Set EnableMetrics to false since it can leak sensitive information if not properly secured - var metrics = host.ServerConfigurationManager.Configuration.EnableMetrics; - if (metrics) - { - logger.LogInformation("Disabling metrics collection during migration"); - metrics = false; - - host.ServerConfigurationManager.SaveConfiguration("false", metrics); - } - } - } -} diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 2cc7cff87e..8bcfd13504 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -72,7 +72,8 @@ namespace Jellyfin.Server app.UseAuthorization(); if (_serverConfigurationManager.Configuration.EnableMetrics) { - app.UseHttpMetrics(); // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + // Must be registered after any middleware that could chagne HTTP response codes or the data will be bad + app.UseHttpMetrics(); } app.UseEndpoints(endpoints => From 655208d375bce4a5c82bf8da87d5d72814a8da83 Mon Sep 17 00:00:00 2001 From: Vasily <just.one.man@yandex.ru> Date: Mon, 27 Apr 2020 19:03:42 +0300 Subject: [PATCH 286/411] Now parse date in header correctly as being in UTC --- Emby.Server.Implementations/HttpServer/HttpResultFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 464ca3a0b5..2e9ecc4ae6 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -426,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer if (!noCache) { - if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal, out var ifModifiedSinceHeader)) + if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ifModifiedSinceHeader)) { _logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]); return null; From ab8a5595f609ab54e017ecabadc841d181acd4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Nowak?= <qkash@o2.pl> Date: Mon, 27 Apr 2020 17:14:00 +0000 Subject: [PATCH 287/411] Translated using Weblate (Polish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/ --- .../Localization/Core/pl.json | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index e9d9bbf2e1..bdc0d0169b 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -92,5 +92,27 @@ "UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów", "ValueSpecialEpisodeName": "Specjalne - {0}", - "VersionNumber": "Wersja {0}" + "VersionNumber": "Wersja {0}", + "TaskDownloadMissingSubtitlesDescription": "Przeszukuje internet w poszukiwaniu brakujących napisów w oparciu o konfigurację metadanych.", + "TaskDownloadMissingSubtitles": "Pobierz brakujące napisy", + "TaskRefreshChannelsDescription": "Odświeża informacje o kanałach internetowych.", + "TaskRefreshChannels": "Odśwież kanały", + "TaskCleanTranscodeDescription": "Usuwa transkodowane pliki starsze niż 1 dzień.", + "TaskCleanTranscode": "Wyczyść folder transkodowania", + "TaskUpdatePluginsDescription": "Pobiera i instaluje aktualizacje dla pluginów które są skonfigurowane do automatycznej aktualizacji.", + "TaskUpdatePlugins": "Aktualizuj pluginy", + "TaskRefreshPeopleDescription": "Odświeża metadane o aktorów i reżyserów w Twojej bibliotece mediów.", + "TaskRefreshPeople": "Odśwież obsadę", + "TaskCleanLogsDescription": "Kasuje pliki logów starsze niż {0} dni.", + "TaskCleanLogs": "Wyczyść folder logów", + "TaskRefreshLibraryDescription": "Skanuje Twoją bibliotekę mediów dla nowych plików i odświeżenia metadanych.", + "TaskRefreshLibrary": "Skanuj bibliotekę mediów", + "TaskRefreshChapterImagesDescription": "Tworzy miniatury dla filmów posiadających rozdziały.", + "TaskRefreshChapterImages": "Wydobądź grafiki rozdziałów", + "TaskCleanCacheDescription": "Usuwa niepotrzebne i przestarzałe pliki cache.", + "TaskCleanCache": "Wyczyść folder Cache", + "TasksChannelsCategory": "Kanały internetowe", + "TasksApplicationCategory": "Aplikacja", + "TasksLibraryCategory": "Biblioteka", + "TasksMaintenanceCategory": "Konserwacja" } From 8c9604afba027baed71ad5f0775679228869f079 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" <joshua@boniface.me> Date: Wed, 29 Apr 2020 16:06:42 -0400 Subject: [PATCH 288/411] Add Web integration option in default service conf --- debian/conf/jellyfin | 5 ++++- debian/jellyfin.service | 2 +- fedora/jellyfin.env | 3 +++ fedora/jellyfin.service | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/debian/conf/jellyfin b/debian/conf/jellyfin index c6e595f15a..64c98520cb 100644 --- a/debian/conf/jellyfin +++ b/debian/conf/jellyfin @@ -18,6 +18,9 @@ JELLYFIN_CONFIG_DIR="/etc/jellyfin" JELLYFIN_LOG_DIR="/var/log/jellyfin" JELLYFIN_CACHE_DIR="/var/cache/jellyfin" +# web client path, installed by the jellyfin-web package +JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin/web" + # Restart script for in-app server control JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh" @@ -37,4 +40,4 @@ JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg" # Application username JELLYFIN_USER="jellyfin" # Full application command -JELLYFIN_ARGS="$JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" +JELLYFIN_ARGS="$JELLYFIN_WEB_OPT $JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLFIN_NOWEBAPP_OPT" diff --git a/debian/jellyfin.service b/debian/jellyfin.service index 1305e238b0..f1a8f4652c 100644 --- a/debian/jellyfin.service +++ b/debian/jellyfin.service @@ -6,7 +6,7 @@ After = network.target Type = simple EnvironmentFile = /etc/default/jellyfin User = jellyfin -ExecStart = /usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +ExecStart = /usr/bin/jellyfin ${JELLYFIN_WEB_OPT} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} Restart = on-failure TimeoutSec = 15 diff --git a/fedora/jellyfin.env b/fedora/jellyfin.env index de48f13af5..bf64acd3f9 100644 --- a/fedora/jellyfin.env +++ b/fedora/jellyfin.env @@ -20,6 +20,9 @@ JELLYFIN_CONFIG_DIR="/etc/jellyfin" JELLYFIN_LOG_DIR="/var/log/jellyfin" JELLYFIN_CACHE_DIR="/var/cache/jellyfin" +# web client path, installed by the jellyfin-web package +JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin-web" + # In-App service control JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh" diff --git a/fedora/jellyfin.service b/fedora/jellyfin.service index f3dc594b1c..b092ebf2f0 100644 --- a/fedora/jellyfin.service +++ b/fedora/jellyfin.service @@ -5,7 +5,7 @@ Description=Jellyfin is a free software media system that puts you in control of [Service] EnvironmentFile=/etc/sysconfig/jellyfin WorkingDirectory=/var/lib/jellyfin -ExecStart=/usr/bin/jellyfin ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} +ExecStart=/usr/bin/jellyfin ${JELLYFIN_WEB_OPT} ${JELLYFIN_RESTART_OPT} ${JELLYFIN_FFMPEG_OPT} ${JELLYFIN_SERVICE_OPT} ${JELLYFIN_NOWEBAPP_OPT} TimeoutSec=15 Restart=on-failure User=jellyfin From a370c5c007850196a41c2d6550498310ab73a999 Mon Sep 17 00:00:00 2001 From: Erwin de Haan <EraYaN@users.noreply.github.com> Date: Thu, 30 Apr 2020 10:03:49 +0200 Subject: [PATCH 289/411] Restore the versioning extension settings. --- MediaBrowser.sln | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 0294ec7f3b..aaf672ec00 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -189,6 +189,17 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} EndGlobalSection + GlobalSection(AutomaticVersions) = postSolution + UpdateAssemblyVersion = True + UpdateAssemblyFileVersion = True + UpdateAssemblyInfoVersion = True + AssemblyVersionSettings = None.None.None.None + AssemblyFileVersionSettings = None.None.None.None + AssemblyInfoVersionSettings = None.None.None.None + UpdatePackageVersion = False + AssemblyInfoVersionType = SettingsVersion + InheritWinAppVersionFrom = None + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.StandardHeader = $1 From bbd74f811f053fc849e459fc12f1397de8732024 Mon Sep 17 00:00:00 2001 From: Erwin de Haan <EraYaN@users.noreply.github.com> Date: Thu, 30 Apr 2020 10:06:02 +0200 Subject: [PATCH 290/411] Spaces -> Tab in solution file --- MediaBrowser.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index aaf672ec00..a1dbe80476 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -189,7 +189,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} EndGlobalSection - GlobalSection(AutomaticVersions) = postSolution + GlobalSection(AutomaticVersions) = postSolution UpdateAssemblyVersion = True UpdateAssemblyFileVersion = True UpdateAssemblyInfoVersion = True From c342c6b582e96ecfec0c762d451acb91dba6cd32 Mon Sep 17 00:00:00 2001 From: fesken <fesken@gmail.com> Date: Thu, 30 Apr 2020 18:57:10 +0000 Subject: [PATCH 291/411] Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index b7c50394ae..c8662b2cab 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -9,7 +9,7 @@ "Channels": "Kanaler", "ChapterNameValue": "Kapitel {0}", "Collections": "Samlingar", - "DeviceOfflineWithName": "{0} har tappat anslutningen", + "DeviceOfflineWithName": "{0} har kopplat från", "DeviceOnlineWithName": "{0} är ansluten", "FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}", "Favorites": "Favoriter", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", "NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades", "NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp", - "NotificationOptionInstallationFailed": "Fel vid installation", + "NotificationOptionInstallationFailed": "Installationen misslyckades", "NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till", "NotificationOptionPluginError": "Fel uppstod med tillägget", "NotificationOptionPluginInstalled": "Tillägg har installerats", @@ -113,5 +113,6 @@ "TasksChannelsCategory": "Internetkanaler", "TasksApplicationCategory": "Applikation", "TasksLibraryCategory": "Bibliotek", - "TasksMaintenanceCategory": "Underhåll" + "TasksMaintenanceCategory": "Underhåll", + "TaskRefreshPeople": "Uppdatera Personer" } From 8b6bec60d3693fadbe958c83fee5abab4a2ec0e1 Mon Sep 17 00:00:00 2001 From: Heikki Jetsonen <heikki.jetsonen@gmail.com> Date: Thu, 30 Apr 2020 23:34:43 +0000 Subject: [PATCH 292/411] Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- .../Localization/Core/fi.json | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index b39adefe70..4ed7b301a8 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "Suorat lähetykset", + "HeaderLiveTV": "Live-TV", "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.", "NameSeasonUnknown": "Tuntematon Kausi", "NameSeasonNumber": "Kausi {0}", @@ -67,21 +67,21 @@ "UserDownloadingItemWithValues": "{0} lataa {1}", "UserDeletedWithName": "Käyttäjä {0} poistettu", "UserCreatedWithName": "Käyttäjä {0} luotu", - "TvShows": "TV-Ohjelmat", + "TvShows": "TV-sarjat", "Sync": "Synkronoi", - "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}", + "SubtitleDownloadFailureFromForItem": "Tekstitysten lataus ({0} -> {1}) epäonnistui //this string would have to be generated for each provider and movie because of finnish cases, sorry", "StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.", "Songs": "Kappaleet", - "Shows": "Ohjelmat", - "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen", + "Shows": "Sarjat", + "ServerNameNeedsToBeRestarted": "{0} täytyy käynnistää uudelleen", "ProviderValue": "Tarjoaja: {0}", "Plugin": "Liitännäinen", "NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty", - "NotificationOptionVideoPlayback": "Videon toisto aloitettu", - "NotificationOptionUserLockedOut": "Käyttäjä lukittu", + "NotificationOptionVideoPlayback": "Videota toistetaan", + "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos", "NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui", - "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan", - "NotificationOptionPluginUpdateInstalled": "Lisäosan päivitys asennettu", + "NotificationOptionServerRestartRequired": "Palvelin pitää käynnistää uudelleen", + "NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty", "NotificationOptionPluginUninstalled": "Liitännäinen poistettu", "NotificationOptionPluginInstalled": "Liitännäinen asennettu", "NotificationOptionPluginError": "Ongelma liitännäisessä", @@ -90,8 +90,8 @@ "NotificationOptionCameraImageUploaded": "Kameran kuva ladattu", "NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu", "NotificationOptionAudioPlayback": "Toistetaan ääntä", - "NotificationOptionApplicationUpdateInstalled": "Uusi sovellusversio asennettu", - "NotificationOptionApplicationUpdateAvailable": "Sovelluksesta on uusi versio saatavilla", + "NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettu", + "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla", "TasksMaintenanceCategory": "Ylläpito", "TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.", "TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset", From 9265b422f70cdb1e87a165623fcde66ce6681368 Mon Sep 17 00:00:00 2001 From: Aragon <michael2210x@gmail.com> Date: Fri, 1 May 2020 09:24:55 +0000 Subject: [PATCH 293/411] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 2662913621..8abe31d2a0 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -62,7 +62,7 @@ "NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionVideoPlaybackStopped": "Video playback stopped", "Photos": "תמונות", - "Playlists": "רשימות ניגון", + "Playlists": "רשימות הפעלה", "Plugin": "Plugin", "PluginInstalledWithName": "{0} was installed", "PluginUninstalledWithName": "{0} was uninstalled", From 7659a2ab326770d7c81501c88966b4443ad2d39e Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 1 May 2020 16:48:33 +0200 Subject: [PATCH 294/411] 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<DirectPlayProfile>(); + TranscodingProfiles = Array.Empty<TranscodingProfile>(); + ResponseProfiles = Array.Empty<ResponseProfile>(); + CodecProfiles = Array.Empty<CodecProfile>(); + ContainerProfiles = Array.Empty<ContainerProfile>(); SubtitleProfiles = Array.Empty<SubtitleProfile>(); - XmlRootAttributes = new XmlAttribute[] { }; + XmlRootAttributes = Array.Empty<XmlAttribute>(); 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 62e251663fce8216cea529f85382299ac2f39fbc Mon Sep 17 00:00:00 2001 From: Heikki Jetsonen <heikki.jetsonen@gmail.com> Date: Fri, 1 May 2020 14:43:54 +0000 Subject: [PATCH 295/411] Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- Emby.Server.Implementations/Localization/Core/fi.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index 4ed7b301a8..f8d6e0e09b 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -12,7 +12,7 @@ "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty", "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}", "MessageApplicationUpdated": "Jellyfin palvelin on päivitetty", - "Latest": "Viimeisin", + "Latest": "Uusimmat", "LabelRunningTimeValue": "Toiston kesto: {0}", "LabelIpAddressValue": "IP-osoite: {0}", "ItemRemovedWithName": "{0} poistettiin kirjastosta", @@ -41,7 +41,7 @@ "CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}", "Books": "Kirjat", "AuthenticationSucceededWithUserName": "{0} todennus onnistui", - "Artists": "Esiintyjät", + "Artists": "Artistit", "Application": "Sovellus", "AppDeviceValues": "Sovellus: {0}, Laite: {1}", "Albums": "Albumit", From 04f826e50c23a8a996fd317124260f67b7ff3f9a Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sat, 2 May 2020 01:09:35 +0200 Subject: [PATCH 296/411] 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<Task> next) - => HttpServer.RequestHandler(context); + => _httpServer.RequestHandler(context); /// <summary> /// Registers services/resources with the service collection that will be available via DI. @@ -597,7 +597,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<ISearchEngine, SearchEngine>(); serviceCollection.AddSingleton<ServiceController>(); - serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>(); serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>(); serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>(); 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<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>(); - private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>(); private readonly IHostEnvironment _hostEnvironment; private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>(); @@ -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 - { - /// <summary> - /// Gets or sets the error handler. - /// </summary> - /// <value>The error handler.</value> - Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; } - - /// <summary> - /// Gets or sets the request handler. - /// </summary> - /// <value>The request handler.</value> - Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; } - - /// <summary> - /// Gets or sets the web socket handler. - /// </summary> - /// <value>The web socket handler.</value> - Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; } - - /// <summary> - /// Stops this instance. - /// </summary> - 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 - { - /// <summary> - /// Gets or sets the URL. - /// </summary> - /// <value>The URL.</value> - public string Url { get; set; } - /// <summary> - /// Gets or sets the query string. - /// </summary> - /// <value>The query string.</value> - public IQueryCollection QueryString { get; set; } - /// <summary> - /// Gets or sets the web socket. - /// </summary> - /// <value>The web socket.</value> - public IWebSocket WebSocket { get; set; } - /// <summary> - /// Gets or sets the endpoint. - /// </summary> - /// <value>The endpoint.</value> - 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<WebSocketSharpListener> logger) - { - _logger = logger; - _disposeCancellationToken = _disposeCancellationTokenSource.Token; - } - - public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; } - - public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; } - - public Action<WebSocketConnectEventArgs> 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<byte>(); - - 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; - } - - /// <summary> - /// Releases the unmanaged resources and disposes of the managed resources used. - /// </summary> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private bool _disposed; - - /// <summary> - /// Releases the unmanaged resources and disposes of the managed resources used. - /// </summary> - /// <param name="disposing">Whether or not the managed resources should be disposed.</param> - 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<WebSocketManager> _logger; - private const int BufferSize = 4096; - - public WebSocketManager(IWebSocketHandler[] webSocketHandlers, IJsonSerializer jsonSerializer, ILogger<WebSocketManager> logger) - { - _webSocketHandlers = webSocketHandlers; - _jsonSerializer = jsonSerializer; - _logger = logger; - } - - public async Task OnWebSocketConnected(WebSocket webSocket) - { - var taskCompletionSource = new TaskCompletionSource<bool>(); - var cancellationToken = new CancellationTokenSource().Token; - WebSocketReceiveResult result; - var message = new List<byte>(); - - // 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<bool> 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<WebSocketMessage<object>>(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 <bond.009@outlook.com> Date: Sat, 2 May 2020 01:30:04 +0200 Subject: [PATCH 297/411] 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 { /// <summary> /// The key for a setting that specifies the default redirect path @@ -699,28 +698,6 @@ namespace Emby.Server.Implementations.HttpServer return _baseUrlPrefix + NormalizeUrlPath(path); } - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - // TODO: - } - - _disposed = true; - } - /// <summary> /// Processes the web socket message received. /// </summary> 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 /// </summary> private readonly WebSocket _socket; - private bool _disposed = false; - /// <summary> /// Initializes a new instance of the <see cref="WebSocketConnection" /> class. /// </summary> @@ -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<IWebSocketConnection> _sockets; + private readonly List<IWebSocketConnection> _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 { /// <summary> - /// Interface IHttpServer + /// Interface IHttpServer. /// </summary> - public interface IHttpServer : IDisposable + public interface IHttpServer { /// <summary> /// Gets the URL prefix. From 472efeeec4ddf5dbea1550aeea2173590b24953e Mon Sep 17 00:00:00 2001 From: Davide Polonio <poloniodavide@gmail.com> Date: Sat, 2 May 2020 13:09:57 +0200 Subject: [PATCH 298/411] Remove extra line in UserManager Co-authored-by: Bond-009 <bond.009@outlook.com> --- 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 <marknr.monteiro@protonmail.com> Date: Sat, 2 May 2020 15:44:24 -0400 Subject: [PATCH 299/411] 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; } + + /// <inheritdoc/> + /// <remarks> + /// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and + /// <see cref="BaseSqliteRepository.WriteConnection"/> are managed by <see cref="SqliteItemRepository"/>. + /// See <see cref="Initialize(IUserManager, SemaphoreSlim, SQLiteDatabaseConnection)"/>. + /// </remarks> + protected override void Dispose(bool dispose) + { + } } } From df65e3ab0db8fd55a6a02b8c067565abc926136f Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Sat, 2 May 2020 15:29:05 -0500 Subject: [PATCH 300/411] Add Access-Control-Allow-Origin header to exceptions Fixes #1794 --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 211a0c1d99..77878eacc9 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -542,6 +542,11 @@ namespace Emby.Server.Implementations.HttpServer var requestInnerEx = GetActualException(requestEx); var statusCode = GetStatusCode(requestInnerEx); + if (!httpRes.Headers.ContainsKey("Access-Control-Allow-Origin")) + { + httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); + } + // Do not handle 500 server exceptions manually when in development mode // The framework-defined development exception page will be returned instead if (statusCode == 500 && _hostEnvironment.IsDevelopment()) From 2b41f8ab632b706b0f4e2d17032dbb07ce73136e Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Sat, 2 May 2020 17:56:05 -0400 Subject: [PATCH 301/411] Clean up generated code --- Jellyfin.Data/Entities/Artwork.cs | 366 +++++----- Jellyfin.Data/Entities/Book.cs | 109 ++- Jellyfin.Data/Entities/BookMetadata.cs | 199 +++--- Jellyfin.Data/Entities/Chapter.cs | 453 ++++++------ Jellyfin.Data/Entities/Collection.cs | 201 +++--- Jellyfin.Data/Entities/CollectionItem.cs | 270 ++++--- Jellyfin.Data/Entities/Company.cs | 259 ++++--- Jellyfin.Data/Entities/CompanyMetadata.cs | 411 ++++++----- Jellyfin.Data/Entities/CustomItem.cs | 109 ++- Jellyfin.Data/Entities/CustomItemMetadata.cs | 113 ++- Jellyfin.Data/Entities/Episode.cs | 209 +++--- Jellyfin.Data/Entities/EpisodeMetadata.cs | 341 +++++---- Jellyfin.Data/Entities/Genre.cs | 281 ++++---- Jellyfin.Data/Entities/Group.cs | 203 +++--- Jellyfin.Data/Entities/Library.cs | 271 ++++---- Jellyfin.Data/Entities/LibraryItem.cs | 318 +++++---- Jellyfin.Data/Entities/LibraryRoot.cs | 362 +++++----- Jellyfin.Data/Entities/MediaFile.cs | 368 +++++----- Jellyfin.Data/Entities/MediaFileStream.cs | 275 ++++---- Jellyfin.Data/Entities/Metadata.cs | 696 +++++++++---------- Jellyfin.Data/Entities/MetadataProvider.cs | 271 ++++---- Jellyfin.Data/Entities/MetadataProviderId.cs | 340 +++++---- Jellyfin.Data/Entities/Movie.cs | 109 ++- Jellyfin.Data/Entities/MovieMetadata.cs | 421 ++++++----- Jellyfin.Data/Entities/MusicAlbum.cs | 110 ++- Jellyfin.Data/Entities/MusicAlbumMetadata.cs | 350 +++++----- Jellyfin.Data/Entities/Permission.cs | 270 ++++--- Jellyfin.Data/Entities/PermissionKind.cs | 40 -- Jellyfin.Data/Entities/Person.cs | 519 +++++++------- Jellyfin.Data/Entities/PersonRole.cs | 379 +++++----- Jellyfin.Data/Entities/Photo.cs | 110 ++- Jellyfin.Data/Entities/PhotoMetadata.cs | 113 ++- Jellyfin.Data/Entities/Preference.cs | 204 +++--- Jellyfin.Data/Entities/PreferenceKind.cs | 27 - Jellyfin.Data/Entities/ProviderMapping.cs | 236 ++++--- Jellyfin.Data/Entities/Rating.cs | 352 +++++----- Jellyfin.Data/Entities/RatingSource.cs | 437 ++++++------ Jellyfin.Data/Entities/Release.cs | 356 +++++----- Jellyfin.Data/Entities/Season.cs | 208 +++--- Jellyfin.Data/Entities/SeasonMetadata.cs | 201 +++--- Jellyfin.Data/Entities/Series.cs | 312 ++++----- Jellyfin.Data/Entities/SeriesMetadata.cs | 421 ++++++----- Jellyfin.Data/Entities/Track.cs | 207 +++--- Jellyfin.Data/Entities/TrackMetadata.cs | 113 ++- Jellyfin.Data/Entities/User.cs | 456 ++++++------ 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, 6093 insertions(+), 6456 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Artwork() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Artwork CreateArtworkUnsafe() - { - return new Artwork(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="path"></param> - /// <param name="kind"></param> - /// <param name="_metadata0"></param> - /// <param name="_personrole1"></param> - 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; - - this.Kind = kind; - - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Artwork.Add(this); - - if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1)); - _personrole1.Artwork = this; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="path"></param> - /// <param name="kind"></param> - /// <param name="_metadata0"></param> - /// <param name="_personrole1"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Artwork")] + public partial class Artwork + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Artwork() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Artwork CreateArtworkUnsafe() + { + return new Artwork(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path"></param> + /// <param name="kind"></param> + /// <param name="_metadata0"></param> + /// <param name="_personrole1"></param> + 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; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Artwork.Add(this); + + if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1)); + _personrole1.Artwork = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path"></param> + /// <param name="kind"></param> + /// <param name="_metadata0"></param> + /// <param name="_personrole1"></param> + public static Artwork Create(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1) + { + return new Artwork(path, kind, _metadata0, _personrole1); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get + { + string value = _Path; + GetPath(ref value); + return (_Path = value); + } + set { - _Id = value; + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } } - } - } - - /// <summary> - /// Backing field for Path - /// </summary> - protected string _Path; - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before setting. - /// </summary> - partial void SetPath(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before returning. - /// </summary> - partial void GetPath(ref string result); - - /// <summary> - /// Required, Max length = 65535 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Kind + /// </summary> + internal Enums.ArtKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(Enums.ArtKind oldValue, ref Enums.ArtKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref Enums.ArtKind result); + + /// <summary> + /// Indexed, Required + /// </summary> + [Required] + public Enums.ArtKind Kind + { + get { - _Path = value; + Enums.ArtKind value = _Kind; + GetKind(ref value); + return (_Kind = value); } - } - } - - /// <summary> - /// Backing field for Kind - /// </summary> - internal global::Jellyfin.Data.Enums.ArtKind _Kind; - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// </summary> - partial void SetKind(global::Jellyfin.Data.Enums.ArtKind oldValue, ref global::Jellyfin.Data.Enums.ArtKind newValue); - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// </summary> - partial void GetKind(ref global::Jellyfin.Data.Enums.ArtKind result); - - /// <summary> - /// Indexed, Required - /// </summary> - [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) + set { - _Kind = value; + Enums.ArtKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + } } - } - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Book(): base() - { - BookMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.BookMetadata>(); - Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Book() : base() + { + BookMetadata = new HashSet<BookMetadata>(); + Releases = new HashSet<Release>(); - Init(); - } + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Book CreateBookUnsafe() - { - return new Book(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Book CreateBookUnsafe() + { + return new Book(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public Book(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Book(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.BookMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.BookMetadata>(); - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + this.BookMetadata = new HashSet<BookMetadata>(); + this.Releases = new HashSet<Release>(); - Init(); - } + Init(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static Book Create(Guid urlid, DateTime dateadded) - { - return new Book(urlid, dateadded); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Book Create(Guid urlid, DateTime dateadded) + { + return new Book(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.BookMetadata> BookMetadata { get; protected set; } + [ForeignKey("BookMetadata_BookMetadata_Id")] + public virtual ICollection<BookMetadata> BookMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected BookMetadata(): base() - { - Publishers = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static BookMetadata CreateBookMetadataUnsafe() - { - return new BookMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_book0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_book0 == null) throw new ArgumentNullException(nameof(_book0)); - _book0.BookMetadata.Add(this); - - this.Publishers = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_book0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for ISBN - /// </summary> - protected long? _ISBN; - /// <summary> - /// When provided in a partial class, allows value of ISBN to be changed before setting. - /// </summary> - partial void SetISBN(long? oldValue, ref long? newValue); - /// <summary> - /// When provided in a partial class, allows value of ISBN to be changed before returning. - /// </summary> - 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 partial class BookMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected BookMetadata() : base() + { + Publishers = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static BookMetadata CreateBookMetadataUnsafe() + { + return new BookMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_book0"></param> + 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 (_book0 == null) throw new ArgumentNullException(nameof(_book0)); + _book0.BookMetadata.Add(this); + + this.Publishers = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_book0"></param> + public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0) + { + return new BookMetadata(title, language, dateadded, datemodified, _book0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for ISBN + /// </summary> + protected long? _ISBN; + /// <summary> + /// When provided in a partial class, allows value of ISBN to be changed before setting. + /// </summary> + partial void SetISBN(long? oldValue, ref long? newValue); + /// <summary> + /// When provided in a partial class, allows value of ISBN to be changed before returning. + /// </summary> + partial void GetISBN(ref long? result); + + public long? ISBN + { + get + { + long? value = _ISBN; + GetISBN(ref value); + return (_ISBN = value); + } + set { - _ISBN = value; + long? oldValue = _ISBN; + SetISBN(oldValue, ref value); + if (oldValue != value) + { + _ISBN = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.Company> Publishers { get; protected set; } + [ForeignKey("Company_Publishers_Id")] + public virtual ICollection<Company> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Chapter() - { - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Chapter() + { + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Chapter CreateChapterUnsafe() - { - return new Chapter(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Chapter CreateChapterUnsafe() + { + return new Chapter(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="timestart"></param> - /// <param name="_release0"></param> - public Chapter(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) - { - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="timestart"></param> + /// <param name="_release0"></param> + 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(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="timestart"></param> - /// <param name="_release0"></param> - public static Chapter Create(string language, long timestart, global::Jellyfin.Data.Entities.Release _release0) - { - return new Chapter(language, timestart, _release0); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="timestart"></param> + /// <param name="_release0"></param> + public static Chapter Create(string language, long timestart, Release _release0) + { + return new Chapter(language, timestart, _release0); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _Name = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } - /// <summary> - /// Backing field for Language - /// </summary> - protected string _Language; - /// <summary> - /// When provided in a partial class, allows value of Language to be changed before setting. - /// </summary> - partial void SetLanguage(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Language to be changed before returning. - /// </summary> - partial void GetLanguage(ref string result); + /// <summary> + /// Backing field for Language + /// </summary> + protected string _Language; + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before setting. + /// </summary> + partial void SetLanguage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before returning. + /// </summary> + partial void GetLanguage(ref string result); - /// <summary> - /// Required, Min length = 3, Max length = 3 - /// ISO-639-3 3-character language codes - /// </summary> - [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) + /// <summary> + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// </summary> + [Required] + [MinLength(3)] + [MaxLength(3)] + [StringLength(3)] + public string Language + { + get + { + string value = _Language; + GetLanguage(ref value); + return (_Language = value); + } + set { - _Language = value; + string oldValue = _Language; + SetLanguage(oldValue, ref value); + if (oldValue != value) + { + _Language = value; + } } - } - } + } - /// <summary> - /// Backing field for TimeStart - /// </summary> - protected long _TimeStart; - /// <summary> - /// When provided in a partial class, allows value of TimeStart to be changed before setting. - /// </summary> - partial void SetTimeStart(long oldValue, ref long newValue); - /// <summary> - /// When provided in a partial class, allows value of TimeStart to be changed before returning. - /// </summary> - partial void GetTimeStart(ref long result); + /// <summary> + /// Backing field for TimeStart + /// </summary> + protected long _TimeStart; + /// <summary> + /// When provided in a partial class, allows value of TimeStart to be changed before setting. + /// </summary> + partial void SetTimeStart(long oldValue, ref long newValue); + /// <summary> + /// When provided in a partial class, allows value of TimeStart to be changed before returning. + /// </summary> + partial void GetTimeStart(ref long result); - /// <summary> - /// Required - /// </summary> - [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) + /// <summary> + /// Required + /// </summary> + [Required] + public long TimeStart + { + get + { + long value = _TimeStart; + GetTimeStart(ref value); + return (_TimeStart = value); + } + set { - _TimeStart = value; + long oldValue = _TimeStart; + SetTimeStart(oldValue, ref value); + if (oldValue != value) + { + _TimeStart = value; + } } - } - } + } - /// <summary> - /// Backing field for TimeEnd - /// </summary> - protected long? _TimeEnd; - /// <summary> - /// When provided in a partial class, allows value of TimeEnd to be changed before setting. - /// </summary> - partial void SetTimeEnd(long? oldValue, ref long? newValue); - /// <summary> - /// When provided in a partial class, allows value of TimeEnd to be changed before returning. - /// </summary> - partial void GetTimeEnd(ref long? result); + /// <summary> + /// Backing field for TimeEnd + /// </summary> + protected long? _TimeEnd; + /// <summary> + /// When provided in a partial class, allows value of TimeEnd to be changed before setting. + /// </summary> + partial void SetTimeEnd(long? oldValue, ref long? newValue); + /// <summary> + /// When provided in a partial class, allows value of TimeEnd to be changed before returning. + /// </summary> + 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) + public long? TimeEnd + { + get { - _TimeEnd = value; + long? value = _TimeEnd; + GetTimeEnd(ref value); + return (_TimeEnd = value); } - } - } + set + { + long? oldValue = _TimeEnd; + SetTimeEnd(oldValue, ref value); + if (oldValue != value) + { + _TimeEnd = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor - /// </summary> - public Collection() - { - CollectionItem = new System.Collections.Generic.LinkedList<global::Jellyfin.Data.Entities.CollectionItem>(); + /// <summary> + /// Default constructor + /// </summary> + public Collection() + { + CollectionItem = new LinkedList<CollectionItem>(); - Init(); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set { - _Name = value; + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } } - } - } + } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection<global::Jellyfin.Data.Entities.CollectionItem> CollectionItem { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CollectionItem_CollectionItem_Id")] + public virtual ICollection<CollectionItem> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static CollectionItem CreateCollectionItemUnsafe() - { - return new CollectionItem(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="_collection0"></param> - /// <param name="_collectionitem1"></param> - /// <param name="_collectionitem2"></param> - 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. - - if (_collection0 == null) throw new ArgumentNullException(nameof(_collection0)); - _collection0.CollectionItem.Add(this); - - if (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1)); - _collectionitem1.Next = this; - - if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2)); - _collectionitem2.Previous = this; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="_collection0"></param> - /// <param name="_collectionitem1"></param> - /// <param name="_collectionitem2"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("CollectionItem")] + public partial class CollectionItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CollectionItem CreateCollectionItemUnsafe() + { + return new CollectionItem(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="_collection0"></param> + /// <param name="_collectionitem1"></param> + /// <param name="_collectionitem2"></param> + 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 (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1)); + _collectionitem1.Next = this; + + if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2)); + _collectionitem2.Previous = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="_collection0"></param> + /// <param name="_collectionitem1"></param> + /// <param name="_collectionitem2"></param> + public static CollectionItem Create(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2) + { + return new CollectionItem(_collection0, _collectionitem1, _collectionitem2); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - /// <summary> - /// Required - /// </summary> - public virtual global::Jellyfin.Data.Entities.LibraryItem LibraryItem { get; set; } - - /// <remarks> - /// TODO check if this properly updated dependant and has the proper principal relationship - /// </remarks> - public virtual global::Jellyfin.Data.Entities.CollectionItem Next { get; set; } - - /// <remarks> - /// TODO check if this properly updated dependant and has the proper principal relationship - /// </remarks> - public virtual global::Jellyfin.Data.Entities.CollectionItem Previous { get; set; } - - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + [ForeignKey("LibraryItem_Id")] + public virtual LibraryItem LibraryItem { get; set; } + + /// <remarks> + /// TODO check if this properly updated dependant and has the proper principal relationship + /// </remarks> + [ForeignKey("CollectionItem_Next_Id")] + public virtual CollectionItem Next { get; set; } + + /// <remarks> + /// TODO check if this properly updated dependant and has the proper principal relationship + /// </remarks> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Company() - { - CompanyMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CompanyMetadata>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Company CreateCompanyUnsafe() - { - return new Company(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="_moviemetadata0"></param> - /// <param name="_seriesmetadata1"></param> - /// <param name="_musicalbummetadata2"></param> - /// <param name="_bookmetadata3"></param> - /// <param name="_company4"></param> - 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); - - 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 (_bookmetadata3 == null) throw new ArgumentNullException(nameof(_bookmetadata3)); - _bookmetadata3.Publishers.Add(this); - - if (_company4 == null) throw new ArgumentNullException(nameof(_company4)); - _company4.Parent = this; - - this.CompanyMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CompanyMetadata>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="_moviemetadata0"></param> - /// <param name="_seriesmetadata1"></param> - /// <param name="_musicalbummetadata2"></param> - /// <param name="_bookmetadata3"></param> - /// <param name="_company4"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Company")] + public partial class Company + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Company() + { + CompanyMetadata = new HashSet<CompanyMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Company CreateCompanyUnsafe() + { + return new Company(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="_moviemetadata0"></param> + /// <param name="_seriesmetadata1"></param> + /// <param name="_musicalbummetadata2"></param> + /// <param name="_bookmetadata3"></param> + /// <param name="_company4"></param> + 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 (_musicalbummetadata2 == null) throw new ArgumentNullException(nameof(_musicalbummetadata2)); + _musicalbummetadata2.Labels.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; + + this.CompanyMetadata = new HashSet<CompanyMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="_moviemetadata0"></param> + /// <param name="_seriesmetadata1"></param> + /// <param name="_musicalbummetadata2"></param> + /// <param name="_bookmetadata3"></param> + /// <param name="_company4"></param> + public static Company Create(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4) + { + return new Company(_moviemetadata0, _seriesmetadata1, _musicalbummetadata2, _bookmetadata3, _company4); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual ICollection<global::Jellyfin.Data.Entities.CompanyMetadata> CompanyMetadata { get; protected set; } - - public virtual global::Jellyfin.Data.Entities.Company Parent { get; set; } - - } + protected set + { + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CompanyMetadata_CompanyMetadata_Id")] + public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; } + [ForeignKey("Company_Parent_Id")] + public virtual 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected CompanyMetadata(): base() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static CompanyMetadata CreateCompanyMetadataUnsafe() - { - return new CompanyMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_company0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_company0 == null) throw new ArgumentNullException(nameof(_company0)); - _company0.CompanyMetadata.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_company0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Description - /// </summary> - protected string _Description; - /// <summary> - /// When provided in a partial class, allows value of Description to be changed before setting. - /// </summary> - partial void SetDescription(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Description to be changed before returning. - /// </summary> - partial void GetDescription(ref string result); - - /// <summary> - /// Max length = 65535 - /// </summary> - [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) + [Table("CompanyMetadata")] + public partial class CompanyMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CompanyMetadata() : base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CompanyMetadata CreateCompanyMetadataUnsafe() + { + return new CompanyMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_company0"></param> + 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 (_company0 == null) throw new ArgumentNullException(nameof(_company0)); + _company0.CompanyMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_company0"></param> + public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0) + { + return new CompanyMetadata(title, language, dateadded, datemodified, _company0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Description + /// </summary> + protected string _Description; + /// <summary> + /// When provided in a partial class, allows value of Description to be changed before setting. + /// </summary> + partial void SetDescription(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Description to be changed before returning. + /// </summary> + partial void GetDescription(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [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) + { + _Description = value; + } + } + } + + /// <summary> + /// Backing field for Headquarters + /// </summary> + protected string _Headquarters; + /// <summary> + /// When provided in a partial class, allows value of Headquarters to be changed before setting. + /// </summary> + partial void SetHeadquarters(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Headquarters to be changed before returning. + /// </summary> + partial void GetHeadquarters(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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) + { + _Headquarters = value; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get { - _Description = value; + string value = _Country; + GetCountry(ref value); + return (_Country = value); } - } - } - - /// <summary> - /// Backing field for Headquarters - /// </summary> - protected string _Headquarters; - /// <summary> - /// When provided in a partial class, allows value of Headquarters to be changed before setting. - /// </summary> - partial void SetHeadquarters(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Headquarters to be changed before returning. - /// </summary> - partial void GetHeadquarters(ref string result); - - /// <summary> - /// Max length = 255 - /// </summary> - [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 = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } } - } - } - - /// <summary> - /// Backing field for Country - /// </summary> - protected string _Country; - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before setting. - /// </summary> - partial void SetCountry(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before returning. - /// </summary> - partial void GetCountry(ref string result); - - /// <summary> - /// Max length = 2 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Homepage + /// </summary> + protected string _Homepage; + /// <summary> + /// When provided in a partial class, allows value of Homepage to be changed before setting. + /// </summary> + partial void SetHomepage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Homepage to be changed before returning. + /// </summary> + partial void GetHomepage(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Homepage + { + get { - _Country = value; + string value = _Homepage; + GetHomepage(ref value); + return (_Homepage = value); } - } - } - - /// <summary> - /// Backing field for Homepage - /// </summary> - protected string _Homepage; - /// <summary> - /// When provided in a partial class, allows value of Homepage to be changed before setting. - /// </summary> - partial void SetHomepage(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Homepage to be changed before returning. - /// </summary> - partial void GetHomepage(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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 = _Homepage; + SetHomepage(oldValue, ref value); + if (oldValue != value) + { + _Homepage = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected CustomItem(): base() - { - CustomItemMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CustomItemMetadata>(); - Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + public partial class CustomItem : LibraryItem + { + partial void Init(); - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CustomItem() : base() + { + CustomItemMetadata = new HashSet<CustomItemMetadata>(); + Releases = new HashSet<Release>(); - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static CustomItem CreateCustomItemUnsafe() - { - return new CustomItem(); - } + Init(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public CustomItem(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CustomItem CreateCustomItemUnsafe() + { + return new CustomItem(); + } - this.CustomItemMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.CustomItemMetadata>(); - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public CustomItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - Init(); - } + this.CustomItemMetadata = new HashSet<CustomItemMetadata>(); + this.Releases = new HashSet<Release>(); - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static CustomItem Create(Guid urlid, DateTime dateadded) - { - return new CustomItem(urlid, dateadded); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static CustomItem Create(Guid urlid, DateTime dateadded) + { + return new CustomItem(urlid, dateadded); + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.CustomItemMetadata> CustomItemMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("CustomItemMetadata_CustomItemMetadata_Id")] + public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected CustomItemMetadata(): base() - { - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected CustomItemMetadata() : base() + { + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static CustomItemMetadata CreateCustomItemMetadataUnsafe() - { - return new CustomItemMetadata(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static CustomItemMetadata CreateCustomItemMetadataUnsafe() + { + return new CustomItemMetadata(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_customitem0"></param> - 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; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_customitem0"></param> + 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(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_customitem0"></param> - 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); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_customitem0"></param> + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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<global::Jellyfin.Data.Entities.Release>(); - EpisodeMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.EpisodeMetadata>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Episode CreateEpisodeUnsafe() - { - return new Episode(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_season0"></param> - 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. - - this.UrlId = urlid; - - if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); - _season0.Episodes.Add(this); - - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); - this.EpisodeMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.EpisodeMetadata>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_season0"></param> - public static Episode Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Season _season0) - { - return new Episode(urlid, dateadded, _season0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for EpisodeNumber - /// </summary> - protected int? _EpisodeNumber; - /// <summary> - /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting. - /// </summary> - partial void SetEpisodeNumber(int? oldValue, ref int? newValue); - /// <summary> - /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning. - /// </summary> - 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) + [Table("Episode")] + public partial class Episode : LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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 HashSet<Release>(); + EpisodeMetadata = new HashSet<EpisodeMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Episode CreateEpisodeUnsafe() + { + return new Episode(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_season0"></param> + 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; + + if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.Episodes.Add(this); + + this.Releases = new HashSet<Release>(); + this.EpisodeMetadata = new HashSet<EpisodeMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_season0"></param> + public static Episode Create(Guid urlid, DateTime dateadded, Season _season0) + { + return new Episode(urlid, dateadded, _season0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for EpisodeNumber + /// </summary> + protected int? _EpisodeNumber; + /// <summary> + /// When provided in a partial class, allows value of EpisodeNumber to be changed before setting. + /// </summary> + partial void SetEpisodeNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of EpisodeNumber to be changed before returning. + /// </summary> + partial void GetEpisodeNumber(ref int? result); + + public int? EpisodeNumber + { + get { - _EpisodeNumber = value; + int? value = _EpisodeNumber; + GetEpisodeNumber(ref value); + return (_EpisodeNumber = value); } - } - } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + set + { + int? oldValue = _EpisodeNumber; + SetEpisodeNumber(oldValue, ref value); + if (oldValue != value) + { + _EpisodeNumber = value; + } + } + } - public virtual ICollection<global::Jellyfin.Data.Entities.EpisodeMetadata> EpisodeMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> Releases { get; protected set; } + [ForeignKey("EpisodeMetadata_EpisodeMetadata_Id")] + public virtual ICollection<EpisodeMetadata> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected EpisodeMetadata(): base() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static EpisodeMetadata CreateEpisodeMetadataUnsafe() - { - return new EpisodeMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_episode0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_episode0 == null) throw new ArgumentNullException(nameof(_episode0)); - _episode0.EpisodeMetadata.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_episode0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Outline - /// </summary> - protected string _Outline; - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// </summary> - partial void SetOutline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// </summary> - partial void GetOutline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + [Table("EpisodeMetadata")] + public partial class EpisodeMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected EpisodeMetadata() : base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static EpisodeMetadata CreateEpisodeMetadataUnsafe() + { + return new EpisodeMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_episode0"></param> + 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 (_episode0 == null) throw new ArgumentNullException(nameof(_episode0)); + _episode0.EpisodeMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_episode0"></param> + public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0) + { + return new EpisodeMetadata(title, language, dateadded, datemodified, _episode0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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) + { + _Outline = value; + } + } + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get + { + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); + } + set { - _Outline = value; + string oldValue = _Plot; + SetPlot(oldValue, ref value); + if (oldValue != value) + { + _Plot = value; + } } - } - } - - /// <summary> - /// Backing field for Plot - /// </summary> - protected string _Plot; - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// </summary> - partial void SetPlot(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// </summary> - partial void GetPlot(ref string result); - - /// <summary> - /// Max length = 65535 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Tagline + { + get { - _Plot = value; + string value = _Tagline; + GetTagline(ref value); + return (_Tagline = value); } - } - } - - /// <summary> - /// Backing field for Tagline - /// </summary> - protected string _Tagline; - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// </summary> - partial void SetTagline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// </summary> - partial void GetTagline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + set { - _Tagline = value; + string oldValue = _Tagline; + SetTagline(oldValue, ref value); + if (oldValue != value) + { + _Tagline = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Genre() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Genre CreateGenreUnsafe() - { - return new Genre(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="name"></param> - /// <param name="_metadata0"></param> - public Genre(string name, global::Jellyfin.Data.Entities.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); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="name"></param> - /// <param name="_metadata0"></param> - public static Genre Create(string name, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new Genre(name, _metadata0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Genre")] + public partial class Genre + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Genre() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Genre CreateGenreUnsafe() + { + return new Genre(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_metadata0"></param> + 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); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_metadata0"></param> + public static Genre Create(string name, Metadata _metadata0) + { + return new Genre(name, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } - - /// <summary> - /// Backing field for Name - /// </summary> - internal string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); - - /// <summary> - /// Indexed, Required, Max length = 255 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Name + /// </summary> + internal string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Indexed, Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Name + { + get { - _Name = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Group() - { - GroupPermissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); - ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); - Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Group CreateGroupUnsafe() - { - return new Group(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="name"></param> - /// <param name="_user0"></param> - public Group(string name, global::Jellyfin.Data.Entities.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); - - this.GroupPermissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); - this.ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); - this.Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="name"></param> - /// <param name="_user0"></param> - public static Group Create(string name, global::Jellyfin.Data.Entities.User _user0) - { - return new Group(name, _user0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [Key] - [Required] - public int Id { get; protected set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string Name { get; set; } - - /// <summary> - /// Concurrency token - /// </summary> - [Timestamp] - public Byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual ICollection<global::Jellyfin.Data.Entities.Permission> GroupPermissions { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.Preference> Preferences { get; protected set; } - - } + [Table("Group")] + public partial class Group + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Group() + { + GroupPermissions = new HashSet<Permission>(); + ProviderMappings = new HashSet<ProviderMapping>(); + Preferences = new HashSet<Preference>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Group CreateGroupUnsafe() + { + return new Group(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_user0"></param> + 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); + + this.GroupPermissions = new HashSet<Permission>(); + this.ProviderMappings = new HashSet<ProviderMapping>(); + this.Preferences = new HashSet<Preference>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_user0"></param> + public static Group Create(string name, User _user0) + { + return new Group(name, _user0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Name { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + [ForeignKey("Permission_GroupPermissions_Id")] + public virtual ICollection<Permission> GroupPermissions { get; protected set; } + + [ForeignKey("ProviderMapping_ProviderMappings_Id")] + public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; } + + [ForeignKey("Preference_Preferences_Id")] + public virtual ICollection<Preference> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Library() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Library CreateLibraryUnsafe() - { - return new Library(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="name"></param> - public Library(string name) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="name"></param> - public static Library Create(string name) - { - return new Library(name); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Library")] + public partial class Library + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Library() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Library CreateLibraryUnsafe() + { + return new Library(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + public Library(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + public static Library Create(string name) + { + return new Library(name); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } - - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); - - /// <summary> - /// Required, Max length = 1024 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _Name = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to being abstract. - /// </summary> - protected LibraryItem() - { - Init(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - protected LibraryItem(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; - - - Init(); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("LibraryItem")] + public abstract partial class LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to being abstract. + /// </summary> + protected LibraryItem() + { + Init(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + protected LibraryItem(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + + Init(); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for UrlId + /// </summary> + internal Guid _UrlId; + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// </summary> + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// </summary> + partial void GetUrlId(ref Guid result); + + /// <summary> + /// Indexed, Required + /// This is whats gets displayed in the Urls and API requests. This could also be a string. + /// </summary> + [Required] + public Guid UrlId + { + get + { + Guid value = _UrlId; + GetUrlId(ref value); + return (_UrlId = value); + } + set { - _Id = value; + Guid oldValue = _UrlId; + SetUrlId(oldValue, ref value); + if (oldValue != value) + { + _UrlId = value; + } } - } - } - - /// <summary> - /// Backing field for UrlId - /// </summary> - internal Guid _UrlId; - /// <summary> - /// When provided in a partial class, allows value of UrlId to be changed before setting. - /// </summary> - partial void SetUrlId(Guid oldValue, ref Guid newValue); - /// <summary> - /// When provided in a partial class, allows value of UrlId to be changed before returning. - /// </summary> - partial void GetUrlId(ref Guid result); - - /// <summary> - /// Indexed, Required - /// This is whats gets displayed in the Urls and API requests. This could also be a string. - /// </summary> - [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) + } + + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [Required] + public DateTime DateAdded + { + get { - _UrlId = value; + DateTime value = _DateAdded; + GetDateAdded(ref value); + return (_DateAdded = value); } - } - } - - /// <summary> - /// Backing field for DateAdded - /// </summary> - protected DateTime _DateAdded; - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// </summary> - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// </summary> - partial void GetDateAdded(ref DateTime result); - - /// <summary> - /// Required - /// </summary> - [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) + internal set { - _DateAdded = value; + DateTime oldValue = _DateAdded; + SetDateAdded(oldValue, ref value); + if (oldValue != value) + { + _DateAdded = value; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - /// <summary> - /// Required - /// </summary> - public virtual global::Jellyfin.Data.Entities.LibraryRoot LibraryRoot { get; set; } - - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected LibraryRoot() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static LibraryRoot CreateLibraryRootUnsafe() - { - return new LibraryRoot(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="path">Absolute Path</param> - public LibraryRoot(string path) - { - if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); - this.Path = path; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="path">Absolute Path</param> - public static LibraryRoot Create(string path) - { - return new LibraryRoot(path); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("LibraryRoot")] + public partial class LibraryRoot + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected LibraryRoot() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static LibraryRoot CreateLibraryRootUnsafe() + { + return new LibraryRoot(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path">Absolute Path</param> + public LibraryRoot(string path) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path">Absolute Path</param> + public static LibraryRoot Create(string path) + { + return new LibraryRoot(path); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// Absolute Path + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get + { + string value = _Path; + GetPath(ref value); + return (_Path = value); + } + set { - _Id = value; + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } } - } - } - - /// <summary> - /// Backing field for Path - /// </summary> - protected string _Path; - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before setting. - /// </summary> - partial void SetPath(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before returning. - /// </summary> - partial void GetPath(ref string result); - - /// <summary> - /// Required, Max length = 65535 - /// Absolute Path - /// </summary> - [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) + } + + /// <summary> + /// Backing field for NetworkPath + /// </summary> + protected string _NetworkPath; + /// <summary> + /// When provided in a partial class, allows value of NetworkPath to be changed before setting. + /// </summary> + partial void SetNetworkPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of NetworkPath to be changed before returning. + /// </summary> + partial void GetNetworkPath(ref string result); + + /// <summary> + /// Max length = 65535 + /// Absolute network path, for example for transcoding sattelites. + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string NetworkPath + { + get { - _Path = value; + string value = _NetworkPath; + GetNetworkPath(ref value); + return (_NetworkPath = value); } - } - } - - /// <summary> - /// Backing field for NetworkPath - /// </summary> - protected string _NetworkPath; - /// <summary> - /// When provided in a partial class, allows value of NetworkPath to be changed before setting. - /// </summary> - partial void SetNetworkPath(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of NetworkPath to be changed before returning. - /// </summary> - partial void GetNetworkPath(ref string result); - - /// <summary> - /// Max length = 65535 - /// Absolute network path, for example for transcoding sattelites. - /// </summary> - [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) + set { - _NetworkPath = value; + string oldValue = _NetworkPath; + SetNetworkPath(oldValue, ref value); + if (oldValue != value) + { + _NetworkPath = value; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - /// <summary> - /// Required - /// </summary> - public virtual global::Jellyfin.Data.Entities.Library Library { get; set; } - - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MediaFile() - { - MediaFileStreams = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFileStream>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MediaFile CreateMediaFileUnsafe() - { - return new MediaFile(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="path">Relative to the LibraryRoot</param> - /// <param name="kind"></param> - /// <param name="_release0"></param> - 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; - - this.Kind = kind; - - if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); - _release0.MediaFiles.Add(this); - - this.MediaFileStreams = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFileStream>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="path">Relative to the LibraryRoot</param> - /// <param name="kind"></param> - /// <param name="_release0"></param> - public static MediaFile Create(string path, global::Jellyfin.Data.Enums.MediaFileKind kind, global::Jellyfin.Data.Entities.Release _release0) - { - return new MediaFile(path, kind, _release0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("MediaFile")] + public partial class MediaFile + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MediaFile() + { + MediaFileStreams = new HashSet<MediaFileStream>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MediaFile CreateMediaFileUnsafe() + { + return new MediaFile(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="path">Relative to the LibraryRoot</param> + /// <param name="kind"></param> + /// <param name="_release0"></param> + public MediaFile(string path, Enums.MediaFileKind kind, Release _release0) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path)); + this.Path = path; + + this.Kind = kind; + + if (_release0 == null) throw new ArgumentNullException(nameof(_release0)); + _release0.MediaFiles.Add(this); + + this.MediaFileStreams = new HashSet<MediaFileStream>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="path">Relative to the LibraryRoot</param> + /// <param name="kind"></param> + /// <param name="_release0"></param> + public static MediaFile Create(string path, Enums.MediaFileKind kind, Release _release0) + { + return new MediaFile(path, kind, _release0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Path + /// </summary> + protected string _Path; + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before setting. + /// </summary> + partial void SetPath(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Path to be changed before returning. + /// </summary> + partial void GetPath(ref string result); + + /// <summary> + /// Required, Max length = 65535 + /// Relative to the LibraryRoot + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Path + { + get + { + string value = _Path; + GetPath(ref value); + return (_Path = value); + } + set { - _Id = value; + string oldValue = _Path; + SetPath(oldValue, ref value); + if (oldValue != value) + { + _Path = value; + } } - } - } - - /// <summary> - /// Backing field for Path - /// </summary> - protected string _Path; - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before setting. - /// </summary> - partial void SetPath(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Path to be changed before returning. - /// </summary> - partial void GetPath(ref string result); - - /// <summary> - /// Required, Max length = 65535 - /// Relative to the LibraryRoot - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Kind + /// </summary> + protected Enums.MediaFileKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(Enums.MediaFileKind oldValue, ref Enums.MediaFileKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref Enums.MediaFileKind result); + + /// <summary> + /// Required + /// </summary> + [Required] + public Enums.MediaFileKind Kind + { + get { - _Path = value; + Enums.MediaFileKind value = _Kind; + GetKind(ref value); + return (_Kind = value); } - } - } - - /// <summary> - /// Backing field for Kind - /// </summary> - protected global::Jellyfin.Data.Enums.MediaFileKind _Kind; - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// </summary> - partial void SetKind(global::Jellyfin.Data.Enums.MediaFileKind oldValue, ref global::Jellyfin.Data.Enums.MediaFileKind newValue); - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// </summary> - partial void GetKind(ref global::Jellyfin.Data.Enums.MediaFileKind result); - - /// <summary> - /// Required - /// </summary> - [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) + set { - _Kind = value; + Enums.MediaFileKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + } } - } - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.MediaFileStream> MediaFileStreams { get; protected set; } + [ForeignKey("MediaFileStream_MediaFileStreams_Id")] + public virtual ICollection<MediaFileStream> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MediaFileStream() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MediaFileStream CreateMediaFileStreamUnsafe() - { - return new MediaFileStream(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="streamnumber"></param> - /// <param name="_mediafile0"></param> - public MediaFileStream(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) - { - this.StreamNumber = streamnumber; - - if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0)); - _mediafile0.MediaFileStreams.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="streamnumber"></param> - /// <param name="_mediafile0"></param> - public static MediaFileStream Create(int streamnumber, global::Jellyfin.Data.Entities.MediaFile _mediafile0) - { - return new MediaFileStream(streamnumber, _mediafile0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("MediaFileStream")] + public partial class MediaFileStream + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MediaFileStream() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MediaFileStream CreateMediaFileStreamUnsafe() + { + return new MediaFileStream(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="streamnumber"></param> + /// <param name="_mediafile0"></param> + public MediaFileStream(int streamnumber, MediaFile _mediafile0) + { + this.StreamNumber = streamnumber; + + if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0)); + _mediafile0.MediaFileStreams.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="streamnumber"></param> + /// <param name="_mediafile0"></param> + public static MediaFileStream Create(int streamnumber, MediaFile _mediafile0) + { + return new MediaFileStream(streamnumber, _mediafile0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } - - /// <summary> - /// Backing field for StreamNumber - /// </summary> - protected int _StreamNumber; - /// <summary> - /// When provided in a partial class, allows value of StreamNumber to be changed before setting. - /// </summary> - partial void SetStreamNumber(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of StreamNumber to be changed before returning. - /// </summary> - partial void GetStreamNumber(ref int result); - - /// <summary> - /// Required - /// </summary> - [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) + } + + /// <summary> + /// Backing field for StreamNumber + /// </summary> + protected int _StreamNumber; + /// <summary> + /// When provided in a partial class, allows value of StreamNumber to be changed before setting. + /// </summary> + partial void SetStreamNumber(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of StreamNumber to be changed before returning. + /// </summary> + partial void GetStreamNumber(ref int result); + + /// <summary> + /// Required + /// </summary> + [Required] + public int StreamNumber + { + get { - _StreamNumber = value; + int value = _StreamNumber; + GetStreamNumber(ref value); + return (_StreamNumber = value); } - } - } + set + { + int oldValue = _StreamNumber; + SetStreamNumber(oldValue, ref value); + if (oldValue != value) + { + _StreamNumber = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to being abstract. - /// </summary> - protected Metadata() - { - PersonRoles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PersonRole>(); - Genres = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Genre>(); - Artwork = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Artwork>(); - Ratings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Rating>(); - Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); - - Init(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - 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; - - this.PersonRoles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PersonRole>(); - this.Genres = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Genre>(); - this.Artwork = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Artwork>(); - this.Ratings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Rating>(); - this.Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); - - Init(); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Metadata")] + public abstract partial class Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to being abstract. + /// </summary> + protected Metadata() + { + PersonRoles = new HashSet<PersonRole>(); + Genres = new HashSet<Genre>(); + Artwork = new HashSet<Artwork>(); + Ratings = new HashSet<Rating>(); + Sources = new HashSet<MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + 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; + + this.PersonRoles = new HashSet<PersonRole>(); + this.Genres = new HashSet<Genre>(); + this.Artwork = new HashSet<Artwork>(); + this.Ratings = new HashSet<Rating>(); + this.Sources = new HashSet<MetadataProviderId>(); + + Init(); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Title + /// </summary> + protected string _Title; + /// <summary> + /// When provided in a partial class, allows value of Title to be changed before setting. + /// </summary> + partial void SetTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Title to be changed before returning. + /// </summary> + partial void GetTitle(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// The title or name of the object + /// </summary> + [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) + { + _Title = value; + } + } + } + + /// <summary> + /// Backing field for OriginalTitle + /// </summary> + protected string _OriginalTitle; + /// <summary> + /// When provided in a partial class, allows value of OriginalTitle to be changed before setting. + /// </summary> + partial void SetOriginalTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of OriginalTitle to be changed before returning. + /// </summary> + partial void GetOriginalTitle(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string OriginalTitle + { + get { - _Id = value; + string value = _OriginalTitle; + GetOriginalTitle(ref value); + return (_OriginalTitle = value); } - } - } - - /// <summary> - /// Backing field for Title - /// </summary> - protected string _Title; - /// <summary> - /// When provided in a partial class, allows value of Title to be changed before setting. - /// </summary> - partial void SetTitle(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Title to be changed before returning. - /// </summary> - partial void GetTitle(ref string result); - - /// <summary> - /// Required, Max length = 1024 - /// The title or name of the object - /// </summary> - [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) + set { - _Title = value; + string oldValue = _OriginalTitle; + SetOriginalTitle(oldValue, ref value); + if (oldValue != value) + { + _OriginalTitle = value; + } } - } - } - - /// <summary> - /// Backing field for OriginalTitle - /// </summary> - protected string _OriginalTitle; - /// <summary> - /// When provided in a partial class, allows value of OriginalTitle to be changed before setting. - /// </summary> - partial void SetOriginalTitle(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of OriginalTitle to be changed before returning. - /// </summary> - partial void GetOriginalTitle(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for SortTitle + /// </summary> + protected string _SortTitle; + /// <summary> + /// When provided in a partial class, allows value of SortTitle to be changed before setting. + /// </summary> + partial void SetSortTitle(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of SortTitle to be changed before returning. + /// </summary> + partial void GetSortTitle(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string SortTitle + { + get { - _OriginalTitle = value; + string value = _SortTitle; + GetSortTitle(ref value); + return (_SortTitle = value); } - } - } - - /// <summary> - /// Backing field for SortTitle - /// </summary> - protected string _SortTitle; - /// <summary> - /// When provided in a partial class, allows value of SortTitle to be changed before setting. - /// </summary> - partial void SetSortTitle(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of SortTitle to be changed before returning. - /// </summary> - partial void GetSortTitle(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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 = _SortTitle; + SetSortTitle(oldValue, ref value); + if (oldValue != value) + { + _SortTitle = value; + } } - } - } - - /// <summary> - /// Backing field for Language - /// </summary> - protected string _Language; - /// <summary> - /// When provided in a partial class, allows value of Language to be changed before setting. - /// </summary> - partial void SetLanguage(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Language to be changed before returning. - /// </summary> - partial void GetLanguage(ref string result); - - /// <summary> - /// Required, Min length = 3, Max length = 3 - /// ISO-639-3 3-character language codes - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Language + /// </summary> + protected string _Language; + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before setting. + /// </summary> + partial void SetLanguage(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Language to be changed before returning. + /// </summary> + partial void GetLanguage(ref string result); + + /// <summary> + /// Required, Min length = 3, Max length = 3 + /// ISO-639-3 3-character language codes + /// </summary> + [Required] + [MinLength(3)] + [MaxLength(3)] + [StringLength(3)] + public string Language + { + get { - _Language = value; + string value = _Language; + GetLanguage(ref value); + return (_Language = value); } - } - } - - /// <summary> - /// Backing field for ReleaseDate - /// </summary> - protected DateTimeOffset? _ReleaseDate; - /// <summary> - /// When provided in a partial class, allows value of ReleaseDate to be changed before setting. - /// </summary> - partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// <summary> - /// When provided in a partial class, allows value of ReleaseDate to be changed before returning. - /// </summary> - 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 = _Language; + SetLanguage(oldValue, ref value); + if (oldValue != value) + { + _Language = value; + } } - } - } - - /// <summary> - /// Backing field for DateAdded - /// </summary> - protected DateTime _DateAdded; - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// </summary> - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// </summary> - partial void GetDateAdded(ref DateTime result); - - /// <summary> - /// Required - /// </summary> - [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) + } + + /// <summary> + /// Backing field for ReleaseDate + /// </summary> + protected DateTimeOffset? _ReleaseDate; + /// <summary> + /// When provided in a partial class, allows value of ReleaseDate to be changed before setting. + /// </summary> + partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of ReleaseDate to be changed before returning. + /// </summary> + partial void GetReleaseDate(ref DateTimeOffset? result); + + public DateTimeOffset? ReleaseDate + { + get { - _DateAdded = value; + DateTimeOffset? value = _ReleaseDate; + GetReleaseDate(ref value); + return (_ReleaseDate = value); } - } - } - - /// <summary> - /// Backing field for DateModified - /// </summary> - protected DateTime _DateModified; - /// <summary> - /// When provided in a partial class, allows value of DateModified to be changed before setting. - /// </summary> - partial void SetDateModified(DateTime oldValue, ref DateTime newValue); - /// <summary> - /// When provided in a partial class, allows value of DateModified to be changed before returning. - /// </summary> - partial void GetDateModified(ref DateTime result); - - /// <summary> - /// Required - /// </summary> - [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; + DateTimeOffset? oldValue = _ReleaseDate; + SetReleaseDate(oldValue, ref value); + if (oldValue != value) + { + _ReleaseDate = value; + } } - } - } + } + + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for DateModified + /// </summary> + protected DateTime _DateModified; + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// </summary> + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// </summary> + partial void GetDateModified(ref DateTime result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.PersonRole> PersonRoles { get; protected set; } + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection<PersonRole> PersonRoles { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Genre> Genres { get; protected set; } + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection<Genre> Genres { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Artwork> Artwork { get; protected set; } + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection<Artwork> Artwork { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Rating> Ratings { get; protected set; } + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection<Rating> Ratings { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + [ForeignKey("PersonRole_PersonRoles_Id")] + public virtual ICollection<MetadataProviderId> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MetadataProvider() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MetadataProvider CreateMetadataProviderUnsafe() - { - return new MetadataProvider(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="name"></param> - public MetadataProvider(string name) - { - if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); - this.Name = name; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="name"></param> - public static MetadataProvider Create(string name) - { - return new MetadataProvider(name); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("MetadataProvider")] + public partial class MetadataProvider + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MetadataProvider() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MetadataProvider CreateMetadataProviderUnsafe() + { + return new MetadataProvider(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + public MetadataProvider(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + public static MetadataProvider Create(string name) + { + return new MetadataProvider(name); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } - - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); - - /// <summary> - /// Required, Max length = 1024 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _Name = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } + set + { + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + public void OnSavingChanges() + { + RowVersion++; + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MetadataProviderId CreateMetadataProviderIdUnsafe() - { - return new MetadataProviderId(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="providerid"></param> - /// <param name="_metadata0"></param> - /// <param name="_person1"></param> - /// <param name="_personrole2"></param> - /// <param name="_ratingsource3"></param> - 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. - - 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 (_person1 == null) throw new ArgumentNullException(nameof(_person1)); - _person1.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; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="providerid"></param> - /// <param name="_metadata0"></param> - /// <param name="_person1"></param> - /// <param name="_personrole2"></param> - /// <param name="_ratingsource3"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("MetadataProviderId")] + public partial class MetadataProviderId + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MetadataProviderId CreateMetadataProviderIdUnsafe() + { + return new MetadataProviderId(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="providerid"></param> + /// <param name="_metadata0"></param> + /// <param name="_person1"></param> + /// <param name="_personrole2"></param> + /// <param name="_ratingsource3"></param> + 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 (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.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 (_ratingsource3 == null) throw new ArgumentNullException(nameof(_ratingsource3)); + _ratingsource3.Source = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="providerid"></param> + /// <param name="_metadata0"></param> + /// <param name="_person1"></param> + /// <param name="_personrole2"></param> + /// <param name="_ratingsource3"></param> + public static MetadataProviderId Create(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3) + { + return new MetadataProviderId(providerid, _metadata0, _person1, _personrole2, _ratingsource3); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for ProviderId + /// </summary> + protected string _ProviderId; + /// <summary> + /// When provided in a partial class, allows value of ProviderId to be changed before setting. + /// </summary> + partial void SetProviderId(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of ProviderId to be changed before returning. + /// </summary> + partial void GetProviderId(ref string result); + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string ProviderId + { + get { - _Id = value; + string value = _ProviderId; + GetProviderId(ref value); + return (_ProviderId = value); } - } - } - - /// <summary> - /// Backing field for ProviderId - /// </summary> - protected string _ProviderId; - /// <summary> - /// When provided in a partial class, allows value of ProviderId to be changed before setting. - /// </summary> - partial void SetProviderId(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of ProviderId to be changed before returning. - /// </summary> - partial void GetProviderId(ref string result); - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [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) + set { - _ProviderId = value; + string oldValue = _ProviderId; + SetProviderId(oldValue, ref value); + if (oldValue != value) + { + _ProviderId = value; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - /// <summary> - /// Required - /// </summary> - public virtual global::Jellyfin.Data.Entities.MetadataProvider MetadataProvider { get; set; } - - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// Required + /// </summary> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Movie(): base() - { - Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); - MovieMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MovieMetadata>(); + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Movie() : base() + { + Releases = new HashSet<Release>(); + MovieMetadata = new HashSet<MovieMetadata>(); - Init(); - } + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Movie CreateMovieUnsafe() - { - return new Movie(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Movie CreateMovieUnsafe() + { + return new Movie(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public Movie(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Movie(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); - this.MovieMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MovieMetadata>(); + this.Releases = new HashSet<Release>(); + this.MovieMetadata = new HashSet<MovieMetadata>(); - Init(); - } + Init(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static Movie Create(Guid urlid, DateTime dateadded) - { - return new Movie(urlid, dateadded); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Movie Create(Guid urlid, DateTime dateadded) + { + return new Movie(urlid, dateadded); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> Releases { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.MovieMetadata> MovieMetadata { get; protected set; } + [ForeignKey("MovieMetadata_MovieMetadata_Id")] + public virtual ICollection<MovieMetadata> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MovieMetadata(): base() - { - Studios = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MovieMetadata CreateMovieMetadataUnsafe() - { - return new MovieMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_movie0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); - _movie0.MovieMetadata.Add(this); - - this.Studios = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_movie0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Outline - /// </summary> - protected string _Outline; - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// </summary> - partial void SetOutline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// </summary> - partial void GetOutline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + [Table("MovieMetadata")] + public partial class MovieMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MovieMetadata() : base() + { + Studios = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MovieMetadata CreateMovieMetadataUnsafe() + { + return new MovieMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_movie0"></param> + 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 (_movie0 == null) throw new ArgumentNullException(nameof(_movie0)); + _movie0.MovieMetadata.Add(this); + + this.Studios = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_movie0"></param> + public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0) + { + return new MovieMetadata(title, language, dateadded, datemodified, _movie0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } - - /// <summary> - /// Backing field for Plot - /// </summary> - protected string _Plot; - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// </summary> - partial void SetPlot(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// </summary> - partial void GetPlot(ref string result); - - /// <summary> - /// Max length = 65535 - /// </summary> - [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; + } } - } - } - - /// <summary> - /// Backing field for Tagline - /// </summary> - protected string _Tagline; - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// </summary> - partial void SetTagline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// </summary> - partial void GetTagline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get { - _Tagline = value; + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); } - } - } - - /// <summary> - /// Backing field for Country - /// </summary> - protected string _Country; - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before setting. - /// </summary> - partial void SetCountry(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before returning. - /// </summary> - partial void GetCountry(ref string result); - - /// <summary> - /// Max length = 2 - /// </summary> - [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 - *************************************************************************/ + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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; + } + } + } - public virtual ICollection<global::Jellyfin.Data.Entities.Company> Studios { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Company_Studios_Id")] + public virtual ICollection<Company> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MusicAlbum(): base() - { - MusicAlbumMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata>(); - Tracks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Track>(); + [Table("MusicAlbum")] + public partial class MusicAlbum : LibraryItem + { + partial void Init(); - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MusicAlbum() : base() + { + MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>(); + Tracks = new HashSet<Track>(); - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MusicAlbum CreateMusicAlbumUnsafe() - { - return new MusicAlbum(); - } + Init(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public MusicAlbum(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MusicAlbum CreateMusicAlbumUnsafe() + { + return new MusicAlbum(); + } - this.MusicAlbumMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata>(); - this.Tracks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Track>(); + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public MusicAlbum(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - Init(); - } + this.MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>(); + this.Tracks = new HashSet<Track>(); - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static MusicAlbum Create(Guid urlid, DateTime dateadded) - { - return new MusicAlbum(urlid, dateadded); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static MusicAlbum Create(Guid urlid, DateTime dateadded) + { + return new MusicAlbum(urlid, dateadded); + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id")] + public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Track> Tracks { get; protected set; } + [ForeignKey("Track_Tracks_Id")] + public virtual ICollection<Track> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected MusicAlbumMetadata(): base() - { - Labels = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe() - { - return new MusicAlbumMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_musicalbum0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); - _musicalbum0.MusicAlbumMetadata.Add(this); - - this.Labels = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_musicalbum0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Barcode - /// </summary> - protected string _Barcode; - /// <summary> - /// When provided in a partial class, allows value of Barcode to be changed before setting. - /// </summary> - partial void SetBarcode(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Barcode to be changed before returning. - /// </summary> - partial void GetBarcode(ref string result); - - /// <summary> - /// Max length = 255 - /// </summary> - [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) + [Table("MusicAlbumMetadata")] + public partial class MusicAlbumMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected MusicAlbumMetadata() : base() + { + Labels = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static MusicAlbumMetadata CreateMusicAlbumMetadataUnsafe() + { + return new MusicAlbumMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_musicalbum0"></param> + 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 (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.MusicAlbumMetadata.Add(this); + + this.Labels = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_musicalbum0"></param> + public static MusicAlbumMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, MusicAlbum _musicalbum0) + { + return new MusicAlbumMetadata(title, language, dateadded, datemodified, _musicalbum0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Barcode + /// </summary> + protected string _Barcode; + /// <summary> + /// When provided in a partial class, allows value of Barcode to be changed before setting. + /// </summary> + partial void SetBarcode(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Barcode to be changed before returning. + /// </summary> + partial void GetBarcode(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [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) + { + _Barcode = value; + } + } + } + + /// <summary> + /// Backing field for LabelNumber + /// </summary> + protected string _LabelNumber; + /// <summary> + /// When provided in a partial class, allows value of LabelNumber to be changed before setting. + /// </summary> + partial void SetLabelNumber(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of LabelNumber to be changed before returning. + /// </summary> + partial void GetLabelNumber(ref string result); + + /// <summary> + /// Max length = 255 + /// </summary> + [MaxLength(255)] + [StringLength(255)] + public string LabelNumber + { + get + { + string value = _LabelNumber; + GetLabelNumber(ref value); + return (_LabelNumber = value); + } + set { - _Barcode = value; + string oldValue = _LabelNumber; + SetLabelNumber(oldValue, ref value); + if (oldValue != value) + { + _LabelNumber = value; + } } - } - } - - /// <summary> - /// Backing field for LabelNumber - /// </summary> - protected string _LabelNumber; - /// <summary> - /// When provided in a partial class, allows value of LabelNumber to be changed before setting. - /// </summary> - partial void SetLabelNumber(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of LabelNumber to be changed before returning. - /// </summary> - partial void GetLabelNumber(ref string result); - - /// <summary> - /// Max length = 255 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [MaxLength(2)] + [StringLength(2)] + public string Country + { + get { - _LabelNumber = value; + string value = _Country; + GetCountry(ref value); + return (_Country = value); } - } - } - - /// <summary> - /// Backing field for Country - /// </summary> - protected string _Country; - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before setting. - /// </summary> - partial void SetCountry(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before returning. - /// </summary> - partial void GetCountry(ref string result); - - /// <summary> - /// Max length = 2 - /// </summary> - [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 = _Country; + SetCountry(oldValue, ref value); + if (oldValue != value) + { + _Country = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.Company> Labels { get; protected set; } + [ForeignKey("Company_Labels_Id")] + public virtual ICollection<Company> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Permission() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Permission CreatePermissionUnsafe() - { - return new Permission(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="kind"></param> - /// <param name="value"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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; - - this.Value = value; - - if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.Permissions.Add(this); - - if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.GroupPermissions.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="kind"></param> - /// <param name="value"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [Key] - [Required] - public int Id { get; protected set; } - - /// <summary> - /// Backing field for Kind - /// </summary> - protected global::Jellyfin.Data.Enums.PermissionKind _Kind; - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before setting. - /// </summary> - partial void SetKind(global::Jellyfin.Data.Enums.PermissionKind oldValue, ref global::Jellyfin.Data.Enums.PermissionKind newValue); - /// <summary> - /// When provided in a partial class, allows value of Kind to be changed before returning. - /// </summary> - partial void GetKind(ref global::Jellyfin.Data.Enums.PermissionKind result); - - /// <summary> - /// Required - /// </summary> - [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) + [Table("Permission")] + public partial class Permission + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Permission() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Permission CreatePermissionUnsafe() + { + return new Permission(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + public Permission(Enums.PermissionKind kind, bool value, User _user0, Group _group1) + { + this.Kind = kind; + + this.Value = value; + + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Permissions.Add(this); + + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.GroupPermissions.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + public static Permission Create(Enums.PermissionKind kind, bool value, User _user0, Group _group1) + { + return new Permission(kind, value, _user0, _group1); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Backing field for Kind + /// </summary> + protected Enums.PermissionKind _Kind; + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before setting. + /// </summary> + partial void SetKind(Enums.PermissionKind oldValue, ref Enums.PermissionKind newValue); + /// <summary> + /// When provided in a partial class, allows value of Kind to be changed before returning. + /// </summary> + partial void GetKind(ref Enums.PermissionKind result); + + /// <summary> + /// Required + /// </summary> + [Required] + public Enums.PermissionKind Kind + { + get { - _Kind = value; - OnPropertyChanged(); + Enums.PermissionKind value = _Kind; + GetKind(ref value); + return (_Kind = value); } - } - } - - /// <summary> - /// Required - /// </summary> - [Required] - public bool Value { get; set; } - - /// <summary> - /// Concurrency token - /// </summary> - [Timestamp] - public Byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - } + set + { + Enums.PermissionKind oldValue = _Kind; + SetKind(oldValue, ref value); + if (oldValue != value) + { + _Kind = value; + OnPropertyChanged(); + } + } + } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool Value { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - -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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Person() - { - Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Person() + { + Sources = new HashSet<MetadataProviderId>(); - Init(); - } + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Person CreatePersonUnsafe() - { - return new Person(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Person CreatePersonUnsafe() + { + return new Person(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid"></param> - /// <param name="name"></param> - public Person(Guid urlid, string name, DateTime dateadded, DateTime datemodified) - { - this.UrlId = urlid; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid"></param> + /// <param name="name"></param> + 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<global::Jellyfin.Data.Entities.MetadataProviderId>(); + this.Sources = new HashSet<MetadataProviderId>(); - Init(); - } + Init(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid"></param> - /// <param name="name"></param> - public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) - { - return new Person(urlid, name, dateadded, datemodified); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid"></param> + /// <param name="name"></param> + public static Person Create(Guid urlid, string name, DateTime dateadded, DateTime datemodified) + { + return new Person(urlid, name, dateadded, datemodified); + } - /************************************************************************* - * Properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get + { + int value = _Id; + GetId(ref value); + return (_Id = value); + } + protected set { - _Id = value; + int oldValue = _Id; + SetId(oldValue, ref value); + if (oldValue != value) + { + _Id = value; + } } - } - } + } - /// <summary> - /// Backing field for UrlId - /// </summary> - protected Guid _UrlId; - /// <summary> - /// When provided in a partial class, allows value of UrlId to be changed before setting. - /// </summary> - partial void SetUrlId(Guid oldValue, ref Guid newValue); - /// <summary> - /// When provided in a partial class, allows value of UrlId to be changed before returning. - /// </summary> - partial void GetUrlId(ref Guid result); + /// <summary> + /// Backing field for UrlId + /// </summary> + protected Guid _UrlId; + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before setting. + /// </summary> + partial void SetUrlId(Guid oldValue, ref Guid newValue); + /// <summary> + /// When provided in a partial class, allows value of UrlId to be changed before returning. + /// </summary> + partial void GetUrlId(ref Guid result); - /// <summary> - /// Required - /// </summary> - [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) + /// <summary> + /// Required + /// </summary> + [Required] + public Guid UrlId + { + get { - _UrlId = value; + Guid value = _UrlId; + GetUrlId(ref value); + return (_UrlId = value); } - } - } + set + { + Guid oldValue = _UrlId; + SetUrlId(oldValue, ref value); + if (oldValue != value) + { + _UrlId = value; + } + } + } - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); - /// <summary> - /// Required, Max length = 1024 - /// </summary> - [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) + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [Required] + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get + { + string value = _Name; + GetName(ref value); + return (_Name = value); + } + set { - _Name = value; + string oldValue = _Name; + SetName(oldValue, ref value); + if (oldValue != value) + { + _Name = value; + } } - } - } + } - /// <summary> - /// Backing field for SourceId - /// </summary> - protected string _SourceId; - /// <summary> - /// When provided in a partial class, allows value of SourceId to be changed before setting. - /// </summary> - partial void SetSourceId(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of SourceId to be changed before returning. - /// </summary> - partial void GetSourceId(ref string result); + /// <summary> + /// Backing field for SourceId + /// </summary> + protected string _SourceId; + /// <summary> + /// When provided in a partial class, allows value of SourceId to be changed before setting. + /// </summary> + partial void SetSourceId(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of SourceId to be changed before returning. + /// </summary> + partial void GetSourceId(ref string result); - /// <summary> - /// Max length = 255 - /// </summary> - [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) + /// <summary> + /// Max length = 255 + /// </summary> + [MaxLength(255)] + [StringLength(255)] + public string SourceId + { + get { - _SourceId = value; + string value = _SourceId; + GetSourceId(ref value); + return (_SourceId = value); } - } - } + set + { + string oldValue = _SourceId; + SetSourceId(oldValue, ref value); + if (oldValue != value) + { + _SourceId = value; + } + } + } - /// <summary> - /// Backing field for DateAdded - /// </summary> - protected DateTime _DateAdded; - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before setting. - /// </summary> - partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); - /// <summary> - /// When provided in a partial class, allows value of DateAdded to be changed before returning. - /// </summary> - partial void GetDateAdded(ref DateTime result); + /// <summary> + /// Backing field for DateAdded + /// </summary> + protected DateTime _DateAdded; + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before setting. + /// </summary> + partial void SetDateAdded(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateAdded to be changed before returning. + /// </summary> + partial void GetDateAdded(ref DateTime result); - /// <summary> - /// Required - /// </summary> - [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) + /// <summary> + /// Required + /// </summary> + [Required] + public DateTime DateAdded + { + get + { + DateTime value = _DateAdded; + GetDateAdded(ref value); + return (_DateAdded = value); + } + internal set { - _DateAdded = value; + DateTime oldValue = _DateAdded; + SetDateAdded(oldValue, ref value); + if (oldValue != value) + { + _DateAdded = value; + } } - } - } + } - /// <summary> - /// Backing field for DateModified - /// </summary> - protected DateTime _DateModified; - /// <summary> - /// When provided in a partial class, allows value of DateModified to be changed before setting. - /// </summary> - partial void SetDateModified(DateTime oldValue, ref DateTime newValue); - /// <summary> - /// When provided in a partial class, allows value of DateModified to be changed before returning. - /// </summary> - partial void GetDateModified(ref DateTime result); + /// <summary> + /// Backing field for DateModified + /// </summary> + protected DateTime _DateModified; + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before setting. + /// </summary> + partial void SetDateModified(DateTime oldValue, ref DateTime newValue); + /// <summary> + /// When provided in a partial class, allows value of DateModified to be changed before returning. + /// </summary> + partial void GetDateModified(ref DateTime result); - /// <summary> - /// Required - /// </summary> - [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) + /// <summary> + /// Required + /// </summary> + [Required] + public DateTime DateModified + { + get + { + DateTime value = _DateModified; + GetDateModified(ref value); + return (_DateModified = value); + } + internal set { - _DateModified = value; + DateTime oldValue = _DateModified; + SetDateModified(oldValue, ref value); + if (oldValue != value) + { + _DateModified = value; + } } - } - } + } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ + public void OnSavingChanges() + { + RowVersion++; + } - public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MetadataProviderId_Sources_Id")] + public virtual ICollection<MetadataProviderId> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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<global::Jellyfin.Data.Entities.MetadataProviderId>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static PersonRole CreatePersonRoleUnsafe() - { - return new PersonRole(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="type"></param> - /// <param name="_metadata0"></param> - 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. - - this.Type = type; - - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.PersonRoles.Add(this); - - this.Sources = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MetadataProviderId>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="type"></param> - /// <param name="_metadata0"></param> - public static PersonRole Create(global::Jellyfin.Data.Enums.PersonRoleType type, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new PersonRole(type, _metadata0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("PersonRole")] + public partial class PersonRole + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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 HashSet<MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static PersonRole CreatePersonRoleUnsafe() + { + return new PersonRole(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="type"></param> + /// <param name="_metadata0"></param> + 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; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.PersonRoles.Add(this); + + this.Sources = new HashSet<MetadataProviderId>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="type"></param> + /// <param name="_metadata0"></param> + public static PersonRole Create(Enums.PersonRoleType type, Metadata _metadata0) + { + return new PersonRole(type, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Role + /// </summary> + protected string _Role; + /// <summary> + /// When provided in a partial class, allows value of Role to be changed before setting. + /// </summary> + partial void SetRole(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Role to be changed before returning. + /// </summary> + partial void GetRole(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Role + { + get { - _Id = value; + string value = _Role; + GetRole(ref value); + return (_Role = value); } - } - } - - /// <summary> - /// Backing field for Role - /// </summary> - protected string _Role; - /// <summary> - /// When provided in a partial class, allows value of Role to be changed before setting. - /// </summary> - partial void SetRole(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Role to be changed before returning. - /// </summary> - partial void GetRole(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + set { - _Role = value; + string oldValue = _Role; + SetRole(oldValue, ref value); + if (oldValue != value) + { + _Role = value; + } } - } - } - - /// <summary> - /// Backing field for Type - /// </summary> - protected global::Jellyfin.Data.Enums.PersonRoleType _Type; - /// <summary> - /// When provided in a partial class, allows value of Type to be changed before setting. - /// </summary> - partial void SetType(global::Jellyfin.Data.Enums.PersonRoleType oldValue, ref global::Jellyfin.Data.Enums.PersonRoleType newValue); - /// <summary> - /// When provided in a partial class, allows value of Type to be changed before returning. - /// </summary> - partial void GetType(ref global::Jellyfin.Data.Enums.PersonRoleType result); - - /// <summary> - /// Required - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Type + /// </summary> + protected Enums.PersonRoleType _Type; + /// <summary> + /// When provided in a partial class, allows value of Type to be changed before setting. + /// </summary> + partial void SetType(Enums.PersonRoleType oldValue, ref Enums.PersonRoleType newValue); + /// <summary> + /// When provided in a partial class, allows value of Type to be changed before returning. + /// </summary> + partial void GetType(ref Enums.PersonRoleType result); + + /// <summary> + /// Required + /// </summary> + [Required] + public Enums.PersonRoleType Type + { + get { - _Type = value; + Enums.PersonRoleType value = _Type; + GetType(ref value); + return (_Type = value); } - } - } + set + { + Enums.PersonRoleType oldValue = _Type; + SetType(oldValue, ref value); + if (oldValue != value) + { + _Type = value; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ - /************************************************************************* - * Navigation properties - *************************************************************************/ + /// <summary> + /// Required + /// </summary> + [ForeignKey("Person_Id")] - /// <summary> - /// Required - /// </summary> - public virtual global::Jellyfin.Data.Entities.Person Person { get; set; } + public virtual Person Person { get; set; } - public virtual global::Jellyfin.Data.Entities.Artwork Artwork { get; set; } + [ForeignKey("Artwork_Artwork_Id")] + public virtual Artwork Artwork { get; set; } - public virtual ICollection<global::Jellyfin.Data.Entities.MetadataProviderId> Sources { get; protected set; } + [ForeignKey("MetadataProviderId_Sources_Id")] + public virtual ICollection<MetadataProviderId> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Photo(): base() - { - PhotoMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PhotoMetadata>(); - Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + [Table("Photo")] + public partial class Photo : LibraryItem + { + partial void Init(); - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Photo() : base() + { + PhotoMetadata = new HashSet<PhotoMetadata>(); + Releases = new HashSet<Release>(); - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Photo CreatePhotoUnsafe() - { - return new Photo(); - } + Init(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public Photo(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Photo CreatePhotoUnsafe() + { + return new Photo(); + } - this.PhotoMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.PhotoMetadata>(); - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Photo(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; - Init(); - } + this.PhotoMetadata = new HashSet<PhotoMetadata>(); + this.Releases = new HashSet<Release>(); - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static Photo Create(Guid urlid, DateTime dateadded) - { - return new Photo(urlid, dateadded); - } + Init(); + } - /************************************************************************* - * Properties - *************************************************************************/ + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Photo Create(Guid urlid, DateTime dateadded) + { + return new Photo(urlid, dateadded); + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.PhotoMetadata> PhotoMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("PhotoMetadata_PhotoMetadata_Id")] + public virtual ICollection<PhotoMetadata> PhotoMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected PhotoMetadata(): base() - { - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected PhotoMetadata() : base() + { + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static PhotoMetadata CreatePhotoMetadataUnsafe() - { - return new PhotoMetadata(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static PhotoMetadata CreatePhotoMetadataUnsafe() + { + return new PhotoMetadata(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_photo0"></param> - 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; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_photo0"></param> + 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(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_photo0"></param> - 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); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_photo0"></param> + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Preference() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Preference CreatePreferenceUnsafe() - { - return new Preference(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="kind"></param> - /// <param name="value"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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; - - 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 (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.Preferences.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="kind"></param> - /// <param name="value"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [Key] - [Required] - public int Id { get; protected set; } - - /// <summary> - /// Required - /// </summary> - [Required] - public global::Jellyfin.Data.Enums.PreferenceKind Kind { get; set; } - - /// <summary> - /// Required, Max length = 65535 - /// </summary> - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string Value { get; set; } - - /// <summary> - /// Concurrency token - /// </summary> - [Timestamp] - public Byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - } + [Table("Preference")] + public partial class Preference + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Preference() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Preference CreatePreferenceUnsafe() + { + return new Preference(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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 (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.Preferences.Add(this); + + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.Preferences.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="kind"></param> + /// <param name="value"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + public static Preference Create(Enums.PreferenceKind kind, string value, User _user0, Group _group1) + { + return new Preference(kind, value, _user0, _group1); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public Enums.PreferenceKind Kind { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string Value { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - -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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected ProviderMapping() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static ProviderMapping CreateProviderMappingUnsafe() - { - return new ProviderMapping(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="providername"></param> - /// <param name="providersecrets"></param> - /// <param name="providerdata"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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; - - 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 (_user0 == null) throw new ArgumentNullException(nameof(_user0)); - _user0.ProviderMappings.Add(this); - - if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); - _group1.ProviderMappings.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="providername"></param> - /// <param name="providersecrets"></param> - /// <param name="providerdata"></param> - /// <param name="_user0"></param> - /// <param name="_group1"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [Key] - [Required] - public int Id { get; protected set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string ProviderName { get; set; } - - /// <summary> - /// Required, Max length = 65535 - /// </summary> - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderSecrets { get; set; } - - /// <summary> - /// Required, Max length = 65535 - /// </summary> - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderData { get; set; } - - /// <summary> - /// Concurrency token - /// </summary> - [Timestamp] - public Byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - } + [Table("ProviderMapping")] + public partial class ProviderMapping + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected ProviderMapping() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static ProviderMapping CreateProviderMappingUnsafe() + { + return new ProviderMapping(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="providername"></param> + /// <param name="providersecrets"></param> + /// <param name="providerdata"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + 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(providerdata)) throw new ArgumentNullException(nameof(providerdata)); + this.ProviderData = providerdata; + + if (_user0 == null) throw new ArgumentNullException(nameof(_user0)); + _user0.ProviderMappings.Add(this); + + if (_group1 == null) throw new ArgumentNullException(nameof(_group1)); + _group1.ProviderMappings.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="providername"></param> + /// <param name="providersecrets"></param> + /// <param name="providerdata"></param> + /// <param name="_user0"></param> + /// <param name="_group1"></param> + public static ProviderMapping Create(string providername, string providersecrets, string providerdata, User _user0, Group _group1) + { + return new ProviderMapping(providername, providersecrets, providerdata, _user0, _group1); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string ProviderName { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderSecrets { get; set; } + + /// <summary> + /// Required, Max length = 65535 + /// </summary> + [Required] + [MaxLength(65535)] + [StringLength(65535)] + public string ProviderData { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Rating() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Rating CreateRatingUnsafe() - { - return new Rating(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="value"></param> - /// <param name="_metadata0"></param> - public Rating(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - this.Value = value; - - if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); - _metadata0.Ratings.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="value"></param> - /// <param name="_metadata0"></param> - public static Rating Create(double value, global::Jellyfin.Data.Entities.Metadata _metadata0) - { - return new Rating(value, _metadata0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Rating")] + public partial class Rating + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Rating() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Rating CreateRatingUnsafe() + { + return new Rating(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="value"></param> + /// <param name="_metadata0"></param> + public Rating(double value, Metadata _metadata0) + { + this.Value = value; + + if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0)); + _metadata0.Ratings.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="value"></param> + /// <param name="_metadata0"></param> + public static Rating Create(double value, Metadata _metadata0) + { + return new Rating(value, _metadata0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + 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) + { + _Id = value; + } + } + } + + /// <summary> + /// Backing field for Value + /// </summary> + protected double _Value; + /// <summary> + /// When provided in a partial class, allows value of Value to be changed before setting. + /// </summary> + partial void SetValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of Value to be changed before returning. + /// </summary> + partial void GetValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [Required] + public double Value + { + get + { + double value = _Value; + GetValue(ref value); + return (_Value = value); + } + set { - _Id = value; + double oldValue = _Value; + SetValue(oldValue, ref value); + if (oldValue != value) + { + _Value = value; + } } - } - } - - /// <summary> - /// Backing field for Value - /// </summary> - protected double _Value; - /// <summary> - /// When provided in a partial class, allows value of Value to be changed before setting. - /// </summary> - partial void SetValue(double oldValue, ref double newValue); - /// <summary> - /// When provided in a partial class, allows value of Value to be changed before returning. - /// </summary> - partial void GetValue(ref double result); - - /// <summary> - /// Required - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Votes + /// </summary> + protected int? _Votes; + /// <summary> + /// When provided in a partial class, allows value of Votes to be changed before setting. + /// </summary> + partial void SetVotes(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of Votes to be changed before returning. + /// </summary> + partial void GetVotes(ref int? result); + + public int? Votes + { + get { - _Value = value; + int? value = _Votes; + GetVotes(ref value); + return (_Votes = value); } - } - } - - /// <summary> - /// Backing field for Votes - /// </summary> - protected int? _Votes; - /// <summary> - /// When provided in a partial class, allows value of Votes to be changed before setting. - /// </summary> - partial void SetVotes(int? oldValue, ref int? newValue); - /// <summary> - /// When provided in a partial class, allows value of Votes to be changed before returning. - /// </summary> - partial void GetVotes(ref int? 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) + set { - _Votes = value; + int? oldValue = _Votes; + SetVotes(oldValue, ref value); + if (oldValue != value) + { + _Votes = value; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - /// <summary> - /// If this is NULL it's the internal user rating. - /// </summary> - public virtual global::Jellyfin.Data.Entities.RatingSource RatingType { get; set; } - - } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + + /// <summary> + /// If this is NULL it's the internal user rating. + /// </summary> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -21,222 +9,229 @@ using System.Runtime.CompilerServices; namespace Jellyfin.Data.Entities { - /// <summary> - /// This is the entity to store review ratings, not age ratings - /// </summary> - public partial class RatingSource - { - partial void Init(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected RatingSource() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static RatingSource CreateRatingSourceUnsafe() - { - return new RatingSource(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="maximumvalue"></param> - /// <param name="minimumvalue"></param> - /// <param name="_rating0"></param> - public RatingSource(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) - { - this.MaximumValue = maximumvalue; - - this.MinimumValue = minimumvalue; - - if (_rating0 == null) throw new ArgumentNullException(nameof(_rating0)); - _rating0.RatingType = this; - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="maximumvalue"></param> - /// <param name="minimumvalue"></param> - /// <param name="_rating0"></param> - public static RatingSource Create(double maximumvalue, double minimumvalue, global::Jellyfin.Data.Entities.Rating _rating0) - { - return new RatingSource(maximumvalue, minimumvalue, _rating0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + /// <summary> + /// This is the entity to store review ratings, not age ratings + /// </summary> + [Table("RatingSource")] + public partial class RatingSource + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected RatingSource() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static RatingSource CreateRatingSourceUnsafe() + { + return new RatingSource(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="maximumvalue"></param> + /// <param name="minimumvalue"></param> + /// <param name="_rating0"></param> + public RatingSource(double maximumvalue, double minimumvalue, Rating _rating0) + { + this.MaximumValue = maximumvalue; + + this.MinimumValue = minimumvalue; + + if (_rating0 == null) throw new ArgumentNullException(nameof(_rating0)); + _rating0.RatingType = this; + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="maximumvalue"></param> + /// <param name="minimumvalue"></param> + /// <param name="_rating0"></param> + public static RatingSource Create(double maximumvalue, double minimumvalue, Rating _rating0) + { + return new RatingSource(maximumvalue, minimumvalue, _rating0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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; + } } - } - } - - /// <summary> - /// Backing field for MaximumValue - /// </summary> - protected double _MaximumValue; - /// <summary> - /// When provided in a partial class, allows value of MaximumValue to be changed before setting. - /// </summary> - partial void SetMaximumValue(double oldValue, ref double newValue); - /// <summary> - /// When provided in a partial class, allows value of MaximumValue to be changed before returning. - /// </summary> - partial void GetMaximumValue(ref double result); - - /// <summary> - /// Required - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Name + { + get { - _MaximumValue = value; + string value = _Name; + GetName(ref value); + return (_Name = value); } - } - } - - /// <summary> - /// Backing field for MinimumValue - /// </summary> - protected double _MinimumValue; - /// <summary> - /// When provided in a partial class, allows value of MinimumValue to be changed before setting. - /// </summary> - partial void SetMinimumValue(double oldValue, ref double newValue); - /// <summary> - /// When provided in a partial class, allows value of MinimumValue to be changed before returning. - /// </summary> - partial void GetMinimumValue(ref double result); - - /// <summary> - /// Required - /// </summary> - [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; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual global::Jellyfin.Data.Entities.MetadataProviderId Source { get; set; } - - } + } + + /// <summary> + /// Backing field for MaximumValue + /// </summary> + protected double _MaximumValue; + /// <summary> + /// When provided in a partial class, allows value of MaximumValue to be changed before setting. + /// </summary> + partial void SetMaximumValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of MaximumValue to be changed before returning. + /// </summary> + partial void GetMaximumValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for MinimumValue + /// </summary> + protected double _MinimumValue; + /// <summary> + /// When provided in a partial class, allows value of MinimumValue to be changed before setting. + /// </summary> + partial void SetMinimumValue(double oldValue, ref double newValue); + /// <summary> + /// When provided in a partial class, allows value of MinimumValue to be changed before returning. + /// </summary> + partial void GetMinimumValue(ref double result); + + /// <summary> + /// Required + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Release() - { - MediaFiles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFile>(); - Chapters = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Chapter>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Release CreateReleaseUnsafe() - { - return new Release(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="name"></param> - /// <param name="_movie0"></param> - /// <param name="_episode1"></param> - /// <param name="_track2"></param> - /// <param name="_customitem3"></param> - /// <param name="_book4"></param> - /// <param name="_photo5"></param> - 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; - - 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 (_track2 == null) throw new ArgumentNullException(nameof(_track2)); - _track2.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 (_photo5 == null) throw new ArgumentNullException(nameof(_photo5)); - _photo5.Releases.Add(this); - - this.MediaFiles = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.MediaFile>(); - this.Chapters = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Chapter>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="name"></param> - /// <param name="_movie0"></param> - /// <param name="_episode1"></param> - /// <param name="_track2"></param> - /// <param name="_customitem3"></param> - /// <param name="_book4"></param> - /// <param name="_photo5"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Id - /// </summary> - internal int _Id; - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before setting. - /// </summary> - partial void SetId(int oldValue, ref int newValue); - /// <summary> - /// When provided in a partial class, allows value of Id to be changed before returning. - /// </summary> - partial void GetId(ref int result); - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [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) + [Table("Release")] + public partial class Release + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Release() + { + MediaFiles = new HashSet<MediaFile>(); + Chapters = new HashSet<Chapter>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Release CreateReleaseUnsafe() + { + return new Release(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="_movie0"></param> + /// <param name="_episode1"></param> + /// <param name="_track2"></param> + /// <param name="_customitem3"></param> + /// <param name="_book4"></param> + /// <param name="_photo5"></param> + 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 (_episode1 == null) throw new ArgumentNullException(nameof(_episode1)); + _episode1.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 (_book4 == null) throw new ArgumentNullException(nameof(_book4)); + _book4.Releases.Add(this); + + if (_photo5 == null) throw new ArgumentNullException(nameof(_photo5)); + _photo5.Releases.Add(this); + + this.MediaFiles = new HashSet<MediaFile>(); + this.Chapters = new HashSet<Chapter>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="_movie0"></param> + /// <param name="_episode1"></param> + /// <param name="_track2"></param> + /// <param name="_customitem3"></param> + /// <param name="_book4"></param> + /// <param name="_photo5"></param> + 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 + *************************************************************************/ + + /// <summary> + /// Backing field for Id + /// </summary> + internal int _Id; + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before setting. + /// </summary> + partial void SetId(int oldValue, ref int newValue); + /// <summary> + /// When provided in a partial class, allows value of Id to be changed before returning. + /// </summary> + partial void GetId(ref int result); + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id + { + get { - _Id = value; + int value = _Id; + GetId(ref value); + return (_Id = value); } - } - } - - /// <summary> - /// Backing field for Name - /// </summary> - protected string _Name; - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before setting. - /// </summary> - partial void SetName(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Name to be changed before returning. - /// </summary> - partial void GetName(ref string result); - - /// <summary> - /// Required, Max length = 1024 - /// </summary> - [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; + } } - } - } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] Timestamp { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual ICollection<global::Jellyfin.Data.Entities.MediaFile> MediaFiles { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.Chapter> Chapters { get; protected set; } - - } + } + + /// <summary> + /// Backing field for Name + /// </summary> + protected string _Name; + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before setting. + /// </summary> + partial void SetName(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Name to be changed before returning. + /// </summary> + partial void GetName(ref string result); + + /// <summary> + /// Required, Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("MediaFile_MediaFiles_Id")] + public virtual ICollection<MediaFile> MediaFiles { get; protected set; } + + [ForeignKey("Chapter_Chapters_Id")] + public virtual ICollection<Chapter> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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<global::Jellyfin.Data.Entities.SeasonMetadata>(); - Episodes = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Episode>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Season CreateSeasonUnsafe() - { - return new Season(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_series0"></param> - 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. - - this.UrlId = urlid; - - if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); - _series0.Seasons.Add(this); - - this.SeasonMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeasonMetadata>(); - this.Episodes = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Episode>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_series0"></param> - public static Season Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.Series _series0) - { - return new Season(urlid, dateadded, _series0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for SeasonNumber - /// </summary> - protected int? _SeasonNumber; - /// <summary> - /// When provided in a partial class, allows value of SeasonNumber to be changed before setting. - /// </summary> - partial void SetSeasonNumber(int? oldValue, ref int? newValue); - /// <summary> - /// When provided in a partial class, allows value of SeasonNumber to be changed before returning. - /// </summary> - 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) + [Table("Season")] + public partial class Season : LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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 HashSet<SeasonMetadata>(); + Episodes = new HashSet<Episode>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Season CreateSeasonUnsafe() + { + return new Season(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_series0"></param> + 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; + + if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.Seasons.Add(this); + + this.SeasonMetadata = new HashSet<SeasonMetadata>(); + this.Episodes = new HashSet<Episode>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_series0"></param> + public static Season Create(Guid urlid, DateTime dateadded, Series _series0) + { + return new Season(urlid, dateadded, _series0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for SeasonNumber + /// </summary> + protected int? _SeasonNumber; + /// <summary> + /// When provided in a partial class, allows value of SeasonNumber to be changed before setting. + /// </summary> + partial void SetSeasonNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of SeasonNumber to be changed before returning. + /// </summary> + partial void GetSeasonNumber(ref int? result); + + public int? SeasonNumber + { + get { - _SeasonNumber = value; + int? value = _SeasonNumber; + GetSeasonNumber(ref value); + return (_SeasonNumber = value); } - } - } - - /************************************************************************* - * Navigation properties - *************************************************************************/ + set + { + int? oldValue = _SeasonNumber; + SetSeasonNumber(oldValue, ref value); + if (oldValue != value) + { + _SeasonNumber = value; + } + } + } - public virtual ICollection<global::Jellyfin.Data.Entities.SeasonMetadata> SeasonMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("SeasonMetadata_SeasonMetadata_Id")] + public virtual ICollection<SeasonMetadata> SeasonMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Episode> Episodes { get; protected set; } + [ForeignKey("Episode_Episodes_Id")] + public virtual ICollection<Episode> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected SeasonMetadata(): base() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static SeasonMetadata CreateSeasonMetadataUnsafe() - { - return new SeasonMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_season0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_season0 == null) throw new ArgumentNullException(nameof(_season0)); - _season0.SeasonMetadata.Add(this); - - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_season0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Outline - /// </summary> - protected string _Outline; - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// </summary> - partial void SetOutline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// </summary> - partial void GetOutline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + [Table("SeasonMetadata")] + public partial class SeasonMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected SeasonMetadata() : base() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static SeasonMetadata CreateSeasonMetadataUnsafe() + { + return new SeasonMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_season0"></param> + 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 (_season0 == null) throw new ArgumentNullException(nameof(_season0)); + _season0.SeasonMetadata.Add(this); + + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_season0"></param> + public static SeasonMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Season _season0) + { + return new SeasonMetadata(title, language, dateadded, datemodified, _season0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get + { + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); + } + set { - _Outline = value; + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected Series(): base() - { - SeriesMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeriesMetadata>(); - Seasons = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Season>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Series CreateSeriesUnsafe() - { - return new Series(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public Series(Guid urlid, DateTime dateadded) - { - this.UrlId = urlid; - - this.SeriesMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.SeriesMetadata>(); - this.Seasons = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Season>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - public static Series Create(Guid urlid, DateTime dateadded) - { - return new Series(urlid, dateadded); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for AirsDayOfWeek - /// </summary> - protected global::Jellyfin.Data.Enums.Weekday? _AirsDayOfWeek; - /// <summary> - /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting. - /// </summary> - partial void SetAirsDayOfWeek(global::Jellyfin.Data.Enums.Weekday? oldValue, ref global::Jellyfin.Data.Enums.Weekday? newValue); - /// <summary> - /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning. - /// </summary> - partial void GetAirsDayOfWeek(ref global::Jellyfin.Data.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) + [Table("Series")] + public partial class Series : LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected Series() : base() + { + SeriesMetadata = new HashSet<SeriesMetadata>(); + Seasons = new HashSet<Season>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Series CreateSeriesUnsafe() + { + return new Series(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public Series(Guid urlid, DateTime dateadded) + { + this.UrlId = urlid; + + this.SeriesMetadata = new HashSet<SeriesMetadata>(); + this.Seasons = new HashSet<Season>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + public static Series Create(Guid urlid, DateTime dateadded) + { + return new Series(urlid, dateadded); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for AirsDayOfWeek + /// </summary> + protected Enums.Weekday? _AirsDayOfWeek; + /// <summary> + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before setting. + /// </summary> + partial void SetAirsDayOfWeek(Enums.Weekday? oldValue, ref Enums.Weekday? newValue); + /// <summary> + /// When provided in a partial class, allows value of AirsDayOfWeek to be changed before returning. + /// </summary> + partial void GetAirsDayOfWeek(ref Enums.Weekday? result); + + public Enums.Weekday? AirsDayOfWeek + { + get { - _AirsDayOfWeek = value; + Enums.Weekday? value = _AirsDayOfWeek; + GetAirsDayOfWeek(ref value); + return (_AirsDayOfWeek = value); } - } - } - - /// <summary> - /// Backing field for AirsTime - /// </summary> - protected DateTimeOffset? _AirsTime; - /// <summary> - /// When provided in a partial class, allows value of AirsTime to be changed before setting. - /// </summary> - partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// <summary> - /// When provided in a partial class, allows value of AirsTime to be changed before returning. - /// </summary> - partial void GetAirsTime(ref DateTimeOffset? result); - - /// <summary> - /// The time the show airs, ignore the date portion - /// </summary> - 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; + } } - } - } - - /// <summary> - /// Backing field for FirstAired - /// </summary> - protected DateTimeOffset? _FirstAired; - /// <summary> - /// When provided in a partial class, allows value of FirstAired to be changed before setting. - /// </summary> - partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); - /// <summary> - /// When provided in a partial class, allows value of FirstAired to be changed before returning. - /// </summary> - partial void GetFirstAired(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) + } + + /// <summary> + /// Backing field for AirsTime + /// </summary> + protected DateTimeOffset? _AirsTime; + /// <summary> + /// When provided in a partial class, allows value of AirsTime to be changed before setting. + /// </summary> + partial void SetAirsTime(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of AirsTime to be changed before returning. + /// </summary> + partial void GetAirsTime(ref DateTimeOffset? result); + + /// <summary> + /// The time the show airs, ignore the date portion + /// </summary> + public DateTimeOffset? AirsTime + { + get { - _FirstAired = value; + DateTimeOffset? value = _AirsTime; + GetAirsTime(ref value); + return (_AirsTime = value); } - } - } - - /************************************************************************* - * Navigation properties - *************************************************************************/ + set + { + DateTimeOffset? oldValue = _AirsTime; + SetAirsTime(oldValue, ref value); + if (oldValue != value) + { + _AirsTime = value; + } + } + } + + /// <summary> + /// Backing field for FirstAired + /// </summary> + protected DateTimeOffset? _FirstAired; + /// <summary> + /// When provided in a partial class, allows value of FirstAired to be changed before setting. + /// </summary> + partial void SetFirstAired(DateTimeOffset? oldValue, ref DateTimeOffset? newValue); + /// <summary> + /// When provided in a partial class, allows value of FirstAired to be changed before returning. + /// </summary> + partial void GetFirstAired(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) + { + _FirstAired = value; + } + } + } - public virtual ICollection<global::Jellyfin.Data.Entities.SeriesMetadata> SeriesMetadata { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("SeriesMetadata_SeriesMetadata_Id")] + public virtual ICollection<SeriesMetadata> SeriesMetadata { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.Season> Seasons { get; protected set; } + [ForeignKey("Season_Seasons_Id")] + public virtual ICollection<Season> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected SeriesMetadata(): base() - { - Networks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static SeriesMetadata CreateSeriesMetadataUnsafe() - { - return new SeriesMetadata(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_series0"></param> - 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; - - if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language)); - this.Language = language; - - if (_series0 == null) throw new ArgumentNullException(nameof(_series0)); - _series0.SeriesMetadata.Add(this); - - this.Networks = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Company>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_series0"></param> - 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); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for Outline - /// </summary> - protected string _Outline; - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before setting. - /// </summary> - partial void SetOutline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Outline to be changed before returning. - /// </summary> - partial void GetOutline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + [Table("SeriesMetadata")] + public partial class SeriesMetadata : Metadata + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected SeriesMetadata() : base() + { + Networks = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static SeriesMetadata CreateSeriesMetadataUnsafe() + { + return new SeriesMetadata(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_series0"></param> + 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 (_series0 == null) throw new ArgumentNullException(nameof(_series0)); + _series0.SeriesMetadata.Add(this); + + this.Networks = new HashSet<Company>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_series0"></param> + public static SeriesMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Series _series0) + { + return new SeriesMetadata(title, language, dateadded, datemodified, _series0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for Outline + /// </summary> + protected string _Outline; + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before setting. + /// </summary> + partial void SetOutline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Outline to be changed before returning. + /// </summary> + partial void GetOutline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [MaxLength(1024)] + [StringLength(1024)] + public string Outline + { + get { - _Outline = value; + string value = _Outline; + GetOutline(ref value); + return (_Outline = value); } - } - } - - /// <summary> - /// Backing field for Plot - /// </summary> - protected string _Plot; - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before setting. - /// </summary> - partial void SetPlot(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Plot to be changed before returning. - /// </summary> - partial void GetPlot(ref string result); - - /// <summary> - /// Max length = 65535 - /// </summary> - [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; + } } - } - } - - /// <summary> - /// Backing field for Tagline - /// </summary> - protected string _Tagline; - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before setting. - /// </summary> - partial void SetTagline(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Tagline to be changed before returning. - /// </summary> - partial void GetTagline(ref string result); - - /// <summary> - /// Max length = 1024 - /// </summary> - [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) + } + + /// <summary> + /// Backing field for Plot + /// </summary> + protected string _Plot; + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before setting. + /// </summary> + partial void SetPlot(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Plot to be changed before returning. + /// </summary> + partial void GetPlot(ref string result); + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string Plot + { + get { - _Tagline = value; + string value = _Plot; + GetPlot(ref value); + return (_Plot = value); } - } - } - - /// <summary> - /// Backing field for Country - /// </summary> - protected string _Country; - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before setting. - /// </summary> - partial void SetCountry(string oldValue, ref string newValue); - /// <summary> - /// When provided in a partial class, allows value of Country to be changed before returning. - /// </summary> - partial void GetCountry(ref string result); - - /// <summary> - /// Max length = 2 - /// </summary> - [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 - *************************************************************************/ + } + + /// <summary> + /// Backing field for Tagline + /// </summary> + protected string _Tagline; + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before setting. + /// </summary> + partial void SetTagline(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Tagline to be changed before returning. + /// </summary> + partial void GetTagline(ref string result); + + /// <summary> + /// Max length = 1024 + /// </summary> + [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; + } + } + } + + /// <summary> + /// Backing field for Country + /// </summary> + protected string _Country; + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before setting. + /// </summary> + partial void SetCountry(string oldValue, ref string newValue); + /// <summary> + /// When provided in a partial class, allows value of Country to be changed before returning. + /// </summary> + partial void GetCountry(ref string result); + + /// <summary> + /// Max length = 2 + /// </summary> + [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; + } + } + } - public virtual ICollection<global::Jellyfin.Data.Entities.Company> Networks { get; protected set; } + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Company_Networks_Id")] + public virtual ICollection<Company> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - 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<global::Jellyfin.Data.Entities.Release>(); - TrackMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.TrackMetadata>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static Track CreateTrackUnsafe() - { - return new Track(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_musicalbum0"></param> - 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. - - this.UrlId = urlid; - - if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); - _musicalbum0.Tracks.Add(this); - - this.Releases = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Release>(); - this.TrackMetadata = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.TrackMetadata>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> - /// <param name="_musicalbum0"></param> - public static Track Create(Guid urlid, DateTime dateadded, global::Jellyfin.Data.Entities.MusicAlbum _musicalbum0) - { - return new Track(urlid, dateadded, _musicalbum0); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// <summary> - /// Backing field for TrackNumber - /// </summary> - protected int? _TrackNumber; - /// <summary> - /// When provided in a partial class, allows value of TrackNumber to be changed before setting. - /// </summary> - partial void SetTrackNumber(int? oldValue, ref int? newValue); - /// <summary> - /// When provided in a partial class, allows value of TrackNumber to be changed before returning. - /// </summary> - 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) + [Table("Track")] + public partial class Track : LibraryItem + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + 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 HashSet<Release>(); + TrackMetadata = new HashSet<TrackMetadata>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static Track CreateTrackUnsafe() + { + return new Track(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_musicalbum0"></param> + 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; + + if (_musicalbum0 == null) throw new ArgumentNullException(nameof(_musicalbum0)); + _musicalbum0.Tracks.Add(this); + + this.Releases = new HashSet<Release>(); + this.TrackMetadata = new HashSet<TrackMetadata>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param> + /// <param name="_musicalbum0"></param> + public static Track Create(Guid urlid, DateTime dateadded, MusicAlbum _musicalbum0) + { + return new Track(urlid, dateadded, _musicalbum0); + } + + /************************************************************************* + * Properties + *************************************************************************/ + + /// <summary> + /// Backing field for TrackNumber + /// </summary> + protected int? _TrackNumber; + /// <summary> + /// When provided in a partial class, allows value of TrackNumber to be changed before setting. + /// </summary> + partial void SetTrackNumber(int? oldValue, ref int? newValue); + /// <summary> + /// When provided in a partial class, allows value of TrackNumber to be changed before returning. + /// </summary> + partial void GetTrackNumber(ref int? result); + + public int? TrackNumber + { + get + { + int? value = _TrackNumber; + GetTrackNumber(ref value); + return (_TrackNumber = value); + } + set { - _TrackNumber = value; + int? oldValue = _TrackNumber; + SetTrackNumber(oldValue, ref value); + if (oldValue != value) + { + _TrackNumber = value; + } } - } - } + } - /************************************************************************* - * Navigation properties - *************************************************************************/ + /************************************************************************* + * Navigation properties + *************************************************************************/ - public virtual ICollection<global::Jellyfin.Data.Entities.Release> Releases { get; protected set; } + [ForeignKey("Release_Releases_Id")] + public virtual ICollection<Release> Releases { get; protected set; } - public virtual ICollection<global::Jellyfin.Data.Entities.TrackMetadata> TrackMetadata { get; protected set; } + [ForeignKey("TrackMetadata_TrackMetadata_Id")] + public virtual ICollection<TrackMetadata> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected TrackMetadata(): base() - { - Init(); - } + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected TrackMetadata() : base() + { + Init(); + } - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static TrackMetadata CreateTrackMetadataUnsafe() - { - return new TrackMetadata(); - } + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static TrackMetadata CreateTrackMetadataUnsafe() + { + return new TrackMetadata(); + } - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_track0"></param> - 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; + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_track0"></param> + 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(); + } - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="title">The title or name of the object</param> - /// <param name="language">ISO-639-3 3-character language codes</param> - /// <param name="_track0"></param> - 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); - } + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="title">The title or name of the object</param> + /// <param name="language">ISO-639-3 3-character language codes</param> + /// <param name="_track0"></param> + 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected User() - { - Groups = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Group>(); - Permissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); - ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); - Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); - - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static User CreateUserUnsafe() - { - return new User(); - } - - /// <summary> - /// Public constructor with required data - /// </summary> - /// <param name="username"></param> - /// <param name="mustupdatepassword"></param> - /// <param name="audiolanguagepreference"></param> - /// <param name="authenticationproviderid"></param> - /// <param name="invalidloginattemptcount"></param> - /// <param name="subtitlemode"></param> - /// <param name="playdefaultaudiotrack"></param> - 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; - - if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference)); - this.AudioLanguagePreference = audiolanguagepreference; - - if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid)); - this.AuthenticationProviderId = authenticationproviderid; - - this.InvalidLoginAttemptCount = invalidloginattemptcount; - - if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode)); - this.SubtitleMode = subtitlemode; - - this.PlayDefaultAudioTrack = playdefaultaudiotrack; - - this.Groups = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Group>(); - this.Permissions = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Permission>(); - this.ProviderMappings = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.ProviderMapping>(); - this.Preferences = new System.Collections.Generic.HashSet<global::Jellyfin.Data.Entities.Preference>(); - - Init(); - } - - /// <summary> - /// Static create function (for use in LINQ queries, etc.) - /// </summary> - /// <param name="username"></param> - /// <param name="mustupdatepassword"></param> - /// <param name="audiolanguagepreference"></param> - /// <param name="authenticationproviderid"></param> - /// <param name="invalidloginattemptcount"></param> - /// <param name="subtitlemode"></param> - /// <param name="playdefaultaudiotrack"></param> - 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 - *************************************************************************/ - - /// <summary> - /// Identity, Indexed, Required - /// </summary> - [Key] - [Required] - public Guid Id { get; protected set; } - - /// <summary> - /// Required - /// </summary> - [ConcurrencyCheck] - [Required] - public byte[] LastLoginTimestamp { get; set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string Username { get; set; } - - /// <summary> - /// Max length = 65535 - /// </summary> - [MaxLength(65535)] - [StringLength(65535)] - public string Password { get; set; } - - /// <summary> - /// Required - /// </summary> - [Required] - public bool MustUpdatePassword { get; set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string AudioLanguagePreference { get; set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string AuthenticationProviderId { get; set; } - - /// <summary> - /// Max length = 65535 - /// </summary> - [MaxLength(65535)] - [StringLength(65535)] - public string GroupedFolders { get; set; } - - /// <summary> - /// Required - /// </summary> - [Required] - public int InvalidLoginAttemptCount { get; set; } - - /// <summary> - /// Max length = 65535 - /// </summary> - [MaxLength(65535)] - [StringLength(65535)] - public string LatestItemExcludes { get; set; } - - public int? LoginAttemptsBeforeLockout { get; set; } - - /// <summary> - /// Max length = 65535 - /// </summary> - [MaxLength(65535)] - [StringLength(65535)] - public string MyMediaExcludes { get; set; } - - /// <summary> - /// Max length = 65535 - /// </summary> - [MaxLength(65535)] - [StringLength(65535)] - public string OrderedViews { get; set; } - - /// <summary> - /// Required, Max length = 255 - /// </summary> - [Required] - [MaxLength(255)] - [StringLength(255)] - public string SubtitleMode { get; set; } - - /// <summary> - /// Required - /// </summary> - [Required] - public bool PlayDefaultAudioTrack { get; set; } - - /// <summary> - /// Max length = 255 - /// </summary> - [MaxLength(255)] - [StringLength(255)] - public string SubtitleLanguagePrefernce { get; set; } - - public bool? DisplayMissingEpisodes { get; set; } - - public bool? DisplayCollectionsView { get; set; } - - public bool? HidePlayedInLatest { get; set; } - - public bool? RememberAudioSelections { get; set; } - - public bool? RememberSubtitleSelections { get; set; } - - public bool? EnableNextEpisodeAutoPlay { get; set; } - - public bool? EnableUserPreferenceAccess { get; set; } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - public virtual ICollection<global::Jellyfin.Data.Entities.Group> Groups { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.Permission> Permissions { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; protected set; } - - public virtual ICollection<global::Jellyfin.Data.Entities.Preference> Preferences { get; protected set; } - - } + [Table("User")] + public partial class User + { + partial void Init(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected User() + { + Groups = new HashSet<Group>(); + Permissions = new HashSet<Permission>(); + ProviderMappings = new HashSet<ProviderMapping>(); + Preferences = new HashSet<Preference>(); + + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static User CreateUserUnsafe() + { + return new User(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="username"></param> + /// <param name="mustupdatepassword"></param> + /// <param name="audiolanguagepreference"></param> + /// <param name="authenticationproviderid"></param> + /// <param name="invalidloginattemptcount"></param> + /// <param name="subtitlemode"></param> + /// <param name="playdefaultaudiotrack"></param> + 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; + + if (string.IsNullOrEmpty(audiolanguagepreference)) throw new ArgumentNullException(nameof(audiolanguagepreference)); + this.AudioLanguagePreference = audiolanguagepreference; + + if (string.IsNullOrEmpty(authenticationproviderid)) throw new ArgumentNullException(nameof(authenticationproviderid)); + this.AuthenticationProviderId = authenticationproviderid; + + this.InvalidLoginAttemptCount = invalidloginattemptcount; + + if (string.IsNullOrEmpty(subtitlemode)) throw new ArgumentNullException(nameof(subtitlemode)); + this.SubtitleMode = subtitlemode; + + this.PlayDefaultAudioTrack = playdefaultaudiotrack; + + this.Groups = new HashSet<Group>(); + this.Permissions = new HashSet<Permission>(); + this.ProviderMappings = new HashSet<ProviderMapping>(); + this.Preferences = new HashSet<Preference>(); + + Init(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="username"></param> + /// <param name="mustupdatepassword"></param> + /// <param name="audiolanguagepreference"></param> + /// <param name="authenticationproviderid"></param> + /// <param name="invalidloginattemptcount"></param> + /// <param name="subtitlemode"></param> + /// <param name="playdefaultaudiotrack"></param> + 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 + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string Username { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string Password { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool MustUpdatePassword { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AudioLanguagePreference { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string AuthenticationProviderId { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string GroupedFolders { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public int InvalidLoginAttemptCount { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string LatestItemExcludes { get; set; } + + public int? LoginAttemptsBeforeLockout { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string MyMediaExcludes { get; set; } + + /// <summary> + /// Max length = 65535 + /// </summary> + [MaxLength(65535)] + [StringLength(65535)] + public string OrderedViews { get; set; } + + /// <summary> + /// Required, Max length = 255 + /// </summary> + [Required] + [MaxLength(255)] + [StringLength(255)] + public string SubtitleMode { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public bool PlayDefaultAudioTrack { get; set; } + + /// <summary> + /// Max length = 255 + /// </summary> + [MaxLength(255)] + [StringLength(255)] + public string SubtitleLanguagePrefernce { get; set; } + + public bool? DisplayMissingEpisodes { get; set; } + + public bool? DisplayCollectionsView { get; set; } + + public bool? HidePlayedInLatest { get; set; } + + public bool? RememberAudioSelections { get; set; } + + public bool? RememberSubtitleSelections { get; set; } + + public bool? EnableNextEpisodeAutoPlay { get; set; } + + public bool? EnableUserPreferenceAccess { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [ConcurrencyCheck] + [Required] + public uint RowVersion { get; set; } + + public void OnSavingChanges() + { + RowVersion++; + } + + /************************************************************************* + * Navigation properties + *************************************************************************/ + [ForeignKey("Group_Groups_Id")] + public virtual ICollection<Group> Groups { get; protected set; } + + [ForeignKey("Permission_Permissions_Id")] + public virtual ICollection<Permission> Permissions { get; protected set; } + + [ForeignKey("ProviderMapping_ProviderMappings_Id")] + public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; } + + [ForeignKey("Preference_Preferences_Id")] + public virtual ICollection<Preference> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - 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 <barronpm@gmail.com> Date: Sat, 2 May 2020 18:32:22 -0400 Subject: [PATCH 302/411] 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<CameraImageUploadInfo> e) + private async void OnCameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> 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<User> e) + private async void OnUserLockedOut(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> 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<AuthenticationResult> e) + private async void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> 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<AuthenticationRequest> e) + private async void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> 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<User> e) + private async void OnUserPolicyUpdated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> 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<User> e) + private async void OnUserDeleted(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> 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<User> e) + private async void OnUserPasswordChanged(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> 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<User> e) + private async void OnUserCreated(object sender, GenericEventArgs<MediaBrowser.Controller.Entities.User> 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<IPlugin> e) + private async void OnPluginUninstalled(object sender, GenericEventArgs<IPlugin> 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<VersionInfo> e) + private async void OnPluginInstalled(object sender, GenericEventArgs<VersionInfo> 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); /// <inheritdoc /> 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 -{ - /// <summary> - /// The activity log manager. - /// </summary> - public class ActivityManager : IActivityManager - { - private readonly IActivityRepository _repo; - private readonly IUserManager _userManager; - - /// <summary> - /// Initializes a new instance of the <see cref="ActivityManager"/> class. - /// </summary> - /// <param name="repo">The activity repository.</param> - /// <param name="userManager">The user manager.</param> - public ActivityManager(IActivityRepository repo, IUserManager userManager) - { - _repo = repo; - _userManager = userManager; - } - - /// <inheritdoc /> - public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; - - public void Create(ActivityLogEntry entry) - { - entry.Date = DateTime.UtcNow; - - _repo.Create(entry); - - EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry)); - } - - /// <inheritdoc /> - public QueryResult<ActivityLogEntry> 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; - } - - /// <inheritdoc /> - public QueryResult<ActivityLogEntry> 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 -{ - /// <summary> - /// The activity log repository. - /// </summary> - 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; - - /// <summary> - /// Initializes a new instance of the <see cref="ActivityRepository"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="appPaths">The server application paths.</param> - /// <param name="fileSystem">The filesystem.</param> - public ActivityRepository(ILogger<ActivityRepository> logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) - : base(logger) - { - DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db"); - _fileSystem = fileSystem; - } - - /// <summary> - /// Initializes the <see cref="ActivityRepository"/>. - /// </summary> - 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"); - } - } - - /// <inheritdoc /> - 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); - } - - /// <summary> - /// Adds the provided <see cref="ActivityLogEntry"/> to this repository. - /// </summary> - /// <param name="entry">The activity log entry.</param> - /// <exception cref="ArgumentNullException">If entry is null.</exception> - 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); - } - - /// <inheritdoc /> - public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) - { - var commandText = BaseActivitySelectText; - var whereClauses = new List<string>(); - - 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<ActivityLogEntry>(); - var result = new QueryResult<ActivityLogEntry>(); - - 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<IResultSetValue> 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<LogLevel>(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<JellyfinDb>( + options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), + ServiceLifetime.Transient); + + serviceCollection.AddSingleton<JellyfinDbProvider>(); + serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton<TvdbClientManager>(); @@ -663,7 +671,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); - serviceCollection.AddSingleton<IActivityRepository, ActivityRepository>(); serviceCollection.AddSingleton<IActivityManager, ActivityManager>(); serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>(); @@ -696,7 +703,6 @@ namespace Emby.Server.Implementations ((SqliteDisplayPreferencesRepository)Resolve<IDisplayPreferencesRepository>()).Initialize(); ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize(); ((SqliteUserRepository)Resolve<IUserRepository>()).Initialize(); - ((ActivityRepository)Resolve<IActivityRepository>()).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 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> <PropertyGroup> @@ -9,6 +9,7 @@ <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" /> <ProjectReference Include="..\Jellyfin.Api\Jellyfin.Api.csproj" /> + <ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> @@ -50,7 +51,7 @@ </ItemGroup> <PropertyGroup> - <TargetFramework>netstandard2.1</TargetFramework> + <TargetFramework>netcoreapp3.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore; - -namespace Jellyfin.Data.DbContexts -{ - /// <inheritdoc/> - public partial class Jellyfin : DbContext - { - #region DbSets - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Artwork> Artwork { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Book> Books { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.BookMetadata> BookMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Chapter> Chapters { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Collection> Collections { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CollectionItem> CollectionItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Company> Companies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CompanyMetadata> CompanyMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItem> CustomItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItemMetadata> CustomItemMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Episode> Episodes { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.EpisodeMetadata> EpisodeMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Genre> Genres { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Group> Groups { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Library> Libraries { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryItem> LibraryItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryRoot> LibraryRoot { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFile> MediaFiles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFileStream> MediaFileStream { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Metadata> Metadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProvider> MetadataProviders { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProviderId> MetadataProviderIds { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Movie> Movies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MovieMetadata> MovieMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbum> MusicAlbums { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata> MusicAlbumMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Permission> Permissions { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Person> People { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PersonRole> PersonRoles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Photo> Photo { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PhotoMetadata> PhotoMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Preference> Preferences { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Rating> Ratings { get; set; } - - /// <summary> - /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to - /// store review ratings, not age ratings - /// </summary> - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.RatingSource> RatingSources { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Release> Releases { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Season> Seasons { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeasonMetadata> SeasonMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Series> Series { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeriesMetadata> SeriesMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Track> Tracks { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.TrackMetadata> TrackMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.User> Users { get; set; } - #endregion DbSets - - /// <summary> - /// Default connection string - /// </summary> - public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; - - /// <inheritdoc /> - public Jellyfin(DbContextOptions<Jellyfin> options) : base(options) - { - } - - partial void CustomInit(DbContextOptionsBuilder optionsBuilder); - - /// <inheritdoc /> - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - CustomInit(optionsBuilder); - } - - partial void OnModelCreatingImpl(ModelBuilder modelBuilder); - partial void OnModelCreatedImpl(ModelBuilder modelBuilder); - - /// <inheritdoc /> - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - OnModelCreatingImpl(modelBuilder); - - modelBuilder.HasDefaultSchema("jellyfin"); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .ToTable("Artwork") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>().HasIndex(t => t.Kind); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() - .HasMany(x => x.BookMetadata) - .WithOne() - .HasForeignKey("BookMetadata_BookMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() - .Property(t => t.ISBN) - .HasField("_ISBN") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() - .HasMany(x => x.Publishers) - .WithOne() - .HasForeignKey("Company_Publishers_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .ToTable("Chapter") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.TimeStart) - .IsRequired() - .HasField("_TimeStart") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.TimeEnd) - .HasField("_TimeEnd") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .ToTable("Collection") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .HasMany(x => x.CollectionItem) - .WithOne() - .HasForeignKey("CollectionItem_CollectionItem_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .ToTable("CollectionItem") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.LibraryItem) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("LibraryItem_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.Next) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Next_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.Previous) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Previous_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .ToTable("Company") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .HasMany(x => x.CompanyMetadata) - .WithOne() - .HasForeignKey("CompanyMetadata_CompanyMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .HasOne(x => x.Parent) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.Company>("Company_Parent_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Description) - .HasMaxLength(65535) - .HasField("_Description") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Headquarters) - .HasMaxLength(255) - .HasField("_Headquarters") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Homepage) - .HasMaxLength(1024) - .HasField("_Homepage") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() - .HasMany(x => x.CustomItemMetadata) - .WithOne() - .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .Property(t => t.EpisodeNumber) - .HasField("_EpisodeNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .HasMany(x => x.EpisodeMetadata) - .WithOne() - .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .ToTable("Genre") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>().HasIndex(t => t.Name) - .IsUnique(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .ToTable("Groups") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.GroupPermissions) - .WithOne() - .HasForeignKey("Permission_GroupPermissions_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.Preferences) - .WithOne() - .HasForeignKey("Preference_Preferences_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .ToTable("Library") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .ToTable("LibraryItem") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>().HasIndex(t => t.UrlId) - .IsUnique(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .HasOne(x => x.LibraryRoot) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.LibraryItem>("LibraryRoot_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .ToTable("LibraryRoot") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.NetworkPath) - .HasMaxLength(65535) - .HasField("_NetworkPath") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .HasOne(x => x.Library) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.LibraryRoot>("Library_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .ToTable("MediaFile") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .HasMany(x => x.MediaFileStreams) - .WithOne() - .HasForeignKey("MediaFileStream_MediaFileStreams_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .ToTable("MediaFileStream") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.StreamNumber) - .IsRequired() - .HasField("_StreamNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .ToTable("Metadata") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Title) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Title") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.OriginalTitle) - .HasMaxLength(1024) - .HasField("_OriginalTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.SortTitle) - .HasMaxLength(1024) - .HasField("_SortTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.ReleaseDate) - .HasField("_ReleaseDate") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.PersonRoles) - .WithOne() - .HasForeignKey("PersonRole_PersonRoles_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Genres) - .WithOne() - .HasForeignKey("Genre_Genres_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Artwork) - .WithOne() - .HasForeignKey("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Ratings) - .WithOne() - .HasForeignKey("Rating_Ratings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .ToTable("MetadataProvider") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .ToTable("MetadataProviderId") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.ProviderId) - .HasMaxLength(255) - .IsRequired() - .HasField("_ProviderId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .HasOne(x => x.MetadataProvider) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.MetadataProviderId>("MetadataProvider_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() - .HasMany(x => x.MovieMetadata) - .WithOne() - .HasForeignKey("MovieMetadata_MovieMetadata_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .HasMany(x => x.Studios) - .WithOne() - .HasForeignKey("Company_Studios_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() - .HasMany(x => x.MusicAlbumMetadata) - .WithOne() - .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() - .HasMany(x => x.Tracks) - .WithOne() - .HasForeignKey("Track_Tracks_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.Barcode) - .HasMaxLength(255) - .HasField("_Barcode") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.LabelNumber) - .HasMaxLength(255) - .HasField("_LabelNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .HasMany(x => x.Labels) - .WithOne() - .HasForeignKey("Company_Labels_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .ToTable("Permissions") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Value) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .ToTable("Person") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.SourceId) - .HasMaxLength(255) - .HasField("_SourceId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .ToTable("PersonRole") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Role) - .HasMaxLength(1024) - .HasField("_Role") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Type) - .IsRequired() - .HasField("_Type") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasOne(x => x.Person) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Person_Id") - .IsRequired() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasOne(x => x.Artwork) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() - .HasMany(x => x.PhotoMetadata) - .WithOne() - .HasForeignKey("PhotoMetadata_PhotoMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .ToTable("Preferences") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Kind) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Value) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .ToTable("ProviderMappings") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderName) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderSecrets) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderData) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .ToTable("Rating") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Value) - .IsRequired() - .HasField("_Value") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Votes) - .HasField("_Votes") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .HasOne(x => x.RatingType) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.Rating>("RatingSource_RatingType_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .ToTable("RatingType") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.MaximumValue) - .IsRequired() - .HasField("_MaximumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.MinimumValue) - .IsRequired() - .HasField("_MinimumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .HasOne(x => x.Source) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.RatingSource>("MetadataProviderId_Source_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .ToTable("Release") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .HasMany(x => x.MediaFiles) - .WithOne() - .HasForeignKey("MediaFile_MediaFiles_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .HasMany(x => x.Chapters) - .WithOne() - .HasForeignKey("Chapter_Chapters_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .Property(t => t.SeasonNumber) - .HasField("_SeasonNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .HasMany(x => x.SeasonMetadata) - .WithOne() - .HasForeignKey("SeasonMetadata_SeasonMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .HasMany(x => x.Episodes) - .WithOne() - .HasForeignKey("Episode_Episodes_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeasonMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.AirsDayOfWeek) - .HasField("_AirsDayOfWeek") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.AirsTime) - .HasField("_AirsTime") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.FirstAired) - .HasField("_FirstAired") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .HasMany(x => x.SeriesMetadata) - .WithOne() - .HasForeignKey("SeriesMetadata_SeriesMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .HasMany(x => x.Seasons) - .WithOne() - .HasForeignKey("Season_Seasons_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .HasMany(x => x.Networks) - .WithOne() - .HasForeignKey("Company_Networks_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .Property(t => t.TrackNumber) - .HasField("_TrackNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .HasMany(x => x.TrackMetadata) - .WithOne() - .HasForeignKey("TrackMetadata_TrackMetadata_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .ToTable("Users") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.LastLoginTimestamp) - .IsRequired() - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Username) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Password) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.MustUpdatePassword) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.AudioLanguagePreference) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.AuthenticationProviderId) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.GroupedFolders) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.InvalidLoginAttemptCount) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.LatestItemExcludes) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.MyMediaExcludes) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.OrderedViews) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.SubtitleMode) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.PlayDefaultAudioTrack) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.SubtitleLanguagePrefernce) - .HasMaxLength(255); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.Groups) - .WithOne() - .HasForeignKey("Group_Groups_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.Permissions) - .WithOne() - .HasForeignKey("Permission_Permissions_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .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(); + + /// <summary> + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected ActivityLog() + { + Init(); + } + + /// <summary> + /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. + /// </summary> + public static ActivityLog CreateActivityLogUnsafe() + { + return new ActivityLog(); + } + + /// <summary> + /// Public constructor with required data + /// </summary> + /// <param name="name"></param> + /// <param name="type"></param> + /// <param name="userid"></param> + /// <param name="datecreated"></param> + /// <param name="logseverity"></param> + 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(); + } + + /// <summary> + /// Static create function (for use in LINQ queries, etc.) + /// </summary> + /// <param name="name"></param> + /// <param name="type"></param> + /// <param name="userid"></param> + /// <param name="datecreated"></param> + /// <param name="logseverity"></param> + 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 + *************************************************************************/ + + /// <summary> + /// Identity, Indexed, Required + /// </summary> + [Key] + [Required] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; protected set; } + + /// <summary> + /// Required, Max length = 512 + /// </summary> + [Required] + [MaxLength(512)] + [StringLength(512)] + public string Name { get; set; } + + /// <summary> + /// Max length = 512 + /// </summary> + [MaxLength(512)] + [StringLength(512)] + public string Overview { get; set; } + + /// <summary> + /// Max length = 512 + /// </summary> + [MaxLength(512)] + [StringLength(512)] + public string ShortOverview { get; set; } + + /// <summary> + /// Required, Max length = 256 + /// </summary> + [Required] + [MaxLength(256)] + [StringLength(256)] + public string Type { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public Guid UserId { get; set; } + + /// <summary> + /// Max length = 256 + /// </summary> + [MaxLength(256)] + [StringLength(256)] + public string ItemId { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public DateTime DateCreated { get; set; } + + /// <summary> + /// Required + /// </summary> + [Required] + public Microsoft.Extensions.Logging.LogLevel LogSeverity { get; set; } + + /// <summary> + /// Required, ConcurrenyToken + /// </summary> + [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 @@ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> - <TargetFramework>netstandard2.0</TargetFramework> + <TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + + <!-- Code analysers--> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + </ItemGroup> + <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.4" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> </ItemGroup> </Project> 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 +{ + /// <summary> + /// Manages the storage and retrieval of <see cref="ActivityLog"/> instances. + /// </summary> + public class ActivityManager : IActivityManager + { + private JellyfinDbProvider _provider; + + /// <summary> + /// Initializes a new instance of the <see cref="ActivityManager"/> class. + /// </summary> + /// <param name="provider">The Jellyfin database provider.</param> + public ActivityManager(JellyfinDbProvider provider) + { + _provider = provider; + } + + /// <inheritdoc/> + public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; + + /// <inheritdoc/> + public void Create(ActivityLog entry) + { + using var dbContext = _provider.CreateContext(); + dbContext.ActivityLogs.Add(entry); + dbContext.SaveChanges(); + + EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(ConvertToOldModel(entry))); + } + + /// <inheritdoc/> + 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<ActivityLogEntry>(ConvertToOldModel(entry))); + } + + /// <inheritdoc/> + public QueryResult<ActivityLogEntry> GetPagedResult( + Func<IQueryable<ActivityLog>, IEnumerable<ActivityLog>> 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<ActivityLogEntry>() + { + Items = list, + TotalRecordCount = list.Count + }; + } + + /// <inheritdoc/> + public QueryResult<ActivityLogEntry> 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 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + </PropertyGroup> + + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + + <!-- Code analysers--> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + </ItemGroup> + + <ItemGroup> + <Compile Include="..\SharedVersion.cs" /> + <Compile Remove="Migrations\20200430214405_InitialSchema.cs" /> + <Compile Remove="Migrations\20200430214405_InitialSchema.Designer.cs" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" /> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + </ItemGroup> + +</Project> 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 +{ + /// <inheritdoc/> + public partial class JellyfinDb : DbContext + { + public virtual DbSet<ActivityLog> ActivityLogs { get; set; } + public virtual DbSet<Artwork> Artwork { get; set; } + public virtual DbSet<Book> Books { get; set; } + public virtual DbSet<BookMetadata> BookMetadata { get; set; } + public virtual DbSet<Chapter> Chapters { get; set; } + public virtual DbSet<Collection> Collections { get; set; } + public virtual DbSet<CollectionItem> CollectionItems { get; set; } + public virtual DbSet<Company> Companies { get; set; } + public virtual DbSet<CompanyMetadata> CompanyMetadata { get; set; } + public virtual DbSet<CustomItem> CustomItems { get; set; } + public virtual DbSet<CustomItemMetadata> CustomItemMetadata { get; set; } + public virtual DbSet<Episode> Episodes { get; set; } + public virtual DbSet<EpisodeMetadata> EpisodeMetadata { get; set; } + public virtual DbSet<Genre> Genres { get; set; } + public virtual DbSet<Group> Groups { get; set; } + public virtual DbSet<Library> Libraries { get; set; } + public virtual DbSet<LibraryItem> LibraryItems { get; set; } + public virtual DbSet<LibraryRoot> LibraryRoot { get; set; } + public virtual DbSet<MediaFile> MediaFiles { get; set; } + public virtual DbSet<MediaFileStream> MediaFileStream { get; set; } + public virtual DbSet<Metadata> Metadata { get; set; } + public virtual DbSet<MetadataProvider> MetadataProviders { get; set; } + public virtual DbSet<MetadataProviderId> MetadataProviderIds { get; set; } + public virtual DbSet<Movie> Movies { get; set; } + public virtual DbSet<MovieMetadata> MovieMetadata { get; set; } + public virtual DbSet<MusicAlbum> MusicAlbums { get; set; } + public virtual DbSet<MusicAlbumMetadata> MusicAlbumMetadata { get; set; } + public virtual DbSet<Permission> Permissions { get; set; } + public virtual DbSet<Person> People { get; set; } + public virtual DbSet<PersonRole> PersonRoles { get; set; } + public virtual DbSet<Photo> Photo { get; set; } + public virtual DbSet<PhotoMetadata> PhotoMetadata { get; set; } + public virtual DbSet<Preference> Preferences { get; set; } + public virtual DbSet<ProviderMapping> ProviderMappings { get; set; } + public virtual DbSet<Rating> Ratings { get; set; } + + /// <summary> + /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to + /// store review ratings, not age ratings + /// </summary> + public virtual DbSet<RatingSource> RatingSources { get; set; } + public virtual DbSet<Release> Releases { get; set; } + public virtual DbSet<Season> Seasons { get; set; } + public virtual DbSet<SeasonMetadata> SeasonMetadata { get; set; } + public virtual DbSet<Series> Series { get; set; } + public virtual DbSet<SeriesMetadata> SeriesMetadata { get; set; } + public virtual DbSet<Track> Tracks { get; set; } + public virtual DbSet<TrackMetadata> TrackMetadata { get; set; } + public virtual DbSet<User> Users { get; set; } + + /// <summary> + /// Gets or sets the default connection string. + /// </summary> + public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; + + /// <inheritdoc /> + public JellyfinDb(DbContextOptions<JellyfinDb> options) : base(options) + { + } + + partial void CustomInit(DbContextOptionsBuilder optionsBuilder); + + /// <inheritdoc /> + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + CustomInit(optionsBuilder); + } + + partial void OnModelCreatingImpl(ModelBuilder modelBuilder); + partial void OnModelCreatedImpl(ModelBuilder modelBuilder); + + /// <inheritdoc /> + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + OnModelCreatingImpl(modelBuilder); + + modelBuilder.HasDefaultSchema("jellyfin"); + + modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind); + + modelBuilder.Entity<Genre>().HasIndex(t => t.Name) + .IsUnique(); + + modelBuilder.Entity<LibraryItem>().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 +{ + /// <summary> + /// Factory class for generating new <see cref="JellyfinDb"/> instances. + /// </summary> + public class JellyfinDbProvider + { + private readonly IServiceProvider _serviceProvider; + + /// <summary> + /// Initializes a new instance of the <see cref="JellyfinDbProvider"/> class. + /// </summary> + /// <param name="serviceProvider">The application's service provider.</param> + public JellyfinDbProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + serviceProvider.GetService<JellyfinDb>().Database.Migrate(); + } + + /// <summary> + /// Creates a new <see cref="JellyfinDb"/> context. + /// </summary> + /// <returns>The newly created context.</returns> + public JellyfinDb CreateContext() + { + return _serviceProvider.GetService<JellyfinDb>(); + } + } +} 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 + +// <auto-generated /> +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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLog"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Chapter_Chapters_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<long?>("TimeEnd") + .HasColumnType("INTEGER"); + + b.Property<long>("TimeStart") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Chapter_Chapters_Id"); + + b.ToTable("Chapter"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_CollectionItem_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_Next_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_Previous_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("LibraryItem_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Labels_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Networks_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Parent_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Publishers_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Studios_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Group_Groups_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Group_Groups_Id"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<string>("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<int?>("LibraryRoot_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<Guid>("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryRoot_Id"); + + b.HasIndex("UrlId") + .IsUnique(); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator<string>("Discriminator").HasValue("LibraryItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Library_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("NetworkPath") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Library_Id"); + + b.ToTable("LibraryRoot"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("MediaFile_MediaFiles_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFile_MediaFiles_Id"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("MediaFileStream_MediaFileStreams_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<int>("StreamNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileStream_MediaFileStreams_Id"); + + b.ToTable("MediaFileStream"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<DateTime>("DateModified") + .HasColumnType("TEXT"); + + b.Property<string>("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property<string>("OriginalTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<DateTimeOffset?>("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SortTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator<string>("Discriminator").HasValue("Metadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MetadataProvider"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("MetadataProviderId_Sources_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("MetadataProvider_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("Permission_GroupPermissions_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Permission_Permissions_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<bool>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<DateTime>("DateModified") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SourceId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<Guid>("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Artwork_Artwork_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Person_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Role") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<int>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("Preference_Preferences_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderData") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("ProviderMapping_ProviderMappings_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderName") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("ProviderSecrets") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderMapping_ProviderMappings_Id"); + + b.ToTable("ProviderMapping"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("RatingSource_RatingType_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<double>("Value") + .HasColumnType("REAL"); + + b.Property<int?>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<double>("MaximumValue") + .HasColumnType("REAL"); + + b.Property<int?>("MetadataProviderId_Source_Id") + .HasColumnType("INTEGER"); + + b.Property<double>("MinimumValue") + .HasColumnType("REAL"); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Source_Id"); + + b.ToTable("RatingSource"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<int?>("Release_Releases_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Release_Releases_Id"); + + b.ToTable("Release"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudioLanguagePreference") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<bool?>("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property<bool?>("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property<bool?>("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property<bool?>("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property<string>("GroupedFolders") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<bool?>("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property<int>("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property<string>("LatestItemExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property<bool>("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property<string>("MyMediaExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("OrderedViews") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<bool>("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property<bool?>("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property<bool?>("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SubtitleLanguagePrefernce") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("SubtitleMode") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("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<int?>("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("AirsDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property<DateTimeOffset?>("AirsTime") + .HasColumnType("TEXT"); + + b.Property<DateTimeOffset?>("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<int?>("TrackNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("BookMetadata_BookMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<long?>("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<int?>("CompanyMetadata_CompanyMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("Description") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Headquarters") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("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<int?>("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<int?>("EpisodeMetadata_EpisodeMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("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<string>("Country") + .HasColumnName("MovieMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<int?>("MovieMetadata_MovieMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Outline") + .HasColumnName("MovieMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnName("MovieMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("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<string>("Barcode") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("Country") + .HasColumnName("MusicAlbumMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("LabelNumber") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<int?>("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<int?>("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<string>("Outline") + .HasColumnName("SeasonMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<int?>("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<string>("Country") + .HasColumnName("SeriesMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("Outline") + .HasColumnName("SeriesMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnName("SeriesMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("SeriesMetadata_SeriesMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("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<int?>("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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 512, nullable: false), + Overview = table.Column<string>(maxLength: 512, nullable: true), + ShortOverview = table.Column<string>(maxLength: 512, nullable: true), + Type = table.Column<string>(maxLength: 256, nullable: false), + UserId = table.Column<Guid>(nullable: false), + ItemId = table.Column<string>(maxLength: 256, nullable: true), + DateCreated = table.Column<DateTime>(nullable: false), + LogSeverity = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ActivityLog", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Collection", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: true), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Collection", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Library", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: false), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Library", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "MetadataProvider", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: false), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MetadataProvider", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Person", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UrlId = table.Column<Guid>(nullable: false), + Name = table.Column<string>(maxLength: 1024, nullable: false), + SourceId = table.Column<string>(maxLength: 255, nullable: true), + DateAdded = table.Column<DateTime>(nullable: false), + DateModified = table.Column<DateTime>(nullable: false), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Person", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "User", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Username = table.Column<string>(maxLength: 255, nullable: false), + Password = table.Column<string>(maxLength: 65535, nullable: true), + MustUpdatePassword = table.Column<bool>(nullable: false), + AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: false), + AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false), + GroupedFolders = table.Column<string>(maxLength: 65535, nullable: true), + InvalidLoginAttemptCount = table.Column<int>(nullable: false), + LatestItemExcludes = table.Column<string>(maxLength: 65535, nullable: true), + LoginAttemptsBeforeLockout = table.Column<int>(nullable: true), + MyMediaExcludes = table.Column<string>(maxLength: 65535, nullable: true), + OrderedViews = table.Column<string>(maxLength: 65535, nullable: true), + SubtitleMode = table.Column<string>(maxLength: 255, nullable: false), + PlayDefaultAudioTrack = table.Column<bool>(nullable: false), + SubtitleLanguagePrefernce = table.Column<string>(maxLength: 255, nullable: true), + DisplayMissingEpisodes = table.Column<bool>(nullable: true), + DisplayCollectionsView = table.Column<bool>(nullable: true), + HidePlayedInLatest = table.Column<bool>(nullable: true), + RememberAudioSelections = table.Column<bool>(nullable: true), + RememberSubtitleSelections = table.Column<bool>(nullable: true), + EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: true), + EnableUserPreferenceAccess = table.Column<bool>(nullable: true), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "LibraryRoot", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column<string>(maxLength: 65535, nullable: false), + NetworkPath = table.Column<string>(maxLength: 65535, nullable: true), + RowVersion = table.Column<uint>(nullable: false), + Library_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 255, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Group_Groups_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UrlId = table.Column<Guid>(nullable: false), + DateAdded = table.Column<DateTime>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + LibraryRoot_Id = table.Column<int>(nullable: true), + Discriminator = table.Column<string>(nullable: false), + EpisodeNumber = table.Column<int>(nullable: true), + Episode_Episodes_Id = table.Column<int>(nullable: true), + SeasonNumber = table.Column<int>(nullable: true), + Season_Seasons_Id = table.Column<int>(nullable: true), + AirsDayOfWeek = table.Column<int>(nullable: true), + AirsTime = table.Column<DateTimeOffset>(nullable: true), + FirstAired = table.Column<DateTimeOffset>(nullable: true), + TrackNumber = table.Column<int>(nullable: true), + Track_Tracks_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column<int>(nullable: false), + Value = table.Column<bool>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Permission_GroupPermissions_Id = table.Column<int>(nullable: true), + Permission_Permissions_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column<int>(nullable: false), + Value = table.Column<string>(maxLength: 65535, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Preference_Preferences_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ProviderName = table.Column<string>(maxLength: 255, nullable: false), + ProviderSecrets = table.Column<string>(maxLength: 65535, nullable: false), + ProviderData = table.Column<string>(maxLength: 65535, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + ProviderMapping_ProviderMappings_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RowVersion = table.Column<uint>(nullable: false), + LibraryItem_Id = table.Column<int>(nullable: true), + CollectionItem_Next_Id = table.Column<int>(nullable: true), + CollectionItem_Previous_Id = table.Column<int>(nullable: true), + CollectionItem_CollectionItem_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Release_Releases_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: true), + Language = table.Column<string>(maxLength: 3, nullable: false), + TimeStart = table.Column<long>(nullable: false), + TimeEnd = table.Column<long>(nullable: true), + RowVersion = table.Column<uint>(nullable: false), + Chapter_Chapters_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column<string>(maxLength: 65535, nullable: false), + Kind = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + MediaFile_MediaFiles_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + StreamNumber = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + MediaFileStream_MediaFileStreams_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Role = table.Column<string>(maxLength: 1024, nullable: true), + Type = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Person_Id = table.Column<int>(nullable: true), + Artwork_Artwork_Id = table.Column<int>(nullable: true), + PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column<string>(maxLength: 1024, nullable: false), + OriginalTitle = table.Column<string>(maxLength: 1024, nullable: true), + SortTitle = table.Column<string>(maxLength: 1024, nullable: true), + Language = table.Column<string>(maxLength: 3, nullable: false), + ReleaseDate = table.Column<DateTimeOffset>(nullable: true), + DateAdded = table.Column<DateTime>(nullable: false), + DateModified = table.Column<DateTime>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + Discriminator = table.Column<string>(nullable: false), + ISBN = table.Column<long>(nullable: true), + BookMetadata_BookMetadata_Id = table.Column<int>(nullable: true), + Description = table.Column<string>(maxLength: 65535, nullable: true), + Headquarters = table.Column<string>(maxLength: 255, nullable: true), + Country = table.Column<string>(maxLength: 2, nullable: true), + Homepage = table.Column<string>(maxLength: 1024, nullable: true), + CompanyMetadata_CompanyMetadata_Id = table.Column<int>(nullable: true), + CustomItemMetadata_CustomItemMetadata_Id = table.Column<int>(nullable: true), + Outline = table.Column<string>(maxLength: 1024, nullable: true), + Plot = table.Column<string>(maxLength: 65535, nullable: true), + Tagline = table.Column<string>(maxLength: 1024, nullable: true), + EpisodeMetadata_EpisodeMetadata_Id = table.Column<int>(nullable: true), + MovieMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), + MovieMetadata_Plot = table.Column<string>(maxLength: 65535, nullable: true), + MovieMetadata_Tagline = table.Column<string>(maxLength: 1024, nullable: true), + MovieMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), + MovieMetadata_MovieMetadata_Id = table.Column<int>(nullable: true), + Barcode = table.Column<string>(maxLength: 255, nullable: true), + LabelNumber = table.Column<string>(maxLength: 255, nullable: true), + MusicAlbumMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), + MusicAlbumMetadata_MusicAlbumMetadata_Id = table.Column<int>(nullable: true), + PhotoMetadata_PhotoMetadata_Id = table.Column<int>(nullable: true), + SeasonMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), + SeasonMetadata_SeasonMetadata_Id = table.Column<int>(nullable: true), + SeriesMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), + SeriesMetadata_Plot = table.Column<string>(maxLength: 65535, nullable: true), + SeriesMetadata_Tagline = table.Column<string>(maxLength: 1024, nullable: true), + SeriesMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), + SeriesMetadata_SeriesMetadata_Id = table.Column<int>(nullable: true), + TrackMetadata_TrackMetadata_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Path = table.Column<string>(maxLength: 65535, nullable: false), + Kind = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RowVersion = table.Column<uint>(nullable: false), + Company_Parent_Id = table.Column<int>(nullable: true), + Company_Labels_Id = table.Column<int>(nullable: true), + Company_Networks_Id = table.Column<int>(nullable: true), + Company_Publishers_Id = table.Column<int>(nullable: true), + Company_Studios_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 255, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ProviderId = table.Column<string>(maxLength: 255, nullable: false), + RowVersion = table.Column<uint>(nullable: false), + MetadataProvider_Id = table.Column<int>(nullable: true), + MetadataProviderId_Sources_Id = table.Column<int>(nullable: true), + PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 1024, nullable: true), + MaximumValue = table.Column<double>(nullable: false), + MinimumValue = table.Column<double>(nullable: false), + RowVersion = table.Column<uint>(nullable: false), + MetadataProviderId_Source_Id = table.Column<int>(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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Value = table.Column<double>(nullable: false), + Votes = table.Column<int>(nullable: true), + RowVersion = table.Column<uint>(nullable: false), + RatingSource_RatingType_Id = table.Column<int>(nullable: true), + PersonRole_PersonRoles_Id = table.Column<int>(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 +{ + /// <summary> + /// The design time factory for <see cref="JellyfinDb"/>. + /// This is only used for the creation of migrations and not during runtime. + /// </summary> + internal class DesignTimeJellyfinDbFactory : IDesignTimeDbContextFactory<JellyfinDb> + { + public JellyfinDb CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder<JellyfinDb>(); + 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 + +// <auto-generated /> +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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLog"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Chapter_Chapters_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<long?>("TimeEnd") + .HasColumnType("INTEGER"); + + b.Property<long>("TimeStart") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Chapter_Chapters_Id"); + + b.ToTable("Chapter"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_CollectionItem_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_Next_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("CollectionItem_Previous_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("LibraryItem_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Labels_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Networks_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Parent_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Publishers_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Company_Studios_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Group_Groups_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Group_Groups_Id"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Library"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<string>("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<int?>("LibraryRoot_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<Guid>("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("LibraryRoot_Id"); + + b.HasIndex("UrlId") + .IsUnique(); + + b.ToTable("LibraryItem"); + + b.HasDiscriminator<string>("Discriminator").HasValue("LibraryItem"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Library_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("NetworkPath") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Library_Id"); + + b.ToTable("LibraryRoot"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("MediaFile_MediaFiles_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFile_MediaFiles_Id"); + + b.ToTable("MediaFile"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("MediaFileStream_MediaFileStreams_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<int>("StreamNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MediaFileStream_MediaFileStreams_Id"); + + b.ToTable("MediaFileStream"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<DateTime>("DateModified") + .HasColumnType("TEXT"); + + b.Property<string>("Discriminator") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property<string>("Language") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(3); + + b.Property<string>("OriginalTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<DateTimeOffset?>("ReleaseDate") + .HasColumnType("TEXT"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SortTitle") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.ToTable("Metadata"); + + b.HasDiscriminator<string>("Discriminator").HasValue("Metadata"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("MetadataProvider"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("MetadataProviderId_Sources_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("MetadataProvider_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<uint>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("Permission_GroupPermissions_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Permission_Permissions_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<bool>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<DateTime>("DateModified") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SourceId") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<Guid>("UrlId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("Artwork_Artwork_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("Person_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Role") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<int>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int>("Kind") + .HasColumnType("INTEGER"); + + b.Property<int?>("Preference_Preferences_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderData") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("ProviderMapping_ProviderMappings_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("ProviderName") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("ProviderSecrets") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderMapping_ProviderMappings_Id"); + + b.ToTable("ProviderMapping"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<int?>("PersonRole_PersonRoles_Id") + .HasColumnType("INTEGER"); + + b.Property<int?>("RatingSource_RatingType_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<double>("Value") + .HasColumnType("REAL"); + + b.Property<int?>("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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<double>("MaximumValue") + .HasColumnType("REAL"); + + b.Property<int?>("MetadataProviderId_Source_Id") + .HasColumnType("INTEGER"); + + b.Property<double>("MinimumValue") + .HasColumnType("REAL"); + + b.Property<string>("Name") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("MetadataProviderId_Source_Id"); + + b.ToTable("RatingSource"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<int?>("Release_Releases_Id") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Release_Releases_Id"); + + b.ToTable("Release"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudioLanguagePreference") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<bool?>("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property<bool?>("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property<bool?>("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property<bool?>("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property<string>("GroupedFolders") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<bool?>("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property<int>("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property<string>("LatestItemExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property<bool>("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property<string>("MyMediaExcludes") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("OrderedViews") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<bool>("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property<bool?>("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property<bool?>("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("SubtitleLanguagePrefernce") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("SubtitleMode") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("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<int?>("EpisodeNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("SeasonNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("AirsDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property<DateTimeOffset?>("AirsTime") + .HasColumnType("TEXT"); + + b.Property<DateTimeOffset?>("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<int?>("TrackNumber") + .HasColumnType("INTEGER"); + + b.Property<int?>("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<int?>("BookMetadata_BookMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<long?>("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<int?>("CompanyMetadata_CompanyMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("Description") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("Headquarters") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("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<int?>("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<int?>("EpisodeMetadata_EpisodeMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("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<string>("Country") + .HasColumnName("MovieMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<int?>("MovieMetadata_MovieMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("Outline") + .HasColumnName("MovieMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnName("MovieMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<string>("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<string>("Barcode") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<string>("Country") + .HasColumnName("MusicAlbumMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("LabelNumber") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property<int?>("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<int?>("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<string>("Outline") + .HasColumnName("SeasonMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<int?>("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<string>("Country") + .HasColumnName("SeriesMetadata_Country") + .HasColumnType("TEXT") + .HasMaxLength(2); + + b.Property<string>("Outline") + .HasColumnName("SeriesMetadata_Outline") + .HasColumnType("TEXT") + .HasMaxLength(1024); + + b.Property<string>("Plot") + .HasColumnName("SeriesMetadata_Plot") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property<int?>("SeriesMetadata_SeriesMetadata_Id") + .HasColumnType("INTEGER"); + + b.Property<string>("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<int?>("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 @@ <GenerateDocumentationFile>true</GenerateDocumentationFile> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <Nullable>enable</Nullable> + + <!-- Used for generating migrations for EF Core --> + <GenerateRuntimeConfigurationFiles>True</GenerateRuntimeConfigurationFiles> </PropertyGroup> <ItemGroup> @@ -41,6 +44,10 @@ <ItemGroup> <PackageReference Include="CommandLineParser" Version="2.7.82" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> <PackageReference Include="prometheus-net" Version="3.5.0" /> 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() }; /// <summary> 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<JellyfinDb>(); + + 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. /// </summary> /// <value>The user primary image tag.</value> + [Obsolete("UserPrimaryImageTag is not used.")] public string UserPrimaryImageTag { get; set; } /// <summary> 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<GenericEventArgs<ActivityLogEntry>> EntryCreated; - void Create(ActivityLogEntry entry); + void Create(ActivityLog entry); - QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); + Task CreateAsync(ActivityLog entry); - QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); + QueryResult<ActivityLogEntry> GetPagedResult(int? startIndex, int? limit); + + QueryResult<ActivityLogEntry> GetPagedResult( + Func<IQueryable<ActivityLog>, IEnumerable<ActivityLog>> 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<ActivityLogEntry> 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 @@ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" /> + </ItemGroup> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet> 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 <barronpm@gmail.com> Date: Sat, 2 May 2020 19:26:24 -0400 Subject: [PATCH 303/411] 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<ActivityLog> ActivityLogs { get; set; } - public virtual DbSet<Artwork> Artwork { get; set; } + /*public virtual DbSet<Artwork> Artwork { get; set; } public virtual DbSet<Book> Books { get; set; } public virtual DbSet<BookMetadata> BookMetadata { get; set; } public virtual DbSet<Chapter> Chapters { get; set; } @@ -63,7 +63,7 @@ namespace Jellyfin.Server.Implementations public virtual DbSet<SeriesMetadata> SeriesMetadata { get; set; } public virtual DbSet<Track> Tracks { get; set; } public virtual DbSet<TrackMetadata> TrackMetadata { get; set; } - public virtual DbSet<User> Users { get; set; } + public virtual DbSet<User> Users { get; set; } */ /// <summary> /// Gets or sets the default connection string. @@ -94,13 +94,13 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); - modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind); + /*modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind); modelBuilder.Entity<Genre>().HasIndex(t => t.Name) .IsUnique(); modelBuilder.Entity<LibraryItem>().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 - -// <auto-generated /> -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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateCreated") - .HasColumnType("TEXT"); - - b.Property<string>("ItemId") - .HasColumnType("TEXT") - .HasMaxLength(256); - - b.Property<int>("LogSeverity") - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property<string>("Overview") - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("ShortOverview") - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property<string>("Type") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(256); - - b.Property<Guid>("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("ActivityLog"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Artwork", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Chapter_Chapters_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<long?>("TimeEnd") - .HasColumnType("INTEGER"); - - b.Property<long>("TimeStart") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Chapter_Chapters_Id"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Collection"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_CollectionItem_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_Next_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_Previous_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("LibraryItem_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Labels_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Networks_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Parent_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Publishers_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Studios_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Group_Groups_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Group_Groups_Id"); - - b.ToTable("Group"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Library"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<string>("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<int?>("LibraryRoot_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<Guid>("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("LibraryRoot_Id"); - - b.HasIndex("UrlId") - .IsUnique(); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator<string>("Discriminator").HasValue("LibraryItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Library_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("NetworkPath") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Library_Id"); - - b.ToTable("LibraryRoot"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("MediaFile_MediaFiles_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFile_MediaFiles_Id"); - - b.ToTable("MediaFile"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("MediaFileStream_MediaFileStreams_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<int>("StreamNumber") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileStream_MediaFileStreams_Id"); - - b.ToTable("MediaFileStream"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<DateTime>("DateModified") - .HasColumnType("TEXT"); - - b.Property<string>("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<string>("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property<string>("OriginalTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<DateTimeOffset?>("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SortTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Title") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasKey("Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator<string>("Discriminator").HasValue("Metadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("MetadataProvider"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("MetadataProviderId_Sources_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("MetadataProvider_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("Permission_GroupPermissions_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Permission_Permissions_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<bool>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<DateTime>("DateModified") - .HasColumnType("TEXT"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SourceId") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<Guid>("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Artwork_Artwork_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Person_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Role") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<int>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("Preference_Preferences_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderData") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("ProviderMapping_ProviderMappings_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderName") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("ProviderSecrets") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ProviderMapping_ProviderMappings_Id"); - - b.ToTable("ProviderMapping"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("RatingSource_RatingType_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<double>("Value") - .HasColumnType("REAL"); - - b.Property<int?>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<double>("MaximumValue") - .HasColumnType("REAL"); - - b.Property<int?>("MetadataProviderId_Source_Id") - .HasColumnType("INTEGER"); - - b.Property<double>("MinimumValue") - .HasColumnType("REAL"); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Source_Id"); - - b.ToTable("RatingSource"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<int?>("Release_Releases_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Release_Releases_Id"); - - b.ToTable("Release"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("AudioLanguagePreference") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("AuthenticationProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<bool?>("DisplayCollectionsView") - .HasColumnType("INTEGER"); - - b.Property<bool?>("DisplayMissingEpisodes") - .HasColumnType("INTEGER"); - - b.Property<bool?>("EnableNextEpisodeAutoPlay") - .HasColumnType("INTEGER"); - - b.Property<bool?>("EnableUserPreferenceAccess") - .HasColumnType("INTEGER"); - - b.Property<string>("GroupedFolders") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<bool?>("HidePlayedInLatest") - .HasColumnType("INTEGER"); - - b.Property<int>("InvalidLoginAttemptCount") - .HasColumnType("INTEGER"); - - b.Property<string>("LatestItemExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("LoginAttemptsBeforeLockout") - .HasColumnType("INTEGER"); - - b.Property<bool>("MustUpdatePassword") - .HasColumnType("INTEGER"); - - b.Property<string>("MyMediaExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("OrderedViews") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<bool>("PlayDefaultAudioTrack") - .HasColumnType("INTEGER"); - - b.Property<bool?>("RememberAudioSelections") - .HasColumnType("INTEGER"); - - b.Property<bool?>("RememberSubtitleSelections") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SubtitleLanguagePrefernce") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("SubtitleMode") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("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<int?>("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("SeasonNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("AirsDayOfWeek") - .HasColumnType("INTEGER"); - - b.Property<DateTimeOffset?>("AirsTime") - .HasColumnType("TEXT"); - - b.Property<DateTimeOffset?>("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<int?>("TrackNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("BookMetadata_BookMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<long?>("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<int?>("CompanyMetadata_CompanyMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("Description") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Headquarters") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("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<int?>("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<int?>("EpisodeMetadata_EpisodeMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("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<string>("Country") - .HasColumnName("MovieMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<int?>("MovieMetadata_MovieMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Outline") - .HasColumnName("MovieMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnName("MovieMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("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<string>("Barcode") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("Country") - .HasColumnName("MusicAlbumMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("LabelNumber") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<int?>("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<int?>("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<string>("Outline") - .HasColumnName("SeasonMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<int?>("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<string>("Country") - .HasColumnName("SeriesMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("Outline") - .HasColumnName("SeriesMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnName("SeriesMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("SeriesMetadata_SeriesMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("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<int?>("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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 512, nullable: false), - Overview = table.Column<string>(maxLength: 512, nullable: true), - ShortOverview = table.Column<string>(maxLength: 512, nullable: true), - Type = table.Column<string>(maxLength: 256, nullable: false), - UserId = table.Column<Guid>(nullable: false), - ItemId = table.Column<string>(maxLength: 256, nullable: true), - DateCreated = table.Column<DateTime>(nullable: false), - LogSeverity = table.Column<int>(nullable: false), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ActivityLog", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Collection", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: true), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Collection", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Library", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: false), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Library", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "MetadataProvider", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: false), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_MetadataProvider", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Person", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UrlId = table.Column<Guid>(nullable: false), - Name = table.Column<string>(maxLength: 1024, nullable: false), - SourceId = table.Column<string>(maxLength: 255, nullable: true), - DateAdded = table.Column<DateTime>(nullable: false), - DateModified = table.Column<DateTime>(nullable: false), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Person", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "User", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Username = table.Column<string>(maxLength: 255, nullable: false), - Password = table.Column<string>(maxLength: 65535, nullable: true), - MustUpdatePassword = table.Column<bool>(nullable: false), - AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: false), - AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false), - GroupedFolders = table.Column<string>(maxLength: 65535, nullable: true), - InvalidLoginAttemptCount = table.Column<int>(nullable: false), - LatestItemExcludes = table.Column<string>(maxLength: 65535, nullable: true), - LoginAttemptsBeforeLockout = table.Column<int>(nullable: true), - MyMediaExcludes = table.Column<string>(maxLength: 65535, nullable: true), - OrderedViews = table.Column<string>(maxLength: 65535, nullable: true), - SubtitleMode = table.Column<string>(maxLength: 255, nullable: false), - PlayDefaultAudioTrack = table.Column<bool>(nullable: false), - SubtitleLanguagePrefernce = table.Column<string>(maxLength: 255, nullable: true), - DisplayMissingEpisodes = table.Column<bool>(nullable: true), - DisplayCollectionsView = table.Column<bool>(nullable: true), - HidePlayedInLatest = table.Column<bool>(nullable: true), - RememberAudioSelections = table.Column<bool>(nullable: true), - RememberSubtitleSelections = table.Column<bool>(nullable: true), - EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: true), - EnableUserPreferenceAccess = table.Column<bool>(nullable: true), - RowVersion = table.Column<uint>(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "LibraryRoot", - schema: "jellyfin", - columns: table => new - { - Id = table.Column<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column<string>(maxLength: 65535, nullable: false), - NetworkPath = table.Column<string>(maxLength: 65535, nullable: true), - RowVersion = table.Column<uint>(nullable: false), - Library_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 255, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Group_Groups_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UrlId = table.Column<Guid>(nullable: false), - DateAdded = table.Column<DateTime>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - LibraryRoot_Id = table.Column<int>(nullable: true), - Discriminator = table.Column<string>(nullable: false), - EpisodeNumber = table.Column<int>(nullable: true), - Episode_Episodes_Id = table.Column<int>(nullable: true), - SeasonNumber = table.Column<int>(nullable: true), - Season_Seasons_Id = table.Column<int>(nullable: true), - AirsDayOfWeek = table.Column<int>(nullable: true), - AirsTime = table.Column<DateTimeOffset>(nullable: true), - FirstAired = table.Column<DateTimeOffset>(nullable: true), - TrackNumber = table.Column<int>(nullable: true), - Track_Tracks_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column<int>(nullable: false), - Value = table.Column<bool>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Permission_GroupPermissions_Id = table.Column<int>(nullable: true), - Permission_Permissions_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column<int>(nullable: false), - Value = table.Column<string>(maxLength: 65535, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Preference_Preferences_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ProviderName = table.Column<string>(maxLength: 255, nullable: false), - ProviderSecrets = table.Column<string>(maxLength: 65535, nullable: false), - ProviderData = table.Column<string>(maxLength: 65535, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - ProviderMapping_ProviderMappings_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RowVersion = table.Column<uint>(nullable: false), - LibraryItem_Id = table.Column<int>(nullable: true), - CollectionItem_Next_Id = table.Column<int>(nullable: true), - CollectionItem_Previous_Id = table.Column<int>(nullable: true), - CollectionItem_CollectionItem_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Release_Releases_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: true), - Language = table.Column<string>(maxLength: 3, nullable: false), - TimeStart = table.Column<long>(nullable: false), - TimeEnd = table.Column<long>(nullable: true), - RowVersion = table.Column<uint>(nullable: false), - Chapter_Chapters_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column<string>(maxLength: 65535, nullable: false), - Kind = table.Column<int>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - MediaFile_MediaFiles_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - StreamNumber = table.Column<int>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - MediaFileStream_MediaFileStreams_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Role = table.Column<string>(maxLength: 1024, nullable: true), - Type = table.Column<int>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Person_Id = table.Column<int>(nullable: true), - Artwork_Artwork_Id = table.Column<int>(nullable: true), - PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Title = table.Column<string>(maxLength: 1024, nullable: false), - OriginalTitle = table.Column<string>(maxLength: 1024, nullable: true), - SortTitle = table.Column<string>(maxLength: 1024, nullable: true), - Language = table.Column<string>(maxLength: 3, nullable: false), - ReleaseDate = table.Column<DateTimeOffset>(nullable: true), - DateAdded = table.Column<DateTime>(nullable: false), - DateModified = table.Column<DateTime>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - Discriminator = table.Column<string>(nullable: false), - ISBN = table.Column<long>(nullable: true), - BookMetadata_BookMetadata_Id = table.Column<int>(nullable: true), - Description = table.Column<string>(maxLength: 65535, nullable: true), - Headquarters = table.Column<string>(maxLength: 255, nullable: true), - Country = table.Column<string>(maxLength: 2, nullable: true), - Homepage = table.Column<string>(maxLength: 1024, nullable: true), - CompanyMetadata_CompanyMetadata_Id = table.Column<int>(nullable: true), - CustomItemMetadata_CustomItemMetadata_Id = table.Column<int>(nullable: true), - Outline = table.Column<string>(maxLength: 1024, nullable: true), - Plot = table.Column<string>(maxLength: 65535, nullable: true), - Tagline = table.Column<string>(maxLength: 1024, nullable: true), - EpisodeMetadata_EpisodeMetadata_Id = table.Column<int>(nullable: true), - MovieMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), - MovieMetadata_Plot = table.Column<string>(maxLength: 65535, nullable: true), - MovieMetadata_Tagline = table.Column<string>(maxLength: 1024, nullable: true), - MovieMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), - MovieMetadata_MovieMetadata_Id = table.Column<int>(nullable: true), - Barcode = table.Column<string>(maxLength: 255, nullable: true), - LabelNumber = table.Column<string>(maxLength: 255, nullable: true), - MusicAlbumMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), - MusicAlbumMetadata_MusicAlbumMetadata_Id = table.Column<int>(nullable: true), - PhotoMetadata_PhotoMetadata_Id = table.Column<int>(nullable: true), - SeasonMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), - SeasonMetadata_SeasonMetadata_Id = table.Column<int>(nullable: true), - SeriesMetadata_Outline = table.Column<string>(maxLength: 1024, nullable: true), - SeriesMetadata_Plot = table.Column<string>(maxLength: 65535, nullable: true), - SeriesMetadata_Tagline = table.Column<string>(maxLength: 1024, nullable: true), - SeriesMetadata_Country = table.Column<string>(maxLength: 2, nullable: true), - SeriesMetadata_SeriesMetadata_Id = table.Column<int>(nullable: true), - TrackMetadata_TrackMetadata_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column<string>(maxLength: 65535, nullable: false), - Kind = table.Column<int>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RowVersion = table.Column<uint>(nullable: false), - Company_Parent_Id = table.Column<int>(nullable: true), - Company_Labels_Id = table.Column<int>(nullable: true), - Company_Networks_Id = table.Column<int>(nullable: true), - Company_Publishers_Id = table.Column<int>(nullable: true), - Company_Studios_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 255, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ProviderId = table.Column<string>(maxLength: 255, nullable: false), - RowVersion = table.Column<uint>(nullable: false), - MetadataProvider_Id = table.Column<int>(nullable: true), - MetadataProviderId_Sources_Id = table.Column<int>(nullable: true), - PersonRole_PersonRoles_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column<string>(maxLength: 1024, nullable: true), - MaximumValue = table.Column<double>(nullable: false), - MinimumValue = table.Column<double>(nullable: false), - RowVersion = table.Column<uint>(nullable: false), - MetadataProviderId_Source_Id = table.Column<int>(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<int>(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Value = table.Column<double>(nullable: false), - Votes = table.Column<int>(nullable: true), - RowVersion = table.Column<uint>(nullable: false), - RatingSource_RatingType_Id = table.Column<int>(nullable: true), - PersonRole_PersonRoles_Id = table.Column<int>(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 + +// <auto-generated /> +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<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<Guid>("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<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 512, nullable: false), + Overview = table.Column<string>(maxLength: 512, nullable: true), + ShortOverview = table.Column<string>(maxLength: 512, nullable: true), + Type = table.Column<string>(maxLength: 256, nullable: false), + UserId = table.Column<Guid>(nullable: false), + ItemId = table.Column<string>(maxLength: 256, nullable: true), + DateCreated = table.Column<DateTime>(nullable: false), + LogSeverity = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(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 - -// <auto-generated /> +// <auto-generated /> 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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Chapter_Chapters_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<long?>("TimeEnd") - .HasColumnType("INTEGER"); - - b.Property<long>("TimeStart") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Chapter_Chapters_Id"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Collection", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Collection"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.CollectionItem", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_CollectionItem_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_Next_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("CollectionItem_Previous_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("LibraryItem_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Labels_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Networks_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Parent_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Publishers_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Company_Studios_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Group_Groups_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Group_Groups_Id"); - - b.ToTable("Group"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Library", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("Library"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryItem", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<string>("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<int?>("LibraryRoot_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<Guid>("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("LibraryRoot_Id"); - - b.HasIndex("UrlId") - .IsUnique(); - - b.ToTable("LibraryItem"); - - b.HasDiscriminator<string>("Discriminator").HasValue("LibraryItem"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.LibraryRoot", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Library_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("NetworkPath") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Library_Id"); - - b.ToTable("LibraryRoot"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFile", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("MediaFile_MediaFiles_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFile_MediaFiles_Id"); - - b.ToTable("MediaFile"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MediaFileStream", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("MediaFileStream_MediaFileStreams_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<int>("StreamNumber") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MediaFileStream_MediaFileStreams_Id"); - - b.ToTable("MediaFileStream"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Metadata", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<DateTime>("DateModified") - .HasColumnType("TEXT"); - - b.Property<string>("Discriminator") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property<string>("Language") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(3); - - b.Property<string>("OriginalTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<DateTimeOffset?>("ReleaseDate") - .HasColumnType("TEXT"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SortTitle") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Title") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.HasKey("Id"); - - b.ToTable("Metadata"); - - b.HasDiscriminator<string>("Discriminator").HasValue("Metadata"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProvider", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.ToTable("MetadataProvider"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.MetadataProviderId", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("MetadataProviderId_Sources_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("MetadataProvider_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<uint>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("Permission_GroupPermissions_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Permission_Permissions_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<bool>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<DateTime>("DateAdded") - .HasColumnType("TEXT"); - - b.Property<DateTime>("DateModified") - .HasColumnType("TEXT"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SourceId") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<Guid>("UrlId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.PersonRole", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("Artwork_Artwork_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("Person_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Role") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<int>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int>("Kind") - .HasColumnType("INTEGER"); - - b.Property<int?>("Preference_Preferences_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderData") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("ProviderMapping_ProviderMappings_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("ProviderName") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("ProviderSecrets") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("ProviderMapping_ProviderMappings_Id"); - - b.ToTable("ProviderMapping"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Rating", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<int?>("PersonRole_PersonRoles_Id") - .HasColumnType("INTEGER"); - - b.Property<int?>("RatingSource_RatingType_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<double>("Value") - .HasColumnType("REAL"); - - b.Property<int?>("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<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<double>("MaximumValue") - .HasColumnType("REAL"); - - b.Property<int?>("MetadataProviderId_Source_Id") - .HasColumnType("INTEGER"); - - b.Property<double>("MinimumValue") - .HasColumnType("REAL"); - - b.Property<string>("Name") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("MetadataProviderId_Source_Id"); - - b.ToTable("RatingSource"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Release", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<int?>("Release_Releases_Id") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Release_Releases_Id"); - - b.ToTable("Release"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property<string>("AudioLanguagePreference") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("AuthenticationProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<bool?>("DisplayCollectionsView") - .HasColumnType("INTEGER"); - - b.Property<bool?>("DisplayMissingEpisodes") - .HasColumnType("INTEGER"); - - b.Property<bool?>("EnableNextEpisodeAutoPlay") - .HasColumnType("INTEGER"); - - b.Property<bool?>("EnableUserPreferenceAccess") - .HasColumnType("INTEGER"); - - b.Property<string>("GroupedFolders") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<bool?>("HidePlayedInLatest") - .HasColumnType("INTEGER"); - - b.Property<int>("InvalidLoginAttemptCount") - .HasColumnType("INTEGER"); - - b.Property<string>("LatestItemExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("LoginAttemptsBeforeLockout") - .HasColumnType("INTEGER"); - - b.Property<bool>("MustUpdatePassword") - .HasColumnType("INTEGER"); - - b.Property<string>("MyMediaExcludes") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("OrderedViews") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<bool>("PlayDefaultAudioTrack") - .HasColumnType("INTEGER"); - - b.Property<bool?>("RememberAudioSelections") - .HasColumnType("INTEGER"); - - b.Property<bool?>("RememberSubtitleSelections") - .HasColumnType("INTEGER"); - - b.Property<uint>("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property<string>("SubtitleLanguagePrefernce") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("SubtitleMode") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("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<int?>("EpisodeNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("SeasonNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("AirsDayOfWeek") - .HasColumnType("INTEGER"); - - b.Property<DateTimeOffset?>("AirsTime") - .HasColumnType("TEXT"); - - b.Property<DateTimeOffset?>("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<int?>("TrackNumber") - .HasColumnType("INTEGER"); - - b.Property<int?>("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<int?>("BookMetadata_BookMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<long?>("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<int?>("CompanyMetadata_CompanyMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("Description") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("Headquarters") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("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<int?>("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<int?>("EpisodeMetadata_EpisodeMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("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<string>("Country") - .HasColumnName("MovieMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<int?>("MovieMetadata_MovieMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("Outline") - .HasColumnName("MovieMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnName("MovieMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<string>("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<string>("Barcode") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<string>("Country") - .HasColumnName("MusicAlbumMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("LabelNumber") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property<int?>("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<int?>("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<string>("Outline") - .HasColumnName("SeasonMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<int?>("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<string>("Country") - .HasColumnName("SeriesMetadata_Country") - .HasColumnType("TEXT") - .HasMaxLength(2); - - b.Property<string>("Outline") - .HasColumnName("SeriesMetadata_Outline") - .HasColumnType("TEXT") - .HasMaxLength(1024); - - b.Property<string>("Plot") - .HasColumnName("SeriesMetadata_Plot") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property<int?>("SeriesMetadata_SeriesMetadata_Id") - .HasColumnType("INTEGER"); - - b.Property<string>("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<int?>("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 <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 01:10:51 -0400 Subject: [PATCH 304/411] 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 <christoph286@googlemail.com> Date: Sun, 3 May 2020 16:13:05 +0200 Subject: [PATCH 305/411] ~ set Jellyfin.Server as startup project Signed-off-by: Christoph Potas <christoph286@googlemail.com> --- 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 <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 15:31:17 -0400 Subject: [PATCH 306/411] 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<Startup> { private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); - private static readonly ConcurrentBag<ApplicationHost> _appHosts = new ConcurrentBag<ApplicationHost>(); + private static readonly ConcurrentBag<IDisposable> _disposableComponents = new ConcurrentBag<IDisposable>(); /// <summary> /// Initializes a new instance of <see cref="JellyfinApplicationFactory"/>. @@ -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<ManagedFileSystem>(), appPaths), new NetworkManager(loggerFactory.CreateLogger<NetworkManager>())); - _appHosts.Add(appHost); + _disposableComponents.Add(appHost); var serviceCollection = new ServiceCollection(); appHost.Init(serviceCollection); @@ -104,12 +107,12 @@ namespace MediaBrowser.Api.Tests /// <inheritdoc/> 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 <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 16:23:42 -0400 Subject: [PATCH 307/411] 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<IDisposable> _disposableComponents = new ConcurrentBag<IDisposable>(); /// <summary> - /// Initializes a new instance of <see cref="JellyfinApplicationFactory"/>. + /// Initializes a new instance of the <see cref="JellyfinApplicationFactory"/> class. /// </summary> 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 @@ <ProjectReference Include="..\..\MediaBrowser.Api\MediaBrowser.Api.csproj" /> </ItemGroup> + <!-- Code Analyzers--> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + </ItemGroup> + + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>MediaBrowser.Api.Tests.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + </Project> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<RuleSet Name="Rules for MediaBrowser.Api.Tests" Description="Code analysis rules for MediaBrowser.Api.Tests.csproj" ToolsVersion="14.0"> + + <!-- Include the solution default RuleSet. The rules in this file will override the defaults. --> + <Include Path="../../jellyfin.ruleset" Action="Default" /> + + <!-- StyleCop Analyzer Rules --> + <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers"> + <!-- SA0001: XML comment analysis is disabled due to project configuration --> + <Rule Id="SA0001" Action="None" /> + </Rules> + + <!-- FxCop Analyzer Rules --> + <Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design"> + <!-- CA1707: Identifiers should not contain underscores --> + <Rule Id="CA1707" Action="None" /> + <!-- CA2007: Consider calling ConfigureAwait on the awaited task --> + <Rule Id="CA2007" Action="None" /> + <!-- CA2234: Pass system uri objects instead of strings --> + <Rule Id="CA2234" Action="Info" /> + </Rules> +</RuleSet> From 927e7a24ed1c79957f9a39e7ffde4198f49ceb22 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 16:29:13 -0400 Subject: [PATCH 308/411] 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 <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 16:30:39 -0400 Subject: [PATCH 309/411] 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 <marknr.monteiro@protonmail.com> Date: Sun, 3 May 2020 16:51:39 -0400 Subject: [PATCH 310/411] 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 @@ </ItemGroup> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> - <CodeAnalysisRuleSet>MediaBrowser.Api.Tests.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> </PropertyGroup> </Project> 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 @@ <RuleSet Name="Rules for MediaBrowser.Api.Tests" Description="Code analysis rules for MediaBrowser.Api.Tests.csproj" ToolsVersion="14.0"> <!-- Include the solution default RuleSet. The rules in this file will override the defaults. --> - <Include Path="../../jellyfin.ruleset" Action="Default" /> + <Include Path="../jellyfin.ruleset" Action="Default" /> <!-- StyleCop Analyzer Rules --> <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers"> From 675cbd8a168a2096e87208de6eb9fe78bdc8d921 Mon Sep 17 00:00:00 2001 From: artiume <siderite@gmail.com> Date: Sun, 3 May 2020 18:23:25 -0400 Subject: [PATCH 311/411] Update MimeTypes.cs --- MediaBrowser.Model/Net/MimeTypes.cs | 121 ++++++++++++++++------------ 1 file changed, 71 insertions(+), 50 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 /// </summary> private static readonly HashSet<string> _videoFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { - ".mkv", - ".m2t", - ".m2ts", + ".3gp", + ".asf", + ".avi", + ".divx", + ".dvr-ms", + ".f4v", + ".flv", ".img", ".iso", + ".m2t", + ".m2ts", + ".m2v", + ".m4v", ".mk3d", - ".ts", - ".rmvb", + ".mkv", ".mov", - ".avi", + ".mp4", ".mpg", ".mpeg", - ".wmv", - ".mp4", - ".divx", - ".dvr-ms", - ".wtv", + ".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<string, string> _mimeTypeLookup = new Dictionary<string, string>(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" }, + { ".m3u8", "application/x-mpegURL" }, { ".map", "application/x-javascript" }, + { ".mobi", "application/x-mobipocket-ebook" }, { ".pdf", "application/pdf" }, + { ".rar", "application/vnd.rar" }, + { ".srt", "application/x-subrip" }, { ".ttml", "application/ttml+xml" }, - { ".m3u8", "application/x-mpegURL" }, - { ".mobi", "application/x-mobipocket-ebook" }, - { ".xml", "application/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" }, + { ".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" }, - { ".mpegts", "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<string, string> _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 <tin.pavelic@gmail.com> Date: Sun, 3 May 2020 11:11:15 +0000 Subject: [PATCH 312/411] 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 <ilmatar@gmail.com> Date: Sun, 3 May 2020 13:05:11 +0000 Subject: [PATCH 313/411] 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 <filip.rutar@gmail.com> Date: Sun, 3 May 2020 11:13:02 +0000 Subject: [PATCH 314/411] 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 <siderite@gmail.com> Date: Mon, 4 May 2020 12:13:50 -0400 Subject: [PATCH 315/411] 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 <functionbyx@gmail.com> Date: Mon, 4 May 2020 23:09:35 +0000 Subject: [PATCH 316/411] 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 <siderite@gmail.com> Date: Tue, 5 May 2020 07:25:32 -0400 Subject: [PATCH 317/411] 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 <marknr.monteiro@protonmail.com> Date: Tue, 5 May 2020 11:17:03 -0400 Subject: [PATCH 318/411] 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 <vrgo93@gmail.com> Date: Tue, 5 May 2020 18:08:07 +0000 Subject: [PATCH 319/411] 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 <functionbyx@gmail.com> Date: Mon, 4 May 2020 23:11:12 +0000 Subject: [PATCH 320/411] 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 <siderite@gmail.com> Date: Wed, 6 May 2020 07:38:19 -0400 Subject: [PATCH 321/411] Update MediaBrowser.Model/Net/MimeTypes.cs Co-authored-by: dkanada <dkanada@users.noreply.github.com> --- 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 <siderite@gmail.com> Date: Wed, 6 May 2020 07:45:40 -0400 Subject: [PATCH 322/411] 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 <mmcanals@gmail.com> Date: Wed, 6 May 2020 09:47:34 +0000 Subject: [PATCH 323/411] 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 <siderite@gmail.com> Date: Wed, 6 May 2020 08:23:56 -0400 Subject: [PATCH 324/411] 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 <jimcartlidge@yahoo.co.uk> Date: Wed, 6 May 2020 16:04:07 +0100 Subject: [PATCH 325/411] 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 <poloniodavide@gmail.com> Date: Wed, 6 May 2020 17:21:21 +0200 Subject: [PATCH 326/411] 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 /// <summary> /// 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. /// </summary> /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> - // FIXME this shouldn't be here, but it's necessary when changing password at the first login public bool HasConfiguredPassword { get; set; } /// <summary> From 1c210d930c36bcb4e0bacce238f905628ef75966 Mon Sep 17 00:00:00 2001 From: Oliver Moolman <oliver.moolman@gmail.com> Date: Wed, 6 May 2020 13:33:16 +0000 Subject: [PATCH 327/411] 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 <stefan.petrushevski.coka@gmail.com> Date: Wed, 6 May 2020 19:48:52 +0000 Subject: [PATCH 328/411] 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 <poloniodavide@gmail.com> Date: Thu, 7 May 2020 09:14:00 +0200 Subject: [PATCH 329/411] Fix typo in PublicUserDto Co-authored-by: Vasily <JustAMan@users.noreply.github.com> --- 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 /// <summary> /// 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. /// </summary> /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> @@ -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 <siderite@gmail.com> Date: Thu, 7 May 2020 15:58:20 -0400 Subject: [PATCH 330/411] 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 8bd356ab2077b5c1a90510e3e73b11698eca0331 Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Mon, 4 May 2020 20:19:10 -0700 Subject: [PATCH 331/411] Reduce number of TMDB lookups if filenames have punctuation chars Previosly TMDB would be queried with the raw name and always fail, then retry with the cleaned name. Now non-word chars are always cleaned out first. If first query fails, retry with more aggressive cleaning. --- .../Tmdb/Movies/TmdbSearch.cs | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index 223cef086b..08c1afec28 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using System.Text.RegularExpressions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -19,6 +20,20 @@ namespace MediaBrowser.Providers.Tmdb.Movies public class TmdbSearch { private static readonly CultureInfo EnUs = new CultureInfo("en-US"); + + private static readonly Regex cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); + private static readonly Regex cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); + private static readonly Regex cleanStopWords = new Regex(@"\b( # Start at word boundary + 19[0-9]{2}|20[0-9]{2}| # 1900-2099 + S[0-9]{2}| # Season + E[0-9]{2}| # Episode + (2160|1080|720|576|480)[ip]?| # Resolution + [xh]?264| # Encoding + (web|dvd|bd|hdtv|hd)rip| # *Rip + web|hdtv|mp4|bluray|ktr|dl|single|imageset|internal|doku|dubbed|retail|xxx|flac + ).* # Match rest of string", + RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); + private const string Search3 = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; private readonly ILogger _logger; @@ -61,19 +76,18 @@ namespace MediaBrowser.Providers.Tmdb.Movies var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - if (!string.IsNullOrWhiteSpace(name)) - { - var parsedName = _libraryManager.ParseName(name); - var yearInName = parsedName.Year; - name = parsedName.Name; - year = year ?? yearInName; - } + // Does this mean we are reparsing already parsed ItemLookupInfo? + var parsedName = _libraryManager.ParseName(name); + var yearInName = parsedName.Year; + name = parsedName.Name; + year = year ?? yearInName; - _logger.LogInformation("MovieDbProvider: Finding id for item: " + name); + _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); var language = idInfo.MetadataLanguage.ToLowerInvariant(); - //nope - search for it - //var searchType = item is BoxSet ? "collection" : "movie"; + // Replace sequences of non-word characters with space + // TMDB expects a space separated list of words make sure that is the case + name = cleanNonWord.Replace(name, " ").Trim(); var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); @@ -86,36 +100,35 @@ namespace MediaBrowser.Providers.Tmdb.Movies } } + // Ideally retrying alternatives should be done outside the search + // provider so that the retry logic can be common for all search + // providers if (results.Count == 0) { - // try with dot and _ turned to space - var originalName = name; - - name = name.Replace(",", " "); - name = name.Replace(".", " "); - name = name.Replace("_", " "); - name = name.Replace("-", " "); - name = name.Replace("!", " "); - name = name.Replace("?", " "); - - var parenthIndex = name.IndexOf('('); - if (parenthIndex != -1) - { - name = name.Substring(0, parenthIndex); - } + name = parsedName.Name; + + // Remove things enclosed in []{}() etc + name = cleanEnclosed.Replace(name, string.Empty); + // Replace sequences of non-word characters with space + name = cleanNonWord.Replace(name, " "); + + // Clean based on common stop words / tokens + name = cleanStopWords.Replace(name, string.Empty); + + // Trim whitespace name = name.Trim(); // Search again if the new name is different - if (!string.Equals(name, originalName)) + if (!string.Equals(name, parsedName.Name) && !string.IsNullOrWhiteSpace(name)) { + _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { //one more time, in english results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); - } } } From f7c44565fc8f7cdaa7e7c95f174227ab90dd4afe Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Thu, 7 May 2020 15:47:46 -0700 Subject: [PATCH 332/411] Rename member variables to conform to coding standard --- .../Tmdb/Movies/TmdbSearch.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index 08c1afec28..47d5012471 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -19,11 +19,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies { public class TmdbSearch { - private static readonly CultureInfo EnUs = new CultureInfo("en-US"); + private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private static readonly Regex cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); - private static readonly Regex cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); - private static readonly Regex cleanStopWords = new Regex(@"\b( # Start at word boundary + private static readonly Regex _cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); + private static readonly Regex _cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); + private static readonly Regex _cleanStopWords = new Regex(@"\b( # Start at word boundary 19[0-9]{2}|20[0-9]{2}| # 1900-2099 S[0-9]{2}| # Season E[0-9]{2}| # Episode @@ -34,7 +34,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies ).* # Match rest of string", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); - private const string Search3 = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; + private const string _searchURL = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; private readonly ILogger _logger; private readonly IJsonSerializer _json; @@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies // Replace sequences of non-word characters with space // TMDB expects a space separated list of words make sure that is the case - name = cleanNonWord.Replace(name, " ").Trim(); + name = _cleanNonWord.Replace(name, " ").Trim(); var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); @@ -108,13 +108,13 @@ namespace MediaBrowser.Providers.Tmdb.Movies name = parsedName.Name; // Remove things enclosed in []{}() etc - name = cleanEnclosed.Replace(name, string.Empty); + name = _cleanEnclosed.Replace(name, string.Empty); // Replace sequences of non-word characters with space - name = cleanNonWord.Replace(name, " "); + name = _cleanNonWord.Replace(name, " "); // Clean based on common stop words / tokens - name = cleanStopWords.Replace(name, string.Empty); + name = _cleanStopWords.Replace(name, string.Empty); // Trim whitespace name = name.Trim(); @@ -163,7 +163,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies throw new ArgumentException("name"); } - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); + var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { @@ -192,14 +192,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies if (!string.IsNullOrWhiteSpace(i.Release_Date)) { // These dates are always in this exact format - if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) + if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) { remoteResult.PremiereDate = r.ToUniversalTime(); remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture)); return remoteResult; @@ -216,7 +216,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies throw new ArgumentException("name"); } - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); + var url3 = string.Format(_searchURL, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions { @@ -245,14 +245,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) { // These dates are always in this exact format - if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) + if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) { remoteResult.PremiereDate = r.ToUniversalTime(); remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); + remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture)); return remoteResult; From a517bd2e52571dacf4a536f633d9102735422f13 Mon Sep 17 00:00:00 2001 From: Vasily <just.one.man@yandex.ru> Date: Fri, 8 May 2020 14:32:41 +0300 Subject: [PATCH 333/411] 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<bool> 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 <just.one.man@yandex.ru> Date: Fri, 8 May 2020 23:11:43 +0300 Subject: [PATCH 334/411] 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<bool> 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<string> { TempFilePath }).ConfigureAwait(false); From 0e8505fb961aa0e29528a8404ca6bc25a90f75a1 Mon Sep 17 00:00:00 2001 From: newton181 <newton181.1@gmail.com> Date: Fri, 8 May 2020 21:51:54 +0000 Subject: [PATCH 335/411] 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 <andras.szucs@bluewin.ch> Date: Fri, 8 May 2020 21:22:30 +0000 Subject: [PATCH 336/411] 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 <timothycheung824@gmail.com> Date: Sat, 9 May 2020 14:13:12 +0000 Subject: [PATCH 337/411] 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 <julien.machiels@protonmail.com> Date: Sat, 9 May 2020 19:49:55 +0200 Subject: [PATCH 338/411] 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 { + /// <summary> + /// Subtitle writer for the WebVTT format. + /// </summary> public class VttWriter : ISubtitleWriter { + /// <inheritdoc /> 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 <julien.machiels@protonmail.com> Date: Sun, 10 May 2020 13:53:04 +0200 Subject: [PATCH 339/411] 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<Series, SeriesInfo>, 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 (!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 (actor.Id > 0) + if (seriesInfo.Credits.Crew != null) + { + var keepTypes = new[] { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); - } + PersonType.Director, + PersonType.Writer, + PersonType.Producer + }; + + foreach (var person in seriesInfo.Credits.Crew) + { + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); - seriesResult.AddPerson(personInfo); + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + seriesResult.AddPerson(new PersonInfo { Name = person.Name.Trim(), Role = person.Job, Type = type }); + } } } } From d5ad53e4bb6bc60e9ab9e9f2a71a772bfe67c286 Mon Sep 17 00:00:00 2001 From: MrTimscampi <julien.machiels@protonmail.com> Date: Sun, 10 May 2020 15:16:19 +0200 Subject: [PATCH 340/411] 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 <barronpm@gmail.com> Date: Sun, 10 May 2020 10:54:41 -0400 Subject: [PATCH 341/411] 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 @@ -//------------------------------------------------------------------------------ -// <auto-generated> -// 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 -// </auto-generated> -//------------------------------------------------------------------------------ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore; - -namespace Jellyfin.Data.DbContexts -{ - /// <inheritdoc/> - public partial class Jellyfin : DbContext - { - #region DbSets - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Artwork> Artwork { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Book> Books { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.BookMetadata> BookMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Chapter> Chapters { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Collection> Collections { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CollectionItem> CollectionItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Company> Companies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CompanyMetadata> CompanyMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItem> CustomItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.CustomItemMetadata> CustomItemMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Episode> Episodes { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.EpisodeMetadata> EpisodeMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Genre> Genres { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Group> Groups { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Library> Libraries { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryItem> LibraryItems { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.LibraryRoot> LibraryRoot { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFile> MediaFiles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MediaFileStream> MediaFileStream { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Metadata> Metadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProvider> MetadataProviders { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MetadataProviderId> MetadataProviderIds { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Movie> Movies { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MovieMetadata> MovieMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbum> MusicAlbums { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.MusicAlbumMetadata> MusicAlbumMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Permission> Permissions { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Person> People { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PersonRole> PersonRoles { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Photo> Photo { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.PhotoMetadata> PhotoMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Preference> Preferences { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.ProviderMapping> ProviderMappings { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Rating> Ratings { get; set; } - - /// <summary> - /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to - /// store review ratings, not age ratings - /// </summary> - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.RatingSource> RatingSources { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Release> Releases { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Season> Seasons { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeasonMetadata> SeasonMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Series> Series { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.SeriesMetadata> SeriesMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.Track> Tracks { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.TrackMetadata> TrackMetadata { get; set; } - public virtual Microsoft.EntityFrameworkCore.DbSet<global::Jellyfin.Data.Entities.User> Users { get; set; } - #endregion DbSets - - /// <summary> - /// Default connection string - /// </summary> - public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; - - /// <inheritdoc /> - public Jellyfin(DbContextOptions<Jellyfin> options) : base(options) - { - } - - partial void CustomInit(DbContextOptionsBuilder optionsBuilder); - - /// <inheritdoc /> - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - CustomInit(optionsBuilder); - } - - partial void OnModelCreatingImpl(ModelBuilder modelBuilder); - partial void OnModelCreatedImpl(ModelBuilder modelBuilder); - - /// <inheritdoc /> - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - OnModelCreatingImpl(modelBuilder); - - modelBuilder.HasDefaultSchema("jellyfin"); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .ToTable("Artwork") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>().HasIndex(t => t.Kind); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Artwork>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() - .HasMany(x => x.BookMetadata) - .WithOne() - .HasForeignKey("BookMetadata_BookMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Book>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() - .Property(t => t.ISBN) - .HasField("_ISBN") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.BookMetadata>() - .HasMany(x => x.Publishers) - .WithOne() - .HasForeignKey("Company_Publishers_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .ToTable("Chapter") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.TimeStart) - .IsRequired() - .HasField("_TimeStart") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.TimeEnd) - .HasField("_TimeEnd") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Chapter>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .ToTable("Collection") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Collection>() - .HasMany(x => x.CollectionItem) - .WithOne() - .HasForeignKey("CollectionItem_CollectionItem_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .ToTable("CollectionItem") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.LibraryItem) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("LibraryItem_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.Next) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Next_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CollectionItem>() - .HasOne(x => x.Previous) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.CollectionItem>("CollectionItem_Previous_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .ToTable("Company") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .HasMany(x => x.CompanyMetadata) - .WithOne() - .HasForeignKey("CompanyMetadata_CompanyMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Company>() - .HasOne(x => x.Parent) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.Company>("Company_Parent_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Description) - .HasMaxLength(65535) - .HasField("_Description") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Headquarters) - .HasMaxLength(255) - .HasField("_Headquarters") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CompanyMetadata>() - .Property(t => t.Homepage) - .HasMaxLength(1024) - .HasField("_Homepage") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() - .HasMany(x => x.CustomItemMetadata) - .WithOne() - .HasForeignKey("CustomItemMetadata_CustomItemMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.CustomItem>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .Property(t => t.EpisodeNumber) - .HasField("_EpisodeNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Episode>() - .HasMany(x => x.EpisodeMetadata) - .WithOne() - .HasForeignKey("EpisodeMetadata_EpisodeMetadata_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.EpisodeMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .ToTable("Genre") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>().HasIndex(t => t.Name) - .IsUnique(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Genre>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .ToTable("Groups") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .Property(t => t.Name) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.GroupPermissions) - .WithOne() - .HasForeignKey("Permission_GroupPermissions_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Group>() - .HasMany(x => x.Preferences) - .WithOne() - .HasForeignKey("Preference_Preferences_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .ToTable("Library") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Library>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .ToTable("LibraryItem") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>().HasIndex(t => t.UrlId) - .IsUnique(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryItem>() - .HasOne(x => x.LibraryRoot) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.LibraryItem>("LibraryRoot_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .ToTable("LibraryRoot") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.NetworkPath) - .HasMaxLength(65535) - .HasField("_NetworkPath") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.LibraryRoot>() - .HasOne(x => x.Library) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.LibraryRoot>("Library_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .ToTable("MediaFile") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Path) - .HasMaxLength(65535) - .IsRequired() - .HasField("_Path") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFile>() - .HasMany(x => x.MediaFileStreams) - .WithOne() - .HasForeignKey("MediaFileStream_MediaFileStreams_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .ToTable("MediaFileStream") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.StreamNumber) - .IsRequired() - .HasField("_StreamNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MediaFileStream>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .ToTable("Metadata") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Title) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Title") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.OriginalTitle) - .HasMaxLength(1024) - .HasField("_OriginalTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.SortTitle) - .HasMaxLength(1024) - .HasField("_SortTitle") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Language) - .HasMaxLength(3) - .IsRequired() - .HasField("_Language") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.ReleaseDate) - .HasField("_ReleaseDate") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.PersonRoles) - .WithOne() - .HasForeignKey("PersonRole_PersonRoles_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Genres) - .WithOne() - .HasForeignKey("Genre_Genres_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Artwork) - .WithOne() - .HasForeignKey("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Ratings) - .WithOne() - .HasForeignKey("Rating_Ratings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Metadata>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .ToTable("MetadataProvider") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProvider>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .ToTable("MetadataProviderId") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.ProviderId) - .HasMaxLength(255) - .IsRequired() - .HasField("_ProviderId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MetadataProviderId>() - .HasOne(x => x.MetadataProvider) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.MetadataProviderId>("MetadataProvider_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Movie>() - .HasMany(x => x.MovieMetadata) - .WithOne() - .HasForeignKey("MovieMetadata_MovieMetadata_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MovieMetadata>() - .HasMany(x => x.Studios) - .WithOne() - .HasForeignKey("Company_Studios_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() - .HasMany(x => x.MusicAlbumMetadata) - .WithOne() - .HasForeignKey("MusicAlbumMetadata_MusicAlbumMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbum>() - .HasMany(x => x.Tracks) - .WithOne() - .HasForeignKey("Track_Tracks_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.Barcode) - .HasMaxLength(255) - .HasField("_Barcode") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.LabelNumber) - .HasMaxLength(255) - .HasField("_LabelNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.MusicAlbumMetadata>() - .HasMany(x => x.Labels) - .WithOne() - .HasForeignKey("Company_Labels_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .ToTable("Permissions") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Kind) - .IsRequired() - .HasField("_Kind") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>() - .Property(t => t.Value) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Permission>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .ToTable("Person") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.UrlId) - .IsRequired() - .HasField("_UrlId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.SourceId) - .HasMaxLength(255) - .HasField("_SourceId") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.DateAdded) - .IsRequired() - .HasField("_DateAdded") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.DateModified) - .IsRequired() - .HasField("_DateModified") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Person>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .ToTable("PersonRole") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Role) - .HasMaxLength(1024) - .HasField("_Role") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Type) - .IsRequired() - .HasField("_Type") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasOne(x => x.Person) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Person_Id") - .IsRequired() - .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasOne(x => x.Artwork) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.PersonRole>("Artwork_Artwork_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.PersonRole>() - .HasMany(x => x.Sources) - .WithOne() - .HasForeignKey("MetadataProviderId_Sources_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() - .HasMany(x => x.PhotoMetadata) - .WithOne() - .HasForeignKey("PhotoMetadata_PhotoMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Photo>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .ToTable("Preferences") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Kind) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>() - .Property(t => t.Value) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Preference>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .ToTable("ProviderMappings") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderName) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderSecrets) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>() - .Property(t => t.ProviderData) - .HasMaxLength(65535) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.ProviderMapping>().Property<byte[]>("Timestamp").IsConcurrencyToken(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .ToTable("Rating") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Value) - .IsRequired() - .HasField("_Value") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Votes) - .HasField("_Votes") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Rating>() - .HasOne(x => x.RatingType) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.Rating>("RatingSource_RatingType_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .ToTable("RatingType") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Name) - .HasMaxLength(1024) - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.MaximumValue) - .IsRequired() - .HasField("_MaximumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.MinimumValue) - .IsRequired() - .HasField("_MinimumValue") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.RatingSource>() - .HasOne(x => x.Source) - .WithOne() - .HasForeignKey<global::Jellyfin.Data.Entities.RatingSource>("MetadataProviderId_Source_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .ToTable("Release") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Id) - .IsRequired() - .HasField("_Id") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Name) - .HasMaxLength(1024) - .IsRequired() - .HasField("_Name") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .Property(t => t.Timestamp) - .IsRequired() - .HasField("_Timestamp") - .UsePropertyAccessMode(PropertyAccessMode.Property) - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .HasMany(x => x.MediaFiles) - .WithOne() - .HasForeignKey("MediaFile_MediaFiles_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Release>() - .HasMany(x => x.Chapters) - .WithOne() - .HasForeignKey("Chapter_Chapters_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .Property(t => t.SeasonNumber) - .HasField("_SeasonNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .HasMany(x => x.SeasonMetadata) - .WithOne() - .HasForeignKey("SeasonMetadata_SeasonMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Season>() - .HasMany(x => x.Episodes) - .WithOne() - .HasForeignKey("Episode_Episodes_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeasonMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.AirsDayOfWeek) - .HasField("_AirsDayOfWeek") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.AirsTime) - .HasField("_AirsTime") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .Property(t => t.FirstAired) - .HasField("_FirstAired") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .HasMany(x => x.SeriesMetadata) - .WithOne() - .HasForeignKey("SeriesMetadata_SeriesMetadata_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Series>() - .HasMany(x => x.Seasons) - .WithOne() - .HasForeignKey("Season_Seasons_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Outline) - .HasMaxLength(1024) - .HasField("_Outline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Plot) - .HasMaxLength(65535) - .HasField("_Plot") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Tagline) - .HasMaxLength(1024) - .HasField("_Tagline") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .Property(t => t.Country) - .HasMaxLength(2) - .HasField("_Country") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.SeriesMetadata>() - .HasMany(x => x.Networks) - .WithOne() - .HasForeignKey("Company_Networks_Id") - .IsRequired(); - - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .Property(t => t.TrackNumber) - .HasField("_TrackNumber") - .UsePropertyAccessMode(PropertyAccessMode.Property); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .HasMany(x => x.Releases) - .WithOne() - .HasForeignKey("Release_Releases_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.Track>() - .HasMany(x => x.TrackMetadata) - .WithOne() - .HasForeignKey("TrackMetadata_TrackMetadata_Id") - .IsRequired(); - - - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .ToTable("Users") - .HasKey(t => t.Id); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Id) - .IsRequired() - .ValueGeneratedOnAdd(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.LastLoginTimestamp) - .IsRequired() - .IsRowVersion(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Username) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.Password) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.MustUpdatePassword) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.AudioLanguagePreference) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.AuthenticationProviderId) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.GroupedFolders) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.InvalidLoginAttemptCount) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.LatestItemExcludes) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.MyMediaExcludes) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.OrderedViews) - .HasMaxLength(65535); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.SubtitleMode) - .HasMaxLength(255) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.PlayDefaultAudioTrack) - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .Property(t => t.SubtitleLanguagePrefernce) - .HasMaxLength(255); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.Groups) - .WithOne() - .HasForeignKey("Group_Groups_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.Permissions) - .WithOne() - .HasForeignKey("Permission_Permissions_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .HasMany(x => x.ProviderMappings) - .WithOne() - .HasForeignKey("ProviderMapping_ProviderMappings_Id") - .IsRequired(); - modelBuilder.Entity<global::Jellyfin.Data.Entities.User>() - .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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected Book() : base() + protected Book() { BookMetadata = new HashSet<BookMetadata>(); Releases = new HashSet<Release>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected BookMetadata() : base() + protected BookMetadata() { Publishers = new HashSet<Company>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected CustomItem() : base() + protected CustomItem() { CustomItemMetadata = new HashSet<CustomItemMetadata>(); Releases = new HashSet<Release>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected Movie() : base() + protected Movie() { Releases = new HashSet<Release>(); MovieMetadata = new HashSet<MovieMetadata>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected MovieMetadata() : base() + protected MovieMetadata() { Studios = new HashSet<Company>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected MusicAlbum() : base() + protected MusicAlbum() { MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>(); Tracks = new HashSet<Track>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected MusicAlbumMetadata() : base() + protected MusicAlbumMetadata() { Labels = new HashSet<Company>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected Photo() : base() + protected Photo() { PhotoMetadata = new HashSet<PhotoMetadata>(); Releases = new HashSet<Release>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 { /// <summary> /// This is the entity to store review ratings, not age ratings /// </summary> - [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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected Series() : base() + protected Series() { SeriesMetadata = new HashSet<SeriesMetadata>(); Seasons = new HashSet<Season>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - protected SeriesMetadata() : base() + protected SeriesMetadata() { Networks = new HashSet<Company>(); 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 /// <summary> /// Default constructor. Protected due to required properties, but present because EF needs it. /// </summary> - 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 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netcoreapp3.1</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + </PropertyGroup> + + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + + <!-- Code analysers--> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + </ItemGroup> + + <ItemGroup> + <Compile Include="..\SharedVersion.cs" /> + <Compile Remove="Migrations\20200430214405_InitialSchema.cs" /> + <Compile Remove="Migrations\20200430214405_InitialSchema.Designer.cs" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" /> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + </ItemGroup> + +</Project> 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 +{ + /// <inheritdoc/> + public partial class JellyfinDb : DbContext + { + /*public virtual DbSet<Artwork> Artwork { get; set; } + public virtual DbSet<Book> Books { get; set; } + public virtual DbSet<BookMetadata> BookMetadata { get; set; } + public virtual DbSet<Chapter> Chapters { get; set; } + public virtual DbSet<Collection> Collections { get; set; } + public virtual DbSet<CollectionItem> CollectionItems { get; set; } + public virtual DbSet<Company> Companies { get; set; } + public virtual DbSet<CompanyMetadata> CompanyMetadata { get; set; } + public virtual DbSet<CustomItem> CustomItems { get; set; } + public virtual DbSet<CustomItemMetadata> CustomItemMetadata { get; set; } + public virtual DbSet<Episode> Episodes { get; set; } + public virtual DbSet<EpisodeMetadata> EpisodeMetadata { get; set; } + public virtual DbSet<Genre> Genres { get; set; } + public virtual DbSet<Group> Groups { get; set; } + public virtual DbSet<Library> Libraries { get; set; } + public virtual DbSet<LibraryItem> LibraryItems { get; set; } + public virtual DbSet<LibraryRoot> LibraryRoot { get; set; } + public virtual DbSet<MediaFile> MediaFiles { get; set; } + public virtual DbSet<MediaFileStream> MediaFileStream { get; set; } + public virtual DbSet<Metadata> Metadata { get; set; } + public virtual DbSet<MetadataProvider> MetadataProviders { get; set; } + public virtual DbSet<MetadataProviderId> MetadataProviderIds { get; set; } + public virtual DbSet<Movie> Movies { get; set; } + public virtual DbSet<MovieMetadata> MovieMetadata { get; set; } + public virtual DbSet<MusicAlbum> MusicAlbums { get; set; } + public virtual DbSet<MusicAlbumMetadata> MusicAlbumMetadata { get; set; } + public virtual DbSet<Permission> Permissions { get; set; } + public virtual DbSet<Person> People { get; set; } + public virtual DbSet<PersonRole> PersonRoles { get; set; } + public virtual DbSet<Photo> Photo { get; set; } + public virtual DbSet<PhotoMetadata> PhotoMetadata { get; set; } + public virtual DbSet<Preference> Preferences { get; set; } + public virtual DbSet<ProviderMapping> ProviderMappings { get; set; } + public virtual DbSet<Rating> Ratings { get; set; } + /// <summary> + /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to + /// store review ratings, not age ratings + /// </summary> + public virtual DbSet<RatingSource> RatingSources { get; set; } + public virtual DbSet<Release> Releases { get; set; } + public virtual DbSet<Season> Seasons { get; set; } + public virtual DbSet<SeasonMetadata> SeasonMetadata { get; set; } + public virtual DbSet<Series> Series { get; set; } + public virtual DbSet<SeriesMetadata> SeriesMetadata { get; set; } + public virtual DbSet<Track> Tracks { get; set; } + public virtual DbSet<TrackMetadata> TrackMetadata { get; set; } + public virtual DbSet<User> Users { get; set; } */ + + /// <summary> + /// Gets or sets the default connection string. + /// </summary> + public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; + + /// <inheritdoc /> + public JellyfinDb(DbContextOptions<JellyfinDb> options) : base(options) + { + } + + partial void CustomInit(DbContextOptionsBuilder optionsBuilder); + + /// <inheritdoc /> + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + CustomInit(optionsBuilder); + } + + partial void OnModelCreatingImpl(ModelBuilder modelBuilder); + partial void OnModelCreatedImpl(ModelBuilder modelBuilder); + + /// <inheritdoc /> + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + OnModelCreatingImpl(modelBuilder); + + modelBuilder.HasDefaultSchema("jellyfin"); + + /*modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind); + modelBuilder.Entity<Genre>().HasIndex(t => t.Name) + .IsUnique(); + modelBuilder.Entity<LibraryItem>().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 <marknr.monteiro@protonmail.com> Date: Sun, 10 May 2020 14:36:11 -0400 Subject: [PATCH 342/411] 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 | 30 +++++++++---------- .../Browser/BrowserLauncher.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../HdHomerun/HdHomerunUdpStream.cs | 2 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 2 +- .../IServerApplicationHost.cs | 25 ++++++++++++---- 6 files changed, 39 insertions(+), 24 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(); } /// <inheritdoc/> - public string GetLocalApiUrl(ReadOnlySpan<char> host) + public Uri GetLoopbackHttpApiUrl() { - var url = new StringBuilder(64); - url.Append(ListenWithHttps ? "https://" : "http://") - .Append(host) - .Append(':') - .Append(ListenWithHttps ? HttpsPort : HttpPort); - - string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; - if (baseUrl.Length != 0) - { - url.Append(baseUrl); - } + return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); + } - return url.ToString(); + /// <inheritdoc/> + public Uri GetLocalApiUrl(ReadOnlySpan<char> host, string scheme = null, int? port = null) + { + return new UriBuilder + { + Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), + Host = host.ToString(), + Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), + Path = ServerConfigurationManager.Configuration.BaseUrl + }.Uri; } public Task<List<IPAddress>> 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 /// <summary> /// 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 <see cref="GetLocalIpAddresses"/>. + /// IP address that can be found via <see cref="GetLocalIpAddresses"/>. HTTPS will be preferred when available. /// </summary> /// <param name="cancellationToken">A cancellation token that can be used to cancel the task.</param> /// <returns>The server URL.</returns> Task<string> GetLocalApiUrl(CancellationToken cancellationToken); /// <summary> - /// 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). /// </summary> - /// <param name="hostname">The hostname to use in the URL.</param> /// <returns>The API URL.</returns> - string GetLocalApiUrl(ReadOnlySpan<char> hostname); + public Uri GetLoopbackHttpApiUrl(); /// <summary> - /// 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. /// </summary> /// <param name="address">The IP address to use as the hostname in the URL.</param> /// <returns>The API URL.</returns> string GetLocalApiUrl(IPAddress address); + /// <summary> + /// Gets a local (LAN) URL that can be used to access the API. + /// </summary> + /// <param name="hostname">The hostname to use in the URL.</param> + /// <param name="scheme"> + /// The scheme to use for the URL. If null, the scheme will be selected automatically, + /// preferring HTTPS, if available. + /// </param> + /// <param name="port"> + /// The port to use for the URL. If null, the port will be selected automatically, + /// preferring the HTTPS port, if available. + /// </param> + /// <returns>The API URL.</returns> + Uri GetLocalApiUrl(ReadOnlySpan<char> hostname, string scheme = null, int? port = null); + /// <summary> /// Open a URL in an external browser window. /// </summary> From 3abf870c1e321dbeb484e4e255000a27760d7bc9 Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 10 May 2020 18:07:56 -0400 Subject: [PATCH 343/411] 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()); } /// <inheritdoc/> - public Uri GetLoopbackHttpApiUrl() + public string GetLoopbackHttpApiUrl() { return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort); } /// <inheritdoc/> - public Uri GetLocalApiUrl(ReadOnlySpan<char> host, string scheme = null, int? port = null) + public string GetLocalApiUrl(ReadOnlySpan<char> 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<List<IPAddress>> 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. /// </summary> /// <param name="appHost">The application host.</param> - /// <param name="url">The URL.</param> - private static void TryOpenUrl(IServerApplicationHost appHost, string url) + /// <param name="relativeUrl">The URL to open, relative to the server base URL.</param> + 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<ILogger>(); - 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). /// </summary> /// <returns>The API URL.</returns> - public Uri GetLoopbackHttpApiUrl(); + string GetLoopbackHttpApiUrl(); /// <summary> /// 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. /// </param> /// <returns>The API URL.</returns> - Uri GetLocalApiUrl(ReadOnlySpan<char> hostname, string scheme = null, int? port = null); + string GetLocalApiUrl(ReadOnlySpan<char> hostname, string scheme = null, int? port = null); /// <summary> /// Open a URL in an external browser window. From a10aec695671cd2e90ff67bd7bc6e18b450a8b3f Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 10 May 2020 18:17:12 -0400 Subject: [PATCH 344/411] 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 <federico.antoniazzi@outlook.com> Date: Mon, 11 May 2020 10:56:05 +0000 Subject: [PATCH 345/411] 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 <millallo@tiscali.it> Date: Mon, 11 May 2020 20:17:41 +0000 Subject: [PATCH 346/411] 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 <julien.machiels@protonmail.com> Date: Tue, 12 May 2020 15:05:58 +0200 Subject: [PATCH 347/411] 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<Series, SeriesInfo>, 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 { + /// <summary> + /// Utilities for the TMDb provider + /// </summary> public static class TmdbUtils { + /// <summary> + /// URL of the TMDB instance to use. + /// </summary> public const string BaseTmdbUrl = "https://www.themoviedb.org/"; + + /// <summary> + /// URL of the TMDB API instance to use. + /// </summary> public const string BaseTmdbApiUrl = "https://api.themoviedb.org/"; + + /// <summary> + /// Name of the provider. + /// </summary> public const string ProviderName = "TheMovieDb"; + + /// <summary> + /// API key to use when performing an API call. + /// </summary> public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; + + /// <summary> + /// Value of the Accept header for requests to the provider. + /// </summary> public const string AcceptHeader = "application/json,image/*"; + /// <summary> + /// Maps the TMDB provided roles for crew members to Jellyfin roles. + /// </summary> + /// <param name="crew">Crew member to map against the Jellyfin person types.</param> + /// <returns>The Jellyfin person type.</returns> 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 <rapmue@gmail.com> Date: Tue, 12 May 2020 13:55:09 +0000 Subject: [PATCH 348/411] 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 <barronpm@gmail.com> Date: Tue, 12 May 2020 12:50:17 -0400 Subject: [PATCH 349/411] 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) }; /// <summary> 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<MigrateActivityLogDb> _logger; + private readonly JellyfinDbProvider _provider; + private readonly IServerApplicationPaths _paths; + + public MigrateActivityLogDb(ILogger<MigrateActivityLogDb> 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<JellyfinDb>(); + _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 <barronpm@gmail.com> Date: Tue, 12 May 2020 16:03:15 -0400 Subject: [PATCH 350/411] 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<IJsonSerializer, JsonSerializer>(); - // TODO: Remove support for injecting ILogger completely - serviceCollection.AddSingleton((provider) => - { - Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger<T>"); - return Logger; - }); - serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton<TvdbClientManager>(); 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 /// </summary> public class MusicAlbumResolver : ItemResolver<MusicAlbum> { - private readonly ILogger _logger; + private readonly ILogger<MusicAlbumResolver> _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; @@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <param name="logger">The logger.</param> /// <param name="fileSystem">The file system.</param> /// <param name="libraryManager">The library manager.</param> - public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager) + public MusicAlbumResolver(ILogger<MusicAlbumResolver> 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 /// </summary> public class MusicArtistResolver : ItemResolver<MusicArtist> { - private readonly ILogger _logger; + private readonly ILogger<MusicAlbumResolver> _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; @@ -23,12 +23,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <summary> /// Initializes a new instance of the <see cref="MusicArtistResolver"/> class. /// </summary> - /// <param name="logger">The logger.</param> + /// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param> /// <param name="fileSystem">The file system.</param> /// <param name="libraryManager">The library manager.</param> /// <param name="config">The configuration manager.</param> public MusicArtistResolver( - ILogger<MusicArtistResolver> logger, + ILogger<MusicAlbumResolver> 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<M3UTunerHost> 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 /// </summary> private readonly ILibraryManager _libraryManager; + /// <summary> + /// The logger for the created <see cref="ItemsService"/> instances. + /// </summary> + private readonly ILogger<ItemsService> _logger; + private readonly IDtoService _dtoService; private readonly ILocalizationManager _localizationManager; private readonly IJsonSerializer _json; private readonly IAuthorizationContext _authContext; public TrailersService( - ILogger<TrailersService> 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<TrailersService>(), serverConfigurationManager, httpResultFactory) { _userManager = userManager; _libraryManager = libraryManager; @@ -56,6 +61,7 @@ namespace MediaBrowser.Api.Movies _localizationManager = localizationManager; _json = json; _authContext = authContext; + _logger = loggerFactory.CreateLogger<ItemsService>(); } 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 /// <param name="localization">The localization.</param> /// <param name="dtoService">The dto service.</param> public ItemsService( - ILogger logger, + ILogger<ItemsService> 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<Episode, EpisodeInfo>, - IHasOrder + public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, 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<IEnumerable<RemoteSearchResult>> 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<HttpResponseInfo> 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 /// <value>The results.</value> public List<SearchResult> Search { get; set; } } - } } From e69ba3531e2cc93c79ccc10b828d563c96753d10 Mon Sep 17 00:00:00 2001 From: D Z <dzevel5@yahoo.com> Date: Tue, 12 May 2020 20:42:04 +0000 Subject: [PATCH 351/411] 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 <tanvir.siddique@adnsl.net> Date: Tue, 12 May 2020 21:31:54 +0000 Subject: [PATCH 352/411] 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 <marknr.monteiro@protonmail.com> Date: Wed, 13 May 2020 09:46:29 -0400 Subject: [PATCH 353/411] Apply documentation suggestions from code review Co-authored-by: Vasily <JustAMan@users.noreply.github.com> --- 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<string> GetLocalApiUrl(CancellationToken cancellationToken); /// <summary> - /// 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). /// </summary> /// <returns>The API URL.</returns> @@ -87,6 +87,7 @@ namespace MediaBrowser.Controller /// <summary> /// 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. /// </summary> /// <param name="hostname">The hostname to use in the URL.</param> /// <param name="scheme"> From 512725a7d1c1b45fa588d76046c262b96614f58a Mon Sep 17 00:00:00 2001 From: MrTimscampi <julien.machiels@protonmail.com> Date: Wed, 13 May 2020 18:02:54 +0200 Subject: [PATCH 354/411] 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 <barronpm@gmail.com> Date: Wed, 13 May 2020 15:03:35 -0400 Subject: [PATCH 355/411] 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 /// </summary> public sealed class ActivityLogEntryPoint : IServerEntryPoint { - private readonly ILogger _logger; + private readonly ILogger<ActivityLogEntryPoint> _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; /// <summary> /// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="sessionManager">The session manager.</param> - /// <param name="deviceManager">The device manager.</param> /// <param name="taskManager">The task manager.</param> /// <param name="activityManager">The activity manager.</param> /// <param name="localization">The localization manager.</param> @@ -55,7 +52,6 @@ namespace Emby.Server.Implementations.Activity public ActivityLogEntryPoint( ILogger<ActivityLogEntryPoint> 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<CameraImageUploadInfo> 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<MediaBrowser.Controller.Entities.User> 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<MediaBrowser.Controller.Entities.User> 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; } /// <summary> @@ -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<GenericEventArgs<Tuple<string, DeviceOptions>>> DeviceOptionsUpdated; - public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> 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<ContentUploadHistory>(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<CameraImageUploadInfo> - { - 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<ContentUploadHistory>(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 /// </summary> /// <param name="name"></param> /// <param name="type"></param> - /// <param name="userid"></param> + /// <param name="userId"></param> /// <param name="datecreated"></param> - /// <param name="logseverity"></param> - public ActivityLog(string name, string type, Guid userid, DateTime datecreated, Microsoft.Extensions.Logging.LogLevel logseverity) + /// <param name="logSeverity"></param> + public ActivityLog(string name, string type, Guid userId) { - 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; + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } - this.DateCreated = datecreated; - - this.LogSeverity = logseverity; + if (string.IsNullOrEmpty(type)) + { + throw new ArgumentNullException(nameof(type)); + } + this.Name = name; + this.Type = type; + this.UserId = userId; + this.DateCreated = DateTime.UtcNow; + this.LogSeverity = LogLevel.Trace; Init(); } @@ -61,12 +59,12 @@ namespace Jellyfin.Data.Entities /// </summary> /// <param name="name"></param> /// <param name="type"></param> - /// <param name="userid"></param> + /// <param name="userId"></param> /// <param name="datecreated"></param> /// <param name="logseverity"></param> - 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 /// </summary> [Required] - public Microsoft.Extensions.Logging.LogLevel LogSeverity { get; set; } + public LogLevel LogSeverity { get; set; } /// <summary> - /// Required, ConcurrenyToken + /// Required, ConcurrencyToken. /// </summary> [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 /// </summary> public class ActivityManager : IActivityManager { - private JellyfinDbProvider _provider; + private readonly JellyfinDbProvider _provider; /// <summary> /// Initializes a new instance of the <see cref="ActivityManager"/> 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 { + /// <summary> + /// The migration routine for migrating the activity log database to EF Core. + /// </summary> 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; + /// <summary> + /// Initializes a new instance of the <see cref="MigrateActivityLogDb"/> class. + /// </summary> + /// <param name="logger">The logger.</param> + /// <param name="paths">The server application paths.</param> + /// <param name="provider">The database provider.</param> public MigrateActivityLogDb(ILogger<MigrateActivityLogDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider) { _logger = logger; @@ -27,19 +35,35 @@ namespace Jellyfin.Server.Migrations.Routines _paths = paths; } + /// <inheritdoc/> public Guid Id => Guid.Parse("3793eb59-bc8c-456c-8b9f-bd5a62a42978"); + /// <inheritdoc/> public string Name => "MigrateActivityLogDatabase"; + /// <inheritdoc/> public void Perform() { + var logLevelDictionary = new Dictionary<string, LogLevel>(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 { - /// <summary> - /// Occurs when [camera image uploaded]. - /// </summary> - event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded; - /// <summary> /// Saves the capabilities. /// </summary> @@ -45,22 +40,6 @@ namespace MediaBrowser.Controller.Devices /// <returns>IEnumerable<DeviceInfo>.</returns> QueryResult<DeviceInfo> GetDevices(DeviceQuery query); - /// <summary> - /// Gets the upload history. - /// </summary> - /// <param name="deviceId">The device identifier.</param> - /// <returns>ContentUploadHistory.</returns> - ContentUploadHistory GetCameraUploadHistory(string deviceId); - - /// <summary> - /// Accepts the upload. - /// </summary> - /// <param name="deviceId">The device identifier.</param> - /// <param name="stream">The stream.</param> - /// <param name="file">The file.</param> - /// <returns>Task.</returns> - Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file); - /// <summary> /// Determines whether this instance [can access device] the specified user identifier. /// </summary> 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 <barronpm@gmail.com> Date: Wed, 13 May 2020 15:35:14 -0400 Subject: [PATCH 356/411] 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<string, string, string> 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<string, string, string>(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 <barronpm@gmail.com> Date: Wed, 13 May 2020 17:55:31 -0400 Subject: [PATCH 357/411] 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<string, ClientCapabilities> _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<string, ClientCapabilities>(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<Folder>() - .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<DeviceManagerEntryPoint> 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<ConfigurationStore> 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<DevicesOptions>("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 <aled@wibblr.com> Date: Wed, 13 May 2020 23:59:19 +0100 Subject: [PATCH 358/411] 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 <julien.machiels@protonmail.com> Date: Thu, 14 May 2020 18:11:32 +0200 Subject: [PATCH 359/411] 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 /// <value>The disposition.</value> [JsonPropertyName("disposition")] public IReadOnlyDictionary<string, int> Disposition { get; set; } + + /// <summary> + /// Gets or sets the color transfer. + /// </summary> + /// <value>The color transfer.</value> + [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 <julien.machiels@protonmail.com> Date: Thu, 14 May 2020 18:44:51 +0200 Subject: [PATCH 360/411] 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 /// <value>The language.</value> public string Language { get; set; } + /// <summary> + /// Gets or sets the color transfer. + /// </summary> + /// <value>The color transfer.</value> public string ColorTransfer { get; set; } + + /// <summary> + /// Gets or sets the color primaries. + /// </summary> + /// <value>The color primaries.</value> public string ColorPrimaries { get; set; } + + /// <summary> + /// Gets or sets the color space. + /// </summary> + /// <value>The color space.</value> public string ColorSpace { get; set; } /// <summary> @@ -44,11 +58,28 @@ namespace MediaBrowser.Model.Entities /// <value>The comment.</value> public string Comment { get; set; } + /// <summary> + /// Gets or sets the time base. + /// </summary> + /// <value>The time base.</value> public string TimeBase { get; set; } + + /// <summary> + /// Gets or sets the codec time base. + /// </summary> + /// <value>The codec time base.</value> public string CodecTimeBase { get; set; } + /// <summary> + /// Gets or sets the title. + /// </summary> + /// <value>The title.</value> public string Title { get; set; } + /// <summary> + /// Gets or sets the video range. + /// </summary> + /// <value>The video range.</value> 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 <julien.machiels@protonmail.com> Date: Thu, 14 May 2020 18:52:42 +0200 Subject: [PATCH 361/411] 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 /// <value>The color transfer.</value> [JsonPropertyName("color_transfer")] public string ColorTransfer { get; set; } + + /// <summary> + /// Gets or sets the color transfer. + /// </summary> + /// <value>The color transfer.</value> + [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 <aled@wibblr.com> Date: Thu, 14 May 2020 18:59:10 +0100 Subject: [PATCH 362/411] 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 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> <PropertyGroup> @@ -21,5 +21,15 @@ <ItemGroup> <ProjectReference Include="..\..\Emby.Naming\Emby.Naming.csproj" /> </ItemGroup> + + <!-- Code Analyzers--> + <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> + <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> + </ItemGroup> + + <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> + <CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> </Project> 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 fa1fef109911c734657f854f259681000a75f13a Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Thu, 14 May 2020 11:56:25 -0700 Subject: [PATCH 363/411] Update MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs Co-authored-by: Vasily <JustAMan@users.noreply.github.com> --- MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index 47d5012471..b6c7226597 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -80,7 +80,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies var parsedName = _libraryManager.ParseName(name); var yearInName = parsedName.Year; name = parsedName.Name; - year = year ?? yearInName; + year ??= yearInName; _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); var language = idInfo.MetadataLanguage.ToLowerInvariant(); From de351839033815ad0e1ee15e3e0b5cc095065d25 Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Thu, 14 May 2020 11:56:31 -0700 Subject: [PATCH 364/411] Update MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs Co-authored-by: Vasily <JustAMan@users.noreply.github.com> --- MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index b6c7226597..aa42fd81ab 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -100,7 +100,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies } } - // Ideally retrying alternatives should be done outside the search + // TODO: retrying alternatives should be done outside the search // provider so that the retry logic can be common for all search // providers if (results.Count == 0) From 4eb4ad3be7909f7a42aadcd442c0c7b77ce63c01 Mon Sep 17 00:00:00 2001 From: artiume <siderite@gmail.com> Date: Thu, 14 May 2020 17:03:53 -0400 Subject: [PATCH 365/411] 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<Book> { - 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 <barronpm@gmail.com> Date: Thu, 14 May 2020 17:13:45 -0400 Subject: [PATCH 366/411] 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<IJsonSerializer, JsonSerializer>(); - // TODO: properly set up scoping and switch to AddDbContextPool - serviceCollection.AddDbContext<JellyfinDb>( - options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), - ServiceLifetime.Transient); - - serviceCollection.AddSingleton<JellyfinDbProvider>(); - serviceCollection.AddSingleton(_fileSystemManager); serviceCollection.AddSingleton<TvdbClientManager>(); @@ -664,8 +655,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); - serviceCollection.AddSingleton<IActivityManager, ActivityManager>(); - serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>(); serviceCollection.AddSingleton<ISessionContext, SessionContext>(); 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 @@ <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" /> <ProjectReference Include="..\Jellyfin.Api\Jellyfin.Api.csproj" /> - <ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> @@ -51,7 +50,7 @@ </ItemGroup> <PropertyGroup> - <TargetFramework>netcoreapp3.1</TargetFramework> + <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> 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 + /// <summary> + /// An entity referencing an activity log entry. + /// </summary> + public partial class ActivityLog : ISavingChanges { - partial void Init(); - - /// <summary> - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// </summary> - protected ActivityLog() - { - Init(); - } - - /// <summary> - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// </summary> - public static ActivityLog CreateActivityLogUnsafe() - { - return new ActivityLog(); - } - /// <summary> - /// Public constructor with required data + /// Initializes a new instance of the <see cref="ActivityLog"/> class. + /// Public constructor with required data. /// </summary> - /// <param name="name"></param> - /// <param name="type"></param> - /// <param name="userId"></param> - /// <param name="datecreated"></param> - /// <param name="logSeverity"></param> + /// <param name="name">The name.</param> + /// <param name="type">The type.</param> + /// <param name="userId">The user id.</param> public ActivityLog(string name, string type, Guid userId) { if (string.IsNullOrEmpty(name)) @@ -54,14 +38,21 @@ namespace Jellyfin.Data.Entities Init(); } + /// <summary> + /// Initializes a new instance of the <see cref="ActivityLog"/> class. + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// </summary> + protected ActivityLog() + { + Init(); + } + /// <summary> /// Static create function (for use in LINQ queries, etc.) /// </summary> - /// <param name="name"></param> - /// <param name="type"></param> - /// <param name="userId"></param> - /// <param name="datecreated"></param> - /// <param name="logseverity"></param> + /// <param name="name">The name.</param> + /// <param name="type">The type.</param> + /// <param name="userId">The user's id.</param> public static ActivityLog Create(string name, string type, Guid userId) { return new ActivityLog(name, type, userId); @@ -72,7 +63,8 @@ namespace Jellyfin.Data.Entities *************************************************************************/ /// <summary> - /// Identity, Indexed, Required + /// Gets the identity of this instance. + /// This is the key in the backing database. /// </summary> [Key] [Required] @@ -80,7 +72,8 @@ namespace Jellyfin.Data.Entities public int Id { get; protected set; } /// <summary> - /// Required, Max length = 512 + /// Gets or sets the name. + /// Required, Max length = 512. /// </summary> [Required] [MaxLength(512)] @@ -88,21 +81,24 @@ namespace Jellyfin.Data.Entities public string Name { get; set; } /// <summary> - /// Max length = 512 + /// Gets or sets the overview. + /// Max length = 512. /// </summary> [MaxLength(512)] [StringLength(512)] public string Overview { get; set; } /// <summary> - /// Max length = 512 + /// Gets or sets the short overview. + /// Max length = 512. /// </summary> [MaxLength(512)] [StringLength(512)] public string ShortOverview { get; set; } /// <summary> - /// Required, Max length = 256 + /// Gets or sets the type. + /// Required, Max length = 256. /// </summary> [Required] [MaxLength(256)] @@ -110,41 +106,48 @@ namespace Jellyfin.Data.Entities public string Type { get; set; } /// <summary> - /// Required + /// Gets or sets the user id. + /// Required. /// </summary> [Required] public Guid UserId { get; set; } /// <summary> - /// Max length = 256 + /// Gets or sets the item id. + /// Max length = 256. /// </summary> [MaxLength(256)] [StringLength(256)] public string ItemId { get; set; } /// <summary> - /// Required + /// Gets or sets the date created. This should be in UTC. + /// Required. /// </summary> [Required] public DateTime DateCreated { get; set; } /// <summary> - /// Required + /// Gets or sets the log severity. Default is <see cref="LogLevel.Trace"/>. + /// Required. /// </summary> [Required] public LogLevel LogSeverity { get; set; } /// <summary> + /// Gets or sets the row version. /// Required, ConcurrencyToken. /// </summary> [ConcurrencyCheck] [Required] public uint RowVersion { get; set; } + partial void Init(); + + /// <inheritdoc /> 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 @@ <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" /> <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> </ItemGroup> - + <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> </ItemGroup> </Project> 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 /// <inheritdoc/> public QueryResult<ActivityLogEntry> GetPagedResult( - Func<IQueryable<ActivityLog>, IEnumerable<ActivityLog>> func, + Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>> 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<ActivityLogEntry>() + return new QueryResult<ActivityLogEntry> { 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 @@ <Compile Remove="Migrations\20200430214405_InitialSchema.Designer.cs" /> </ItemGroup> + <ItemGroup> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + </ItemGroup> + <ItemGroup> <ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> 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 /// <returns>The newly created context.</returns> public JellyfinDb CreateContext() { - return _serviceProvider.GetService<JellyfinDb>(); + return _serviceProvider.GetRequiredService<JellyfinDb>(); } } } 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 // <auto-generated /> 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<JellyfinDb>(); - 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 @@ // <auto-generated /> 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<JellyfinDb>( + options => options.UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"), + ServiceLifetime.Transient); + + serviceCollection.AddSingleton<JellyfinDbProvider>(); + + serviceCollection.AddSingleton<IActivityManager, ActivityManager>(); + 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 @@ <GenerateDocumentationFile>true</GenerateDocumentationFile> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> <Nullable>enable</Nullable> - - <!-- Used for generating migrations for EF Core --> - <GenerateRuntimeConfigurationFiles>True</GenerateRuntimeConfigurationFiles> </PropertyGroup> <ItemGroup> @@ -44,10 +41,6 @@ <ItemGroup> <PackageReference Include="CommandLineParser" Version="2.7.82" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> <PackageReference Include="prometheus-net" Version="3.5.0" /> @@ -67,6 +60,7 @@ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" /> <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" /> <ProjectReference Include="..\Jellyfin.Drawing.Skia\Jellyfin.Drawing.Skia.csproj" /> + <ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> </ItemGroup> </Project> 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<ActivityLog>, IQueryable<ActivityLog>>( + 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<ActivityLogEntry> GetPagedResult(int? startIndex, int? limit); QueryResult<ActivityLogEntry> GetPagedResult( - Func<IQueryable<ActivityLog>, IEnumerable<ActivityLog>> func, + Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>> 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; } /// <summary> 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<string>(); - } - } - - public class DeviceOptions - { - public string CustomName { get; set; } - } -} From 953777f1ba4858f5186086e97910fcb88bfe3d61 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Thu, 14 May 2020 18:12:51 -0400 Subject: [PATCH 367/411] 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 <just.one.man@yandex.ru> Date: Fri, 15 May 2020 01:55:00 +0300 Subject: [PATCH 368/411] 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 <fhriley@gmail.com> Date: Thu, 7 May 2020 20:36:50 -0700 Subject: [PATCH 369/411] 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<string, string> 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 } } + /// <summary> + /// Get the default CORS headers + /// </summary> + /// <param name="req"></param> + /// <returns></returns> + public IDictionary<string, string> 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<string, string>(); + 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<IRequest, HttpResponse, object>[] { - 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 /// </summary> public class ResponseFilter { + private readonly IHttpServer _server; private readonly ILogger _logger; /// <summary> /// Initializes a new instance of the <see cref="ResponseFilter"/> class. /// </summary> + /// <param name="server">The HTTP server.</param> /// <param name="logger">The logger.</param> - public ResponseFilter(ILogger logger) + public ResponseFilter(IHttpServer server, ILogger logger) { + _server = server; _logger = logger; } @@ -32,10 +37,16 @@ namespace Emby.Server.Implementations.HttpServer /// <param name="dto">The dto.</param> public void FilterResponse(IRequest req, HttpResponse res, object dto) { + foreach(KeyValuePair<string, string> 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 /// <param name="context"></param> /// <returns></returns> Task RequestHandler(HttpContext context); + + /// <summary> + /// Get the default CORS headers + /// </summary> + /// <param name="req"></param> + /// <returns></returns> + IDictionary<string, string> GetCorsHeaders(IRequest req); } } From c70c58923667a3c626b4112f783f755f91442d0b Mon Sep 17 00:00:00 2001 From: Frank Riley <fhriley@gmail.com> Date: Wed, 13 May 2020 15:57:40 -0700 Subject: [PATCH 370/411] Update Emby.Server.Implementations/HttpServer/HttpListenerHost.cs from review Co-authored-by: Cody Robibero <cody@robibe.ro> --- 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<string, string> 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 <fhriley@gmail.com> Date: Thu, 14 May 2020 06:28:33 -0700 Subject: [PATCH 371/411] 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 /// <param name="dto">The dto.</param> public void FilterResponse(IRequest req, HttpResponse res, object dto) { - foreach(KeyValuePair<string, string> 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 <fhriley@gmail.com> Date: Thu, 14 May 2020 16:03:45 -0700 Subject: [PATCH 372/411] 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 /// </summary> /// <param name="req"></param> /// <returns></returns> - public IDictionary<string, string> GetCorsHeaders(IRequest req) + public IDictionary<string, string> 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 /// <param name="dto">The dto.</param> 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 /// </summary> /// <param name="req"></param> /// <returns></returns> - IDictionary<string, string> GetCorsHeaders(IRequest req); + IDictionary<string, string> GetDefaultCorsHeaders(IRequest req); } } From 7c571345353417db6990700b350fd22a2778ee4f Mon Sep 17 00:00:00 2001 From: Vasily <just.one.man@yandex.ru> Date: Fri, 15 May 2020 02:30:28 +0300 Subject: [PATCH 373/411] 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) }; /// <summary> 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 +{ + /// <summary> + /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// </summary> + internal class RemoveBuggedExtras : IMigrationRoutine + { + private const string DbFilename = "library.db"; + private readonly ILogger _logger; + private readonly IServerApplicationPaths _paths; + + public RemoveBuggedExtras(ILogger<RemoveBuggedExtras> logger, IServerApplicationPaths paths) + { + _logger = logger; + _paths = paths; + } + + /// <inheritdoc/> + public Guid Id => Guid.Parse("{ACBE17B7-8435-4A83-8B64-6FCF162CB9BD}"); + + /// <inheritdoc/> + public string Name => "RemoveBuggedExtras"; + + /// <inheritdoc/> + 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 e02e041b231dbe2b158fa1c75098bdd08e0abad1 Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Thu, 14 May 2020 16:55:55 -0700 Subject: [PATCH 374/411] If second cleaning results in same name skip lookup --- .../Tmdb/Movies/TmdbSearch.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs index aa42fd81ab..bf63946084 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - // Does this mean we are reparsing already parsed ItemLookupInfo? + // TODO: Investigate: Does this mean we are reparsing already parsed ItemLookupInfo? var parsedName = _libraryManager.ParseName(name); var yearInName = parsedName.Year; name = parsedName.Name; @@ -105,30 +105,30 @@ namespace MediaBrowser.Providers.Tmdb.Movies // providers if (results.Count == 0) { - name = parsedName.Name; + var name2 = parsedName.Name; // Remove things enclosed in []{}() etc - name = _cleanEnclosed.Replace(name, string.Empty); + name2 = _cleanEnclosed.Replace(name2, string.Empty); // Replace sequences of non-word characters with space - name = _cleanNonWord.Replace(name, " "); + name2 = _cleanNonWord.Replace(name2, " "); // Clean based on common stop words / tokens - name = _cleanStopWords.Replace(name, string.Empty); + name2 = _cleanStopWords.Replace(name2, string.Empty); // Trim whitespace - name = name.Trim(); + name2 = name2.Trim(); // Search again if the new name is different - if (!string.Equals(name, parsedName.Name) && !string.IsNullOrWhiteSpace(name)) + if (!string.Equals(name2, name) && !string.IsNullOrWhiteSpace(name2)) { - _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); - results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); + _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year); + results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { //one more time, in english - results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); + results = await GetSearchResults(name2, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); } } } From dcaffd3812b3995e2158f4ea587599249016c03c Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Thu, 14 May 2020 20:11:34 -0400 Subject: [PATCH 375/411] 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<BaseApiService> logger, IServerConfigurationManager serverConfigurationManager, IHttpResultFactory httpResultFactory) { @@ -34,7 +34,7 @@ namespace MediaBrowser.Api /// Gets the logger. /// </summary> /// <value>The logger.</value> - protected ILogger Logger { get; } + protected ILogger<BaseApiService> Logger { get; } /// <summary> /// 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<MoviesService> _moviesServiceLogger; + /// <summary> /// Initializes a new instance of the <see cref="LibraryService" /> class. /// </summary> public LibraryService( ILogger<LibraryService> logger, + ILogger<MoviesService> 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 <see cref="MoviesService" /> class. /// </summary> public MoviesService( - ILogger logger, + ILogger<MoviesService> 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 <see cref="BaseStreamingService" /> class. /// </summary> protected BaseStreamingService( - ILogger logger, + ILogger<BaseStreamingService> 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<BaseHlsService> 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<DynamicHlsService> 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<MediaInfoService> 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<AudioService> 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<BaseProgressiveStreamingService> 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<UniversalAudioService> 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<MediaInfoService>(), 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<DynamicHlsService>(), ServerConfigurationManager, ResultFactory, UserManager, @@ -331,7 +334,7 @@ namespace MediaBrowser.Api.Playback else { var service = new AudioService( - Logger, + _loggerFactory.CreateLogger<AudioService>(), 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<MusicArtist> { public ArtistsService( - ILogger<GenresService> logger, + ILogger<ArtistsService> 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 /// <param name="userDataRepository">The user data repository.</param> /// <param name="dtoService">The dto service.</param> protected BaseItemsByNameService( - ILogger logger, + ILogger<BaseItemsByNameService<TItemType>> 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?= <oddstr13@openshell.no> Date: Fri, 15 May 2020 00:48:58 +0000 Subject: [PATCH 376/411] =?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 <siderite@gmail.com> Date: Fri, 15 May 2020 07:59:46 -0400 Subject: [PATCH 377/411] 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 <siderite@gmail.com> Date: Thu, 14 May 2020 12:42:22 -0400 Subject: [PATCH 378/411] 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 <siderite@gmail.com> Date: Thu, 14 May 2020 13:27:23 -0400 Subject: [PATCH 379/411] 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 <siderite@gmail.com> Date: Fri, 15 May 2020 08:30:58 -0400 Subject: [PATCH 380/411] 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 <siderite@gmail.com> Date: Fri, 15 May 2020 09:38:26 -0400 Subject: [PATCH 381/411] 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 <barronpm@gmail.com> Date: Fri, 15 May 2020 12:51:18 -0400 Subject: [PATCH 382/411] 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 /// <param name="name">The name.</param> /// <param name="type">The type.</param> /// <param name="userId">The user's id.</param> + /// <returns>The new <see cref="ActivityLog"/> instance.</returns> public static ActivityLog Create(string name, string type, Guid userId) { return new ActivityLog(name, type, userId); @@ -63,7 +64,7 @@ namespace Jellyfin.Data.Entities *************************************************************************/ /// <summary> - /// Gets the identity of this instance. + /// Gets or sets the identity of this instance. /// This is the key in the backing database. /// </summary> [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<ActivityLogEntry> { 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<ISavingChanges>()) { - 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<JellyfinDb>(); - 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 <barronpm@gmail.com> Date: Fri, 15 May 2020 14:08:46 -0400 Subject: [PATCH 383/411] 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 <JustAMan@users.noreply.github.com> Date: Fri, 15 May 2020 21:32:56 +0300 Subject: [PATCH 384/411] Apply suggestions from code review Co-authored-by: Mark Monteiro <marknr.monteiro@protonmail.com> --- 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 /// <summary> /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. /// </summary> - 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 <barronpm@gmail.com> Date: Fri, 15 May 2020 14:33:36 -0400 Subject: [PATCH 385/411] 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 <just.one.man@yandex.ru> Date: Fri, 15 May 2020 21:49:45 +0300 Subject: [PATCH 386/411] 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) }; /// <summary> 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<RemoveBuggedExtras> logger, IServerApplicationPaths paths) + public RemoveDuplicateExtras(ILogger<RemoveDuplicateExtras> 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}"); /// <inheritdoc/> - public string Name => "RemoveBuggedExtras"; + public string Name => "RemoveDuplicateExtras"; /// <inheritdoc/> 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 <marknr.monteiro@protonmail.com> Date: Fri, 15 May 2020 15:10:41 -0400 Subject: [PATCH 387/411] 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("No duplicate extras detected, skipping migration."); + return; + } + + // Back up the database before deleting any entries + for (int i = 1; ; i++) { - _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)) { - 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 <erik@rigtorp.se> Date: Thu, 30 Apr 2020 17:15:38 -0700 Subject: [PATCH 388/411] 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 f144acdc9614bed64d7f8842356293a94a3b754a Mon Sep 17 00:00:00 2001 From: Erik Rigtorp <erik@rigtorp.se> Date: Tue, 12 May 2020 14:33:06 -0700 Subject: [PATCH 389/411] Use glob patterns to ignore files --- .../Emby.Server.Implementations.csproj | 1 + .../IO/LibraryMonitor.cs | 40 +--------- .../Library/CoreResolutionIgnoreRule.cs | 47 +----------- .../Library/IgnorePatterns.cs | 73 +++++++++++++++++++ .../Library/IgnorePatternsTests.cs | 21 ++++++ 5 files changed, 100 insertions(+), 82 deletions(-) create mode 100644 Emby.Server.Implementations/Library/IgnorePatterns.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 44fc932e39..bab9e1c177 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -43,6 +43,7 @@ <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> <PackageReference Include="sharpcompress" Version="0.25.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> + <PackageReference Include="DotNet.Glob" Version="3.0.9" /> </ItemGroup> <ItemGroup> diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index 5a1eb43bcb..eb5e190aab 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -11,6 +11,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.IO; +using Emby.Server.Implementations.Library; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.IO @@ -37,38 +38,6 @@ namespace Emby.Server.Implementations.IO /// </summary> private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase); - /// <summary> - /// Any file name ending in any of these will be ignored by the watchers. - /// </summary> - private static readonly HashSet<string> _alwaysIgnoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase) - { - "small.jpg", - "albumart.jpg", - - // WMC temp recording directories that will constantly be written to - "TempRec", - "TempSBE" - }; - - private static readonly string[] _alwaysIgnoreSubstrings = new string[] - { - // Synology - "eaDir", - "#recycle", - ".wd_tv", - ".actors" - }; - - private static readonly HashSet<string> _alwaysIgnoreExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) - { - // thumbs.db - ".db", - - // bts sync files - ".bts", - ".sync" - }; - /// <summary> /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. /// </summary> @@ -395,12 +364,7 @@ namespace Emby.Server.Implementations.IO throw new ArgumentNullException(nameof(path)); } - var filename = Path.GetFileName(path); - - var monitorPath = !string.IsNullOrEmpty(filename) && - !_alwaysIgnoreFiles.Contains(filename) && - !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path)) && - _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1); + var monitorPath = !IgnorePatterns.ShouldIgnore(path); // Ignore certain files var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index bc1398332d..218e5a0c6d 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -1,7 +1,5 @@ using System; using System.IO; -using System.Linq; -using System.Text.RegularExpressions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; @@ -16,32 +14,6 @@ namespace Emby.Server.Implementations.Library { private readonly ILibraryManager _libraryManager; - /// <summary> - /// Any folder named in this list will be ignored - /// </summary> - private static readonly string[] _ignoreFolders = - { - "metadata", - "ps3_update", - "ps3_vprm", - "extrafanart", - "extrathumbs", - ".actors", - ".wd_tv", - - // Synology - "@eaDir", - "eaDir", - "#recycle", - - // Qnap - "@Recycle", - ".@__thumb", - "$RECYCLE.BIN", - "System Volume Information", - ".grab", - }; - /// <summary> /// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class. /// </summary> @@ -60,23 +32,15 @@ namespace Emby.Server.Implementations.Library return false; } - var filename = fileInfo.Name; - - // Ignore hidden files on UNIX - if (Environment.OSVersion.Platform != PlatformID.Win32NT - && filename[0] == '.') + if (IgnorePatterns.ShouldIgnore(fileInfo.FullName)) { return true; } + var filename = fileInfo.Name; + if (fileInfo.IsDirectory) { - // Ignore any folders in our list - if (_ignoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - if (parent != null) { // Ignore trailer folders but allow it at the collection level @@ -109,11 +73,6 @@ namespace Emby.Server.Implementations.Library return true; } } - - // Ignore samples - Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase); - - return m.Success; } return false; diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs new file mode 100644 index 0000000000..49a36495af --- /dev/null +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -0,0 +1,73 @@ +using System.Linq; +using DotNet.Globbing; + +namespace Emby.Server.Implementations.Library +{ + /// <summary> + /// Glob patterns for files to ignore + /// </summary> + public static class IgnorePatterns + { + /// <summary> + /// Files matching these glob patterns will be ignored + /// </summary> + public static readonly string[] Patterns = new string[] + { + "**/small.jpg", + "**/albumart.jpg", + "**/*sample*", + + // Directories + "**/metadata/**", + "**/ps3_update/**", + "**/ps3_vprm/**", + "**/extrafanart/**", + "**/extrathumbs/**", + "**/.actors/**", + "**/.wd_tv/**", + + // WMC temp recording directories that will constantly be written to + "**/TempRec/**", + "**/TempSBE/**", + + // Synology + "**/eaDir/**", + "**/@eaDir/**", + "**/#recycle/**", + + // Qnap + "**/@Recycle/**", + "**/.@__thumb/**", + "**/$RECYCLE.BIN/**", + "**/System Volume Information/**", + "**/.grab/**", + + // Unix hidden files and directories + "**/.*/**", + + // thumbs.db + "**/thumbs.db", + + // bts sync files + "**/*.bts", + "**/*.sync", + }; + + private static readonly GlobOptions _globOptions = new GlobOptions + { + Evaluation = { + CaseInsensitive = true + } + }; + + private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray(); + + /// <summary> + /// Returns true if the supplied path should be ignored + /// </summary> + public static bool ShouldIgnore(string path) + { + return _globs.Any(g => g.IsMatch(path)); + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs new file mode 100644 index 0000000000..26dee38c6d --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -0,0 +1,21 @@ +using Emby.Server.Implementations.Library; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Library +{ + public class IgnorePatternsTests + { + [Theory] + [InlineData("/media/small.jpg", true)] + [InlineData("/media/movies/#Recycle/test.txt", true)] + [InlineData("/media/movies/#recycle/", true)] + [InlineData("thumbs.db", true)] + [InlineData(@"C:\media\movies\movie.avi", false)] + [InlineData("/media/.hiddendir/file.mp4", true)] + [InlineData("/media/dir/.hiddenfile.mp4", true)] + public void PathIgnored(string path, bool expected) + { + Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); + } + } +} From 9314434bbf79250f1e545b459c545f57d5acc67c Mon Sep 17 00:00:00 2001 From: MrTimscampi <julien.machiels@protonmail.com> Date: Sat, 16 May 2020 17:35:34 +0200 Subject: [PATCH 390/411] 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; } /// <summary> - /// Gets or sets the color transfer. + /// Gets or sets the color primaries. /// </summary> - /// <value>The color transfer.</value> + /// <value>The color primaries.</value> [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 <castillofrancodamian@gmail.com> Date: Sat, 16 May 2020 18:15:27 +0000 Subject: [PATCH 391/411] 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 <gagnons.infynox@gmail.com> Date: Sat, 16 May 2020 16:53:30 +0000 Subject: [PATCH 392/411] 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 <nathant93@gmail.com> Date: Sun, 17 May 2020 10:48:30 -0400 Subject: [PATCH 393/411] 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 <marknr.monteiro@protonmail.com> Date: Sun, 17 May 2020 13:33:38 -0400 Subject: [PATCH 394/411] 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 @@ <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.4" /> <PackageReference Include="Mono.Nat" Version="2.0.1" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" /> 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 @@ <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> - <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.3" /> + <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.4" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" /> </ItemGroup> 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 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.4" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.4" /> </ItemGroup> </Project> 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 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.4"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.4"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> 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 @@ <ItemGroup> <PackageReference Include="CommandLineParser" Version="2.7.82" /> - <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" /> <PackageReference Include="prometheus-net" Version="3.5.0" /> <PackageReference Include="prometheus-net.AspNetCore" Version="3.5.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> 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 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.4" /> <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.8" /> </ItemGroup> 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 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.4" /> </ItemGroup> <ItemGroup> 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 @@ <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.4" /> <PackageReference Include="System.Globalization" Version="4.3.0" /> - <PackageReference Include="System.Text.Json" Version="4.7.1" /> + <PackageReference Include="System.Text.Json" Version="4.7.2" /> </ItemGroup> <ItemGroup> 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 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.4" /> + <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.4" /> <PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" /> <PackageReference Include="PlaylistsNET" Version="1.0.4" /> <PackageReference Include="TvDbSharper" Version="3.0.1" /> 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 @@ <PackageReference Include="AutoFixture" Version="4.11.0" /> <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" /> <PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" /> - <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> 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 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.3" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.4" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> From 634bc73c9a641646b633fbc560a288207f5eac4b Mon Sep 17 00:00:00 2001 From: Mark Monteiro <marknr.monteiro@protonmail.com> Date: Sun, 17 May 2020 18:07:37 -0400 Subject: [PATCH 395/411] DO not use developer exception page when exception stack trace should be ignored --- .../HttpServer/HttpListenerHost.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 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); - - // Do not handle 500 server exceptions manually when in development mode - // The framework-defined development exception page will be returned instead - if (statusCode == 500 && _hostEnvironment.IsDevelopment()) + bool ignoreStackTrace = + requestInnerEx is SocketException + || requestInnerEx is IOException + || requestInnerEx is OperationCanceledException + || requestInnerEx is SecurityException + || requestInnerEx is AuthenticationException + || requestInnerEx is FileNotFoundException; + + // Do not handle 500 server exceptions manually when in development mode. + // Instead, re-throw the exception so it can be handled by the DeveloperExceptionPageMiddleware. + // However, do not use the DeveloperExceptionPageMiddleware when the stack trace should be ignored, + // because it will log the stack trace when it handles the exception. + if (statusCode == 500 && !ignoreStackTrace && _hostEnvironment.IsDevelopment() ) { throw; } - await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog).ConfigureAwait(false); + await ErrorHandler(requestInnerEx, httpReq, statusCode, urlToLog, ignoreStackTrace).ConfigureAwait(false); } catch (Exception handlerException) { From 989ddbcafdfcbe32bdf16a30ebec9554d6ca548a Mon Sep 17 00:00:00 2001 From: erikasne6152 <erikas.ne6152@go.kauko.lt> Date: Mon, 18 May 2020 11:31:19 +0000 Subject: [PATCH 396/411] 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 <JustAMan@users.noreply.github.com> Date: Mon, 18 May 2020 17:01:29 +0300 Subject: [PATCH 397/411] Apply suggestions from code review Co-authored-by: dkanada <dkanada@users.noreply.github.com> --- .../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 <marknr.monteiro@protonmail.com> Date: Mon, 18 May 2020 10:47:01 -0400 Subject: [PATCH 398/411] Remove extra whitespace Co-authored-by: dkanada <dkanada@users.noreply.github.com> --- 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; } From 85f04af04c7d33477df5486ae80b6fa9a2a2bfd7 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 18 May 2020 14:30:23 -0500 Subject: [PATCH 399/411] Reuse existing CORS function --- 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 9554b9f462..9a4e858a51 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -497,9 +497,9 @@ namespace Emby.Server.Implementations.HttpServer var requestInnerEx = GetActualException(requestEx); var statusCode = GetStatusCode(requestInnerEx); - if (!httpRes.Headers.ContainsKey("Access-Control-Allow-Origin")) + foreach (var (key, value) in GetDefaultCorsHeaders(httpReq)) { - httpRes.Headers.Add("Access-Control-Allow-Origin", "*"); + httpRes.Headers.Add(key, value); } bool ignoreStackTrace = From 949e4d3e64a73102d0a87c8b34918397a2cec303 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 18 May 2020 16:54:36 -0500 Subject: [PATCH 400/411] Apply suggestions from code review --- Emby.Server.Implementations/HttpServer/HttpListenerHost.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 9a4e858a51..7de4f168c1 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -499,7 +499,10 @@ namespace Emby.Server.Implementations.HttpServer foreach (var (key, value) in GetDefaultCorsHeaders(httpReq)) { - httpRes.Headers.Add(key, value); + if (!httpRes.Headers.ContainsKey(key)) + { + httpRes.Headers.Add(key, value); + } } bool ignoreStackTrace = From 4395644efcf3ae25fd657a5cbdd7db0a0fffa9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= <lukas@kucharczyk.xyz> Date: Mon, 18 May 2020 19:27:56 +0000 Subject: [PATCH 401/411] Translated using Weblate (Czech) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/ --- Emby.Server.Implementations/Localization/Core/cs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 992bb9df37..464ca28ca0 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -23,7 +23,7 @@ "HeaderFavoriteEpisodes": "Oblíbené epizody", "HeaderFavoriteShows": "Oblíbené seriály", "HeaderFavoriteSongs": "Oblíbená hudba", - "HeaderLiveTV": "Živá TV", + "HeaderLiveTV": "Televize", "HeaderNextUp": "Nadcházející", "HeaderRecordingGroups": "Skupiny nahrávek", "HomeVideos": "Domáci videa", From 585e9e6220c50bac5c224ac1ee3a90ce625ef3c6 Mon Sep 17 00:00:00 2001 From: NaorManna <Naor.Ben-David@manna-irrigation.com> Date: Tue, 19 May 2020 06:38:03 +0000 Subject: [PATCH 402/411] 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 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 4e54b9f7ad..682f5325b5 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -107,5 +107,12 @@ "TaskCleanLogs": "נקה תיקיית יומן", "TaskRefreshLibraryDescription": "סורק את ספריית המדיה שלך אחר קבצים חדשים ומרענן מטא נתונים.", "TaskRefreshChapterImagesDescription": "יוצר תמונות ממוזערות לסרטונים שיש להם פרקים.", - "TasksChannelsCategory": "ערוצי אינטרנט" + "TasksChannelsCategory": "ערוצי אינטרנט", + "TaskDownloadMissingSubtitlesDescription": "חפש באינטרנט עבור הכתוביות החסרות בהתבסס על המטה-דיאטה.", + "TaskDownloadMissingSubtitles": "הורד כתוביות חסרות.", + "TaskRefreshChannelsDescription": "רענן פרטי ערוץ אינטרנטי.", + "TaskRefreshChannels": "רענן ערוץ", + "TaskCleanTranscodeDescription": "מחק קבצי transcode שנוצרו מלפני יותר מיום.", + "TaskCleanTranscode": "נקה תקיית Transcode", + "TaskUpdatePluginsDescription": "הורד והתקן עדכונים עבור תוספים שמוגדרים לעדכון אוטומטי." } From 0efb81b21ee05c9cbab4101de826250d0d698cf1 Mon Sep 17 00:00:00 2001 From: artiume <siderite@gmail.com> Date: Tue, 19 May 2020 21:45:48 -0400 Subject: [PATCH 403/411] Add lost+found to ignore list https://forum.jellyfin.org/t/library-not-loading/2086 --- Emby.Server.Implementations/Library/IgnorePatterns.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs index 49a36495af..d12b5855b2 100644 --- a/Emby.Server.Implementations/Library/IgnorePatterns.cs +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -25,6 +25,7 @@ namespace Emby.Server.Implementations.Library "**/extrathumbs/**", "**/.actors/**", "**/.wd_tv/**", + "**/lost+found/**", // WMC temp recording directories that will constantly be written to "**/TempRec/**", From ae4c407b6d4ff314362f693f682521168aa922df Mon Sep 17 00:00:00 2001 From: artiume <siderite@gmail.com> Date: Wed, 20 May 2020 16:46:33 -0400 Subject: [PATCH 404/411] Add .edl 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 b491a015c0..b6d7b4245c 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -100,6 +100,7 @@ namespace MediaBrowser.Model.Net { ".ssa", "text/x-ssa" }, { ".css", "text/css" }, { ".csv", "text/csv" }, + { ".edl", "text/plain" }, { ".rtf", "text/rtf" }, { ".txt", "text/plain" }, { ".vtt", "text/vtt" }, From 7e2bd3018a0926658d34c862ca5084753cdc062a Mon Sep 17 00:00:00 2001 From: abdulaziz <aaljaloud@tutanota.com> Date: Thu, 21 May 2020 02:36:33 +0000 Subject: [PATCH 405/411] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index f313039a69..d68928fce4 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -9,7 +9,7 @@ "Channels": "القنوات", "ChapterNameValue": "الفصل {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": "قُطِع الاتصال بـ{0}", + "DeviceOfflineWithName": "قُطِع الاتصال ب{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", "Favorites": "المفضلة", From 6bf444feaeb07ad24b2aee5afa2f1281970b77e0 Mon Sep 17 00:00:00 2001 From: Vitorvlv <vitorlourovaladares@gmail.com> Date: Thu, 21 May 2020 02:23:40 +0000 Subject: [PATCH 406/411] Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 3a69b6d7a5..275195640b 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -19,10 +19,10 @@ "HeaderCameraUploads": "Envios da Câmera", "HeaderContinueWatching": "Continuar Assistindo", "HeaderFavoriteAlbums": "Álbuns Favoritos", - "HeaderFavoriteArtists": "Artistas Favoritos", - "HeaderFavoriteEpisodes": "Episódios Favoritos", - "HeaderFavoriteShows": "Séries Favoritas", - "HeaderFavoriteSongs": "Músicas Favoritas", + "HeaderFavoriteArtists": "Artistas favoritos", + "HeaderFavoriteEpisodes": "Episódios favoritos", + "HeaderFavoriteShows": "Séries favoritas", + "HeaderFavoriteSongs": "Músicas favoritas", "HeaderLiveTV": "TV ao Vivo", "HeaderNextUp": "A Seguir", "HeaderRecordingGroups": "Grupos de Gravação", From e0d8a474ec41c62920f476b17440bcb5cebbd5f9 Mon Sep 17 00:00:00 2001 From: fonfire <fonfire4u@yahoo.com> Date: Thu, 21 May 2020 09:52:32 +0000 Subject: [PATCH 407/411] Added translation using Weblate (Thai) --- Emby.Server.Implementations/Localization/Core/th.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/th.json diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -0,0 +1 @@ +{} From 74c1e002d2f1d84051ccf3c3bc77b77afcd35954 Mon Sep 17 00:00:00 2001 From: fatbill27 <xxx@billtang.ddns.net> Date: Thu, 21 May 2020 07:29:33 +0000 Subject: [PATCH 408/411] Translated using Weblate (Chinese (Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- .../Localization/Core/zh-HK.json | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index a67a67582f..0804fc9279 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -11,15 +11,15 @@ "Collections": "合輯", "DeviceOfflineWithName": "{0} 已經斷開連結", "DeviceOnlineWithName": "{0} 已經連接", - "FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試", + "FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗", "Favorites": "我的最愛", "Folders": "檔案夾", "Genres": "風格", - "HeaderAlbumArtists": "專輯藝術家", + "HeaderAlbumArtists": "專輯藝人", "HeaderCameraUploads": "相機上載", "HeaderContinueWatching": "繼續觀看", "HeaderFavoriteAlbums": "最愛專輯", - "HeaderFavoriteArtists": "最愛藝術家", + "HeaderFavoriteArtists": "最愛的藝人", "HeaderFavoriteEpisodes": "最愛的劇集", "HeaderFavoriteShows": "最愛的節目", "HeaderFavoriteSongs": "最愛的歌曲", @@ -33,14 +33,14 @@ "LabelIpAddressValue": "IP 地址: {0}", "LabelRunningTimeValue": "運行時間: {0}", "Latest": "最新", - "MessageApplicationUpdated": "Jellyfin Server 已更新", + "MessageApplicationUpdated": "Jellyfin 伺服器已更新", "MessageApplicationUpdatedTo": "Jellyfin 伺服器已更新至 {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已更新", + "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已更新", "MessageServerConfigurationUpdated": "伺服器設定已經更新", - "MixedContent": "Mixed content", + "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", - "MusicVideos": "音樂MV", + "MusicVideos": "音樂視頻", "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季數", @@ -49,7 +49,7 @@ "NotificationOptionApplicationUpdateInstalled": "應用程式已更新", "NotificationOptionAudioPlayback": "開始播放音頻", "NotificationOptionAudioPlaybackStopped": "已停止播放音頻", - "NotificationOptionCameraImageUploaded": "相機相片已上傳", + "NotificationOptionCameraImageUploaded": "相片已上傳", "NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionNewLibraryContent": "已添加新内容", "NotificationOptionPluginError": "擴充元件錯誤", @@ -63,11 +63,11 @@ "NotificationOptionVideoPlaybackStopped": "已停止播放視頻", "Photos": "相片", "Playlists": "播放清單", - "Plugin": "Plugin", + "Plugin": "插件", "PluginInstalledWithName": "已安裝 {0}", "PluginUninstalledWithName": "已移除 {0}", "PluginUpdatedWithName": "已更新 {0}", - "ProviderValue": "Provider: {0}", + "ProviderValue": "提供者: {0}", "ScheduledTaskFailedWithName": "{0} 任務失敗", "ScheduledTaskStartedWithName": "{0} 任務開始", "ServerNameNeedsToBeRestarted": "{0} 需要重啓", @@ -77,17 +77,17 @@ "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕", "Sync": "同步", - "System": "System", + "System": "系統", "TvShows": "電視節目", - "User": "User", - "UserCreatedWithName": "用家 {0} 已創建", - "UserDeletedWithName": "用家 {0} 已移除", + "User": "使用者", + "UserCreatedWithName": "使用者 {0} 已創建", + "UserDeletedWithName": "使用者 {0} 已移除", "UserDownloadingItemWithValues": "{0} 正在下載 {1}", - "UserLockedOutWithName": "用家 {0} 已被鎖定", + "UserLockedOutWithName": "使用者 {0} 已被鎖定", "UserOfflineFromDevice": "{0} 已從 {1} 斷開", "UserOnlineFromDevice": "{0} 已連綫,來自 {1}", - "UserPasswordChangedWithName": "用家 {0} 的密碼已變更", - "UserPolicyUpdatedWithName": "用戶協議已被更新為 {0}", + "UserPasswordChangedWithName": "使用者 {0} 的密碼已變更", + "UserPolicyUpdatedWithName": "使用者協議已更新為 {0}", "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}", "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}", "ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫", @@ -95,5 +95,23 @@ "VersionNumber": "版本{0}", "TaskDownloadMissingSubtitles": "下載遺失的字幕", "TaskUpdatePlugins": "更新插件", - "TasksApplicationCategory": "應用程式" + "TasksApplicationCategory": "應用程式", + "TaskRefreshLibraryDescription": "掃描媒體庫以查找新文件並刷新metadata。", + "TasksMaintenanceCategory": "維護", + "TaskDownloadMissingSubtitlesDescription": "根據metadata配置在互聯網上搜索缺少的字幕。", + "TaskRefreshChannelsDescription": "刷新互聯網頻道信息。", + "TaskRefreshChannels": "刷新頻道", + "TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。", + "TaskCleanTranscode": "清理轉碼目錄", + "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。", + "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的metadata。", + "TaskCleanLogsDescription": "刪除超過{0}天的日誌文件。", + "TaskCleanLogs": "清理日誌目錄", + "TaskRefreshLibrary": "掃描媒體庫", + "TaskRefreshChapterImagesDescription": "為帶有章節的視頻創建縮略圖。", + "TaskRefreshChapterImages": "提取章節圖像", + "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。", + "TaskCleanCache": "清理緩存目錄", + "TasksChannelsCategory": "互聯網頻道", + "TasksLibraryCategory": "庫" } From 367da81ae9a5825aefaed0901db47cd844c969e1 Mon Sep 17 00:00:00 2001 From: fonfire <fonfire4u@yahoo.com> Date: Thu, 21 May 2020 09:53:01 +0000 Subject: [PATCH 409/411] Translated using Weblate (Thai) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/th/ --- .../Localization/Core/th.json | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json index 0967ef424b..32538ac035 100644 --- a/Emby.Server.Implementations/Localization/Core/th.json +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -1 +1,71 @@ -{} +{ + "ProviderValue": "ผู้ให้บริการ: {0}", + "PluginUpdatedWithName": "{0} ได้รับการ update แล้ว", + "PluginUninstalledWithName": "ถอนการติดตั้ง {0}", + "PluginInstalledWithName": "{0} ได้รับการติดตั้ง", + "Plugin": "Plugin", + "Playlists": "รายการ", + "Photos": "รูปภาพ", + "NotificationOptionVideoPlaybackStopped": "หยุดการเล่น Video", + "NotificationOptionVideoPlayback": "เริ่มแสดง Video", + "NotificationOptionUserLockedOut": "ผู้ใช้ Locked Out", + "NotificationOptionTaskFailed": "ตารางการทำงานล้มเหลว", + "NotificationOptionServerRestartRequired": "ควร Restart Server", + "NotificationOptionPluginUpdateInstalled": "Update Plugin แล้ว", + "NotificationOptionPluginUninstalled": "ถอด Plugin", + "NotificationOptionPluginInstalled": "ติดตั้ง Plugin แล้ว", + "NotificationOptionPluginError": "Plugin ล้มเหลว", + "NotificationOptionNewLibraryContent": "เพิ่มข้อมูลใหม่แล้ว", + "NotificationOptionInstallationFailed": "ติดตั้งล้มเหลว", + "NotificationOptionCameraImageUploaded": "รูปภาพถูก upload", + "NotificationOptionAudioPlaybackStopped": "หยุดการเล่นเสียง", + "NotificationOptionAudioPlayback": "เริ่มเล่นเสียง", + "NotificationOptionApplicationUpdateInstalled": "Update ระบบแล้ว", + "NotificationOptionApplicationUpdateAvailable": "ระบบ update สามารถใช้ได้แล้ว", + "NewVersionIsAvailable": "ตรวจพบ Jellyfin เวอร์ชั่นใหม่", + "NameSeasonUnknown": "ไม่ทราบปี", + "NameSeasonNumber": "ปี {0}", + "NameInstallFailed": "{0} ติดตั้งไม่สำเร็จ", + "MusicVideos": "MV", + "Music": "เพลง", + "Movies": "ภาพยนต์", + "MixedContent": "รายการแบบผสม", + "MessageServerConfigurationUpdated": "การตั้งค่า update แล้ว", + "MessageNamedServerConfigurationUpdatedWithValue": "รายการตั้งค่า {0} ได้รับการ update แล้ว", + "MessageApplicationUpdatedTo": "Jellyfin Server จะ update ไปที่ {0}", + "MessageApplicationUpdated": "Jellyfin Server update แล้ว", + "Latest": "ล่าสุด", + "LabelRunningTimeValue": "เวลาที่เล่น : {0}", + "LabelIpAddressValue": "IP address: {0}", + "ItemRemovedWithName": "{0} ถูกลบจากรายการ", + "ItemAddedWithName": "{0} ถูกเพิ่มในรายการ", + "Inherit": "การสืบทอด", + "HomeVideos": "วีดีโอส่วนตัว", + "HeaderRecordingGroups": "ค่ายบันทึก", + "HeaderNextUp": "ถัดไป", + "HeaderLiveTV": "รายการสด", + "HeaderFavoriteSongs": "เพลงโปรด", + "HeaderFavoriteShows": "รายการโชว์โปรด", + "HeaderFavoriteEpisodes": "ฉากโปรด", + "HeaderFavoriteArtists": "นักแสดงโปรด", + "HeaderFavoriteAlbums": "อัมบั้มโปรด", + "HeaderContinueWatching": "ชมต่อจากเดิม", + "HeaderCameraUploads": "Upload รูปภาพ", + "HeaderAlbumArtists": "อัลบั้มนักแสดง", + "Genres": "ประเภท", + "Folders": "โฟลเดอร์", + "Favorites": "รายการโปรด", + "FailedLoginAttemptWithUserName": "การเชื่อมต่อล้มเหลวจาก {0}", + "DeviceOnlineWithName": "{0} เชื่อมต่อสำเร็จ", + "DeviceOfflineWithName": "{0} ตัดการเชื่อมต่อ", + "Collections": "ชุด", + "ChapterNameValue": "บทที่ {0}", + "Channels": "ชาแนล", + "CameraImageUploadedFrom": "รูปภาพถูก upload จาก {0}", + "Books": "หนังสือ", + "AuthenticationSucceededWithUserName": "{0} ยืนยันตัวสำเร็จ", + "Artists": "นักแสดง", + "Application": "แอปพลิเคชั่น", + "AppDeviceValues": "App: {0}, อุปกรณ์: {1}", + "Albums": "อัลบั้ม" +} From 3c86489d2892fab7f1f94ee936ef0410b6721551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93skar=20Freyr?= <skari123@gmail.com> Date: Thu, 21 May 2020 16:14:14 +0000 Subject: [PATCH 410/411] Translated using Weblate (Icelandic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/is/ --- .../Localization/Core/is.json | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json index ef2a57e8e8..0f0f9130b0 100644 --- a/Emby.Server.Implementations/Localization/Core/is.json +++ b/Emby.Server.Implementations/Localization/Core/is.json @@ -80,16 +80,32 @@ "ValueHasBeenAddedToLibrary": "{0} hefur verið bætt við í gagnasafnið þitt", "UserStoppedPlayingItemWithValues": "{0} hefur lokið spilunar af {1} á {2}", "UserStartedPlayingItemWithValues": "{0} er að spila {1} á {2}", - "UserPolicyUpdatedWithName": "Notandaregla hefur verið uppfærð fyrir notanda {0}", + "UserPolicyUpdatedWithName": "Notandaregla hefur verið uppfærð fyrir {0}", "UserPasswordChangedWithName": "Lykilorði fyrir notandann {0} hefur verið breytt", "UserOnlineFromDevice": "{0} hefur verið virkur síðan {1}", "UserOfflineFromDevice": "{0} hefur aftengst frá {1}", - "UserLockedOutWithName": "Notanda {0} hefur verið hindraður aðgangur", + "UserLockedOutWithName": "Notanda {0} hefur verið heflaður aðgangur", "UserDownloadingItemWithValues": "{0} Hleður niður {1}", "SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}", "ProviderValue": "Veitandi: {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón", "ValueSpecialEpisodeName": "Sérstakt - {0}", - "Shows": "Þættir", - "Playlists": "Spilunarlisti" + "Shows": "Sýningar", + "Playlists": "Spilunarlisti", + "TaskRefreshChannelsDescription": "Endurhlaða upplýsingum netrása.", + "TaskRefreshChannels": "Endurhlaða Rásir", + "TaskCleanTranscodeDescription": "Eyða umkóðuðum skrám sem eru meira en einum degi eldri.", + "TaskCleanTranscode": "Hreinsa Umkóðunarmöppu", + "TaskUpdatePluginsDescription": "Sækja og setja upp uppfærslur fyrir viðbætur sem eru stilltar til að uppfæra sjálfkrafa.", + "TaskUpdatePlugins": "Uppfæra viðbætur", + "TaskRefreshPeopleDescription": "Uppfærir lýsigögn fyrir leikara og leikstjóra í miðlasafninu þínu.", + "TaskRefreshLibraryDescription": "Skannar miðlasafnið þitt fyrir nýjum skrám og uppfærir lýsigögn.", + "TaskRefreshLibrary": "Skanna miðlasafn", + "TaskRefreshChapterImagesDescription": "Býr til smámyndir fyrir myndbönd sem hafa kaflaskil.", + "TaskCleanCacheDescription": "Eyðir skrám í skyndiminni sem ekki er lengur þörf fyrir í kerfinu.", + "TaskCleanCache": "Hreinsa skráasafn skyndiminnis", + "TasksChannelsCategory": "Netrásir", + "TasksApplicationCategory": "Forrit", + "TasksLibraryCategory": "Miðlasafn", + "TasksMaintenanceCategory": "Viðhald" } From e98600a76ff49cb70da229e757de8931542497a0 Mon Sep 17 00:00:00 2001 From: WontTell <executor260@gmail.com> Date: Fri, 22 May 2020 00:42:43 +0000 Subject: [PATCH 411/411] Translated using Weblate (Spanish (Mexico)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/ --- .../Localization/Core/es-MX.json | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index d93920f433..20b37ec9f2 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -16,16 +16,16 @@ "Folders": "Carpetas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas del álbum", - "HeaderCameraUploads": "Subidos desde Camara", - "HeaderContinueWatching": "Continuar Viendo", + "HeaderCameraUploads": "Subidas desde la cámara", + "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteEpisodes": "Episodios favoritos", "HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteSongs": "Canciones favoritas", - "HeaderLiveTV": "TV en Vivo", - "HeaderNextUp": "A Continuación", - "HeaderRecordingGroups": "Grupos de Grabaciones", + "HeaderLiveTV": "TV en vivo", + "HeaderNextUp": "A continuación", + "HeaderRecordingGroups": "Grupos de grabación", "HomeVideos": "Videos caseros", "Inherit": "Heredar", "ItemAddedWithName": "{0} fue agregado a la biblioteca", @@ -41,12 +41,12 @@ "Movies": "Películas", "Music": "Música", "MusicVideos": "Videos musicales", - "NameInstallFailed": "{0} instalación fallida", + "NameInstallFailed": "Falló la instalación de {0}", "NameSeasonNumber": "Temporada {0}", - "NameSeasonUnknown": "Temporada Desconocida", + "NameSeasonUnknown": "Temporada desconocida", "NewVersionIsAvailable": "Una nueva versión del Servidor Jellyfin está disponible para descargar.", - "NotificationOptionApplicationUpdateAvailable": "Actualización de aplicación disponible", - "NotificationOptionApplicationUpdateInstalled": "Actualización de aplicación instalada", + "NotificationOptionApplicationUpdateAvailable": "Actualización de la aplicación disponible", + "NotificationOptionApplicationUpdateInstalled": "Actualización de la aplicación instalada", "NotificationOptionAudioPlayback": "Reproducción de audio iniciada", "NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida", "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida", @@ -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": "Reproducción de video iniciada", @@ -69,48 +69,48 @@ "PluginUpdatedWithName": "{0} fue actualizado", "ProviderValue": "Proveedor: {0}", "ScheduledTaskFailedWithName": "{0} falló", - "ScheduledTaskStartedWithName": "{0} Iniciado", + "ScheduledTaskStartedWithName": "{0} iniciado", "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado", "Shows": "Programas", "Songs": "Canciones", - "StartupEmbyServerIsLoading": "El servidor Jellyfin esta cargando. Por favor intente de nuevo dentro de poco.", + "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.", "SubtitleDownloadFailureForItem": "Falló la descarga de subtítulos para {0}", - "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}", + "SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}", "Sync": "Sincronizar", "System": "Sistema", "TvShows": "Programas de TV", "User": "Usuario", - "UserCreatedWithName": "Se ha creado el usuario {0}", - "UserDeletedWithName": "Se ha eliminado el usuario {0}", - "UserDownloadingItemWithValues": "{0} esta descargando {1}", + "UserCreatedWithName": "El usuario {0} ha sido creado", + "UserDeletedWithName": "El usuario {0} ha sido eliminado", + "UserDownloadingItemWithValues": "{0} está descargando {1}", "UserLockedOutWithName": "El usuario {0} ha sido bloqueado", "UserOfflineFromDevice": "{0} se ha desconectado desde {1}", "UserOnlineFromDevice": "{0} está en línea desde {1}", "UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}", - "UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada por {0}", - "UserStartedPlayingItemWithValues": "{0} está reproduciéndose {1} en {2}", - "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducirse {1} en {2}", - "ValueHasBeenAddedToLibrary": "{0} se han añadido a su biblioteca de medios", + "UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}", + "UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}", + "UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}", + "ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios", "ValueSpecialEpisodeName": "Especial - {0}", "VersionNumber": "Versión {0}", - "TaskDownloadMissingSubtitlesDescription": "Buscar subtítulos de internet basado en configuración de metadatos.", - "TaskDownloadMissingSubtitles": "Descargar subtítulos perdidos", - "TaskRefreshChannelsDescription": "Refrescar información de canales de internet.", + "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.", + "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", + "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.", "TaskRefreshChannels": "Actualizar canales", - "TaskCleanTranscodeDescription": "Eliminar archivos transcodificados que tengan mas de un día.", + "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.", "TaskCleanTranscode": "Limpiar directorio de transcodificado", - "TaskUpdatePluginsDescription": "Descargar y actualizar complementos que están configurados para actualizarse automáticamente.", + "TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.", "TaskUpdatePlugins": "Actualizar complementos", - "TaskRefreshPeopleDescription": "Actualizar datos de actores y directores en su librería multimedia.", - "TaskRefreshPeople": "Refrescar persona", - "TaskCleanLogsDescription": "Eliminar archivos de registro con mas de {0} días.", - "TaskCleanLogs": "Directorio de logo limpio", - "TaskRefreshLibraryDescription": "Escanear su librería multimedia para nuevos archivos y refrescar metadatos.", - "TaskRefreshLibrary": "Escanear librería multimerdia", - "TaskRefreshChapterImagesDescription": "Crear miniaturas para videos con capítulos.", - "TaskRefreshChapterImages": "Extraer imágenes de capítulos", - "TaskCleanCacheDescription": "Eliminar archivos cache que ya no se necesiten por el sistema.", - "TaskCleanCache": "Limpiar directorio cache", + "TaskRefreshPeopleDescription": "Actualiza metadatos de actores y directores en tu biblioteca de medios.", + "TaskRefreshPeople": "Actualizar personas", + "TaskCleanLogsDescription": "Elimina archivos de registro con más de {0} días de antigüedad.", + "TaskCleanLogs": "Limpiar directorio de registros", + "TaskRefreshLibraryDescription": "Escanea tu biblioteca de medios por archivos nuevos y actualiza los metadatos.", + "TaskRefreshLibrary": "Escanear biblioteca de medios", + "TaskRefreshChapterImagesDescription": "Crea miniaturas para videos que tienen capítulos.", + "TaskRefreshChapterImages": "Extraer imágenes de los capítulos", + "TaskCleanCacheDescription": "Elimina archivos caché que ya no son necesarios para el sistema.", + "TaskCleanCache": "Limpiar directorio caché", "TasksChannelsCategory": "Canales de Internet", "TasksApplicationCategory": "Aplicación", "TasksLibraryCategory": "Biblioteca",