Merge branch 'master' into authenticationdb-efcore
# Conflicts: # Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs # Emby.Server.Implementations/Session/SessionManager.cs # Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
This commit is contained in:
commit
ae878fa051
2
.github/workflows/automation.yml
vendored
2
.github/workflows/automation.yml
vendored
|
@ -11,6 +11,7 @@ jobs:
|
|||
label:
|
||||
name: Labeling
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'jellyfin/jellyfin' }}
|
||||
steps:
|
||||
- name: Apply label
|
||||
uses: eps1lon/actions-label-merge-conflict@v2.0.1
|
||||
|
@ -22,6 +23,7 @@ jobs:
|
|||
project:
|
||||
name: Project board
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'jellyfin/jellyfin' }}
|
||||
steps:
|
||||
- name: Remove from 'Current Release' project
|
||||
uses: alex-page/github-project-automation-plus@v0.7.1
|
||||
|
|
|
@ -14,9 +14,9 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading.Tasks;
|
||||
using Emby.Dlna.Profiles;
|
||||
using Emby.Dlna.Server;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
|
|
|
@ -6,9 +6,9 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Diacritics.Extensions;
|
||||
using Emby.Dlna.Didl;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Dlna.Service
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Emby.Naming.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
|
||||
namespace Emby.Naming.Audio
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Emby.Naming.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
|
||||
namespace Emby.Naming.Video
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
|
|
@ -11,10 +11,12 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Diacritics.Extensions;
|
||||
using Emby.Server.Implementations.Playlists;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
|
|
@ -6,8 +6,8 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
|
@ -21,6 +21,7 @@ using Emby.Server.Implementations.Playlists;
|
|||
using Emby.Server.Implementations.ScheduledTasks;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Controller;
|
||||
|
|
|
@ -12,7 +12,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
|
|
|
@ -15,7 +15,7 @@ using Jellyfin.Data.Entities;
|
|||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Emby.Naming.Video;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Search;
|
||||
|
|
|
@ -11,9 +11,9 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
|
|
@ -15,7 +15,7 @@ using System.Text.Json;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
|
|
|
@ -12,8 +12,9 @@ using System.Net.Http;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Net.Http;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
|
|
|
@ -8,8 +8,8 @@ using System.IO;
|
|||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
|
|
|
@ -10,8 +10,8 @@ using System.Text.Json;
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.QuickConnect;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.QuickConnect;
|
||||
|
@ -22,9 +19,18 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
/// </summary>
|
||||
public class QuickConnectManager : IQuickConnect, IDisposable
|
||||
{
|
||||
private readonly RNGCryptoServiceProvider _rng = new ();
|
||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
|
||||
private readonly ConcurrentDictionary<string, (string Token, Guid UserId)> _quickConnectTokens = new ();
|
||||
/// <summary>
|
||||
/// The length of user facing codes.
|
||||
/// </summary>
|
||||
private const int CodeLength = 6;
|
||||
|
||||
/// <summary>
|
||||
/// The time (in minutes) that the quick connect token is valid.
|
||||
/// </summary>
|
||||
private const int Timeout = 10;
|
||||
|
||||
private readonly RNGCryptoServiceProvider _rng = new();
|
||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new();
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger<QuickConnectManager> _logger;
|
||||
|
@ -34,80 +40,42 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
/// Initializes a new instance of the <see cref="QuickConnectManager"/> class.
|
||||
/// Should only be called at server startup when a singleton is created.
|
||||
/// </summary>
|
||||
/// <param name="config">The server configuration manager.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="sessionManager">The session manager.</param>
|
||||
public QuickConnectManager(IServerConfigurationManager config, ILogger<QuickConnectManager> logger, ISessionManager sessionManager)
|
||||
/// <param name="config">Configuration.</param>
|
||||
/// <param name="logger">Logger.</param>
|
||||
/// <param name="sessionManager">Session Manager.</param>
|
||||
public QuickConnectManager(
|
||||
IServerConfigurationManager config,
|
||||
ILogger<QuickConnectManager> logger,
|
||||
ISessionManager sessionManager)
|
||||
{
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
_sessionManager = sessionManager;
|
||||
|
||||
ReloadConfiguration();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int CodeLength { get; set; } = 6;
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => _config.Configuration.QuickConnectAvailable;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string TokenName { get; set; } = "QuickConnect";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public QuickConnectState State { get; private set; } = QuickConnectState.Unavailable;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Timeout { get; set; } = 5;
|
||||
|
||||
private DateTime DateActivated { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AssertActive()
|
||||
/// <summary>
|
||||
/// Assert that quick connect is currently active and throws an exception if it is not.
|
||||
/// </summary>
|
||||
private void AssertActive()
|
||||
{
|
||||
if (State != QuickConnectState.Active)
|
||||
if (!IsEnabled)
|
||||
{
|
||||
throw new ArgumentException("Quick connect is not active on this server");
|
||||
throw new AuthenticationException("Quick connect is not active on this server");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Activate()
|
||||
{
|
||||
DateActivated = DateTime.UtcNow;
|
||||
SetState(QuickConnectState.Active);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetState(QuickConnectState newState)
|
||||
{
|
||||
_logger.LogDebug("Changed quick connect state from {State} to {newState}", State, newState);
|
||||
|
||||
ExpireRequests(true);
|
||||
|
||||
State = newState;
|
||||
_config.Configuration.QuickConnectAvailable = newState == QuickConnectState.Available || newState == QuickConnectState.Active;
|
||||
_config.SaveConfiguration();
|
||||
|
||||
_logger.LogDebug("Configuration saved");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public QuickConnectResult TryConnect()
|
||||
{
|
||||
AssertActive();
|
||||
ExpireRequests();
|
||||
|
||||
if (State != QuickConnectState.Active)
|
||||
{
|
||||
_logger.LogDebug("Refusing quick connect initiation request, current state is {State}", State);
|
||||
throw new AuthenticationException("Quick connect is not active on this server");
|
||||
}
|
||||
|
||||
var secret = GenerateSecureRandom();
|
||||
var code = GenerateCode();
|
||||
var result = new QuickConnectResult()
|
||||
{
|
||||
Secret = GenerateSecureRandom(),
|
||||
DateAdded = DateTime.UtcNow,
|
||||
Code = code
|
||||
};
|
||||
var result = new QuickConnectResult(secret, code, DateTime.UtcNow);
|
||||
|
||||
_currentRequests[code] = result;
|
||||
return result;
|
||||
|
@ -116,12 +84,12 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
/// <inheritdoc/>
|
||||
public QuickConnectResult CheckRequestStatus(string secret)
|
||||
{
|
||||
ExpireRequests();
|
||||
AssertActive();
|
||||
ExpireRequests();
|
||||
|
||||
string code = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Code).DefaultIfEmpty(string.Empty).First();
|
||||
|
||||
if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
|
||||
if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result))
|
||||
{
|
||||
throw new ResourceNotFoundException("Unable to find request with provided secret");
|
||||
}
|
||||
|
@ -129,22 +97,11 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void AuthenticateRequest(AuthenticationRequest request, string token)
|
||||
{
|
||||
if (!_quickConnectTokens.TryGetValue(token, out var entry))
|
||||
{
|
||||
throw new SecurityException("Unknown quick connect token");
|
||||
}
|
||||
|
||||
request.UserId = entry.UserId;
|
||||
_quickConnectTokens.Remove(token, out _);
|
||||
|
||||
_sessionManager.AuthenticateQuickConnect(request, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string GenerateCode()
|
||||
/// <summary>
|
||||
/// Generates a short code to display to the user to uniquely identify this request.
|
||||
/// </summary>
|
||||
/// <returns>A short, unique alphanumeric string.</returns>
|
||||
private string GenerateCode()
|
||||
{
|
||||
Span<byte> raw = stackalloc byte[4];
|
||||
|
||||
|
@ -163,12 +120,12 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool AuthorizeRequest(Guid userId, string code)
|
||||
public async Task<bool> AuthorizeRequest(Guid userId, string code)
|
||||
{
|
||||
ExpireRequests();
|
||||
AssertActive();
|
||||
ExpireRequests();
|
||||
|
||||
if (!_currentRequests.TryGetValue(code, out QuickConnectResult result))
|
||||
if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result))
|
||||
{
|
||||
throw new ResourceNotFoundException("Unable to find request");
|
||||
}
|
||||
|
@ -178,37 +135,19 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
throw new InvalidOperationException("Request is already authorized");
|
||||
}
|
||||
|
||||
result.Authentication = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
||||
var token = Guid.NewGuid();
|
||||
result.Authentication = token;
|
||||
|
||||
// Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
|
||||
var added = result.DateAdded ?? DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Timeout));
|
||||
result.DateAdded = added.Subtract(TimeSpan.FromMinutes(Timeout - 1));
|
||||
result.DateAdded = DateTime.Now.Add(TimeSpan.FromMinutes(1));
|
||||
|
||||
_quickConnectTokens[result.Authentication] = (TokenName, userId);
|
||||
await _sessionManager.AuthenticateQuickConnect(userId).ConfigureAwait(false);
|
||||
|
||||
_logger.LogDebug("Authorizing device with code {Code} to login as user {userId}", code, userId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int DeleteAllDevices(Guid user)
|
||||
{
|
||||
var tokens = _quickConnectTokens
|
||||
.Where(entry => entry.Value.Token.StartsWith(TokenName, StringComparison.Ordinal) && entry.Value.UserId == user)
|
||||
.ToList();
|
||||
|
||||
var removed = 0;
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
_quickConnectTokens.Remove(token.Key, out _);
|
||||
_logger.LogDebug("Deleted token {AccessToken}", token.Key);
|
||||
removed++;
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose.
|
||||
/// </summary>
|
||||
|
@ -226,7 +165,7 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
{
|
||||
if (disposing)
|
||||
{
|
||||
_rng?.Dispose();
|
||||
_rng.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,22 +177,19 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
return Convert.ToHexString(bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ExpireRequests(bool expireAll = false)
|
||||
/// <summary>
|
||||
/// Expire quick connect requests that are over the time limit. If <paramref name="expireAll"/> is true, all requests are unconditionally expired.
|
||||
/// </summary>
|
||||
/// <param name="expireAll">If true, all requests will be expired.</param>
|
||||
private void ExpireRequests(bool expireAll = false)
|
||||
{
|
||||
// Check if quick connect should be deactivated
|
||||
if (State == QuickConnectState.Active && DateTime.UtcNow > DateActivated.AddMinutes(Timeout) && !expireAll)
|
||||
{
|
||||
_logger.LogDebug("Quick connect time expired, deactivating");
|
||||
SetState(QuickConnectState.Available);
|
||||
expireAll = true;
|
||||
}
|
||||
// All requests before this timestamp have expired
|
||||
var minTime = DateTime.UtcNow.AddMinutes(-Timeout);
|
||||
|
||||
// Expire stale connection requests
|
||||
foreach (var (_, currentRequest) in _currentRequests)
|
||||
{
|
||||
var added = currentRequest.DateAdded ?? DateTime.UnixEpoch;
|
||||
if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout))
|
||||
if (expireAll || currentRequest.DateAdded > minTime)
|
||||
{
|
||||
var code = currentRequest.Code;
|
||||
_logger.LogDebug("Removing expired request {Code}", code);
|
||||
|
@ -265,10 +201,5 @@ namespace Emby.Server.Implementations.QuickConnect
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReloadConfiguration()
|
||||
{
|
||||
State = _config.Configuration.QuickConnectAvailable ? QuickConnectState.Available : QuickConnectState.Unavailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Data.Events;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Progress;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
|
@ -14,6 +14,7 @@ using Jellyfin.Data.Entities.Security;
|
|||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Data.Events;
|
||||
using Jellyfin.Data.Queries;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller;
|
||||
|
@ -1438,9 +1439,9 @@ namespace Emby.Server.Implementations.Session
|
|||
return AuthenticateNewSessionInternal(request, true);
|
||||
}
|
||||
|
||||
public Task<AuthenticationResult> AuthenticateQuickConnect(AuthenticationRequest request, string token)
|
||||
public Task<AuthenticationResult> AuthenticateQuickConnect(Guid userId)
|
||||
{
|
||||
return AuthenticateNewSessionInternal(request, false);
|
||||
return AuthenticateNewSessionInternal(new AuthenticationRequest { UserId = userId }, false);
|
||||
}
|
||||
|
||||
private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Sorting;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.Sorting
|
|||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
|
||||
return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty);
|
||||
return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -11,7 +11,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Jellyfin.Data.Events;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.Net.Mime;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Jellyfin.Api
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Api.Attributes;
|
||||
using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Models.ConfigurationDtos;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
|
|
@ -8,8 +8,8 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Api.Attributes;
|
||||
using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Models.PluginDtos;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Model.Net;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.QuickConnect;
|
||||
using MediaBrowser.Model.QuickConnect;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -30,13 +32,12 @@ namespace Jellyfin.Api.Controllers
|
|||
/// Gets the current quick connect state.
|
||||
/// </summary>
|
||||
/// <response code="200">Quick connect state returned.</response>
|
||||
/// <returns>The current <see cref="QuickConnectState"/>.</returns>
|
||||
[HttpGet("Status")]
|
||||
/// <returns>Whether Quick Connect is enabled on the server or not.</returns>
|
||||
[HttpGet("Enabled")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<QuickConnectState> GetStatus()
|
||||
public ActionResult<bool> GetEnabled()
|
||||
{
|
||||
_quickConnect.ExpireRequests();
|
||||
return _quickConnect.State;
|
||||
return _quickConnect.IsEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -49,7 +50,14 @@ namespace Jellyfin.Api.Controllers
|
|||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<QuickConnectResult> Initiate()
|
||||
{
|
||||
return _quickConnect.TryConnect();
|
||||
try
|
||||
{
|
||||
return _quickConnect.TryConnect();
|
||||
}
|
||||
catch (AuthenticationException)
|
||||
{
|
||||
return Unauthorized("Quick connect is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -72,42 +80,10 @@ namespace Jellyfin.Api.Controllers
|
|||
{
|
||||
return NotFound("Unknown secret");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily activates quick connect for five minutes.
|
||||
/// </summary>
|
||||
/// <response code="204">Quick connect has been temporarily activated.</response>
|
||||
/// <response code="403">Quick connect is unavailable on this server.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
|
||||
[HttpPost("Activate")]
|
||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public ActionResult Activate()
|
||||
{
|
||||
if (_quickConnect.State == QuickConnectState.Unavailable)
|
||||
catch (AuthenticationException)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Quick connect is unavailable");
|
||||
return Unauthorized("Quick connect is disabled");
|
||||
}
|
||||
|
||||
_quickConnect.Activate();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables quick connect.
|
||||
/// </summary>
|
||||
/// <param name="status">New <see cref="QuickConnectState"/>.</param>
|
||||
/// <response code="204">Quick connect state set successfully.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
|
||||
[HttpPost("Available")]
|
||||
[Authorize(Policy = Policies.RequiresElevation)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public ActionResult Available([FromQuery] QuickConnectState status = QuickConnectState.Available)
|
||||
{
|
||||
_quickConnect.SetState(status);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -121,7 +97,7 @@ namespace Jellyfin.Api.Controllers
|
|||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public ActionResult<bool> Authorize([FromQuery, Required] string code)
|
||||
public async Task<ActionResult<bool>> Authorize([FromQuery, Required] string code)
|
||||
{
|
||||
var userId = ClaimHelpers.GetUserId(Request.HttpContext.User);
|
||||
if (!userId.HasValue)
|
||||
|
@ -129,26 +105,14 @@ namespace Jellyfin.Api.Controllers
|
|||
return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id");
|
||||
}
|
||||
|
||||
return _quickConnect.AuthorizeRequest(userId.Value, code);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deauthorize all quick connect devices for the current user.
|
||||
/// </summary>
|
||||
/// <response code="200">All quick connect devices were deleted.</response>
|
||||
/// <returns>The number of devices that were deleted.</returns>
|
||||
[HttpPost("Deauthorize")]
|
||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<int> Deauthorize()
|
||||
{
|
||||
var userId = ClaimHelpers.GetUserId(Request.HttpContext.User);
|
||||
if (!userId.HasValue)
|
||||
try
|
||||
{
|
||||
return 0;
|
||||
return await _quickConnect.AuthorizeRequest(userId.Value, code).ConfigureAwait(false);
|
||||
}
|
||||
catch (AuthenticationException)
|
||||
{
|
||||
return Unauthorized("Quick connect is disabled");
|
||||
}
|
||||
|
||||
return _quickConnect.DeleteAllDevices(userId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ using Jellyfin.Api.Constants;
|
|||
using Jellyfin.Api.Extensions;
|
||||
using Jellyfin.Api.ModelBinders;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
|
|
|
@ -9,7 +9,7 @@ using Jellyfin.Api.Extensions;
|
|||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.ModelBinders;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
|
||||
namespace Jellyfin.Api.Models.PlaylistDtos
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Session;
|
||||
|
||||
|
@ -85,4 +85,4 @@ namespace Jellyfin.Api.Models.SessionDtos
|
|||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using BlurHashSharp.SkiaSharp;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SkiaSharp;
|
||||
|
@ -142,9 +142,6 @@ namespace Jellyfin.Drawing.Skia
|
|||
return BlurHashEncoder.Encode(xComp, yComp, path, 128, 128);
|
||||
}
|
||||
|
||||
private static bool HasDiacritics(string text)
|
||||
=> !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
|
||||
|
||||
private bool RequiresSpecialCharacterHack(string path)
|
||||
{
|
||||
for (int i = 0; i < path.Length; i++)
|
||||
|
@ -155,7 +152,7 @@ namespace Jellyfin.Drawing.Skia
|
|||
}
|
||||
}
|
||||
|
||||
return HasDiacritics(path);
|
||||
return path.HasDiacritics();
|
||||
}
|
||||
|
||||
private string NormalizePath(string path)
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
|
|
@ -21,11 +21,11 @@ using Jellyfin.Api.Constants;
|
|||
using Jellyfin.Api.Controllers;
|
||||
using Jellyfin.Api.ModelBinders;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Networking.Configuration;
|
||||
using Jellyfin.Server.Configuration;
|
||||
using Jellyfin.Server.Filters;
|
||||
using Jellyfin.Server.Formatters;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.Net.Mime;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
|
|
@ -4,9 +4,9 @@ using Emby.Server.Implementations.Data;
|
|||
using Emby.Server.Implementations.Serialization;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Server.Implementations;
|
||||
using Jellyfin.Server.Implementations.Users;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
|
16
Jellyfin.sln
16
Jellyfin.sln
|
@ -83,6 +83,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -229,6 +235,14 @@ Global
|
|||
{A964008C-2136-4716-B6CB-B3426C22320A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -247,6 +261,8 @@ Global
|
|||
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||
{A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||
{750B8757-BE3D-4F8C-941A-FBAD94904ADA} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}
|
||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
|
@ -8,9 +8,9 @@ using System.Linq;
|
|||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using Diacritics.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.Audio
|
||||
|
|
|
@ -11,13 +11,14 @@ using System.Text;
|
|||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Text.Json;
|
|||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Controller.IO;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using Diacritics.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MediaBrowser.Controller.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseExtensions.
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string RemoveDiacritics(this string text)
|
||||
{
|
||||
var chars = Normalize(text, NormalizationForm.FormD)
|
||||
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark);
|
||||
|
||||
return Normalize(string.Concat(chars), NormalizationForm.FormC);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the number of occurrences of [needle] in the string.
|
||||
/// </summary>
|
||||
/// <param name="value">The haystack to search in.</param>
|
||||
/// <param name="needle">The character to search for.</param>
|
||||
/// <returns>The number of occurrences of the [needle] character.</returns>
|
||||
public static int Count(this ReadOnlySpan<char> value, char needle)
|
||||
{
|
||||
var count = 0;
|
||||
var length = value.Length;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
if (value[i] == needle)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true)
|
||||
{
|
||||
if (stripStringOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
return text.Normalize(form);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// will throw if input contains invalid unicode chars
|
||||
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
|
||||
text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])", string.Empty);
|
||||
return Normalize(text, form, false);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return text.Normalize(form);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// if it still fails, return the original text
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
|
||||
namespace MediaBrowser.Controller.Library
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Diacritics" Version="2.1.20036.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.QuickConnect;
|
||||
|
||||
|
@ -12,40 +11,9 @@ namespace MediaBrowser.Controller.QuickConnect
|
|||
public interface IQuickConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the length of user facing codes.
|
||||
/// Gets a value indicating whether quick connect is enabled or not.
|
||||
/// </summary>
|
||||
int CodeLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of internal access tokens.
|
||||
/// </summary>
|
||||
string TokenName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current state of quick connect.
|
||||
/// </summary>
|
||||
QuickConnectState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time (in minutes) before quick connect will automatically deactivate.
|
||||
/// </summary>
|
||||
int Timeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Assert that quick connect is currently active and throws an exception if it is not.
|
||||
/// </summary>
|
||||
void AssertActive();
|
||||
|
||||
/// <summary>
|
||||
/// Temporarily activates quick connect for a short amount of time.
|
||||
/// </summary>
|
||||
void Activate();
|
||||
|
||||
/// <summary>
|
||||
/// Changes the state of quick connect.
|
||||
/// </summary>
|
||||
/// <param name="newState">New state to change to.</param>
|
||||
void SetState(QuickConnectState newState);
|
||||
bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initiates a new quick connect request.
|
||||
|
@ -60,38 +28,12 @@ namespace MediaBrowser.Controller.QuickConnect
|
|||
/// <returns>Quick connect result.</returns>
|
||||
QuickConnectResult CheckRequestStatus(string secret);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a QuickConnect request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="token">The token.</param>
|
||||
void AuthenticateRequest(AuthenticationRequest request, string token);
|
||||
|
||||
/// <summary>
|
||||
/// Authorizes a quick connect request to connect as the calling user.
|
||||
/// </summary>
|
||||
/// <param name="userId">User id.</param>
|
||||
/// <param name="code">Identifying code for the request.</param>
|
||||
/// <returns>A boolean indicating if the authorization completed successfully.</returns>
|
||||
bool AuthorizeRequest(Guid userId, string code);
|
||||
|
||||
/// <summary>
|
||||
/// Expire quick connect requests that are over the time limit. If <paramref name="expireAll"/> is true, all requests are unconditionally expired.
|
||||
/// </summary>
|
||||
/// <param name="expireAll">If true, all requests will be expired.</param>
|
||||
void ExpireRequests(bool expireAll = false);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all quick connect access tokens for the provided user.
|
||||
/// </summary>
|
||||
/// <param name="user">Guid of the user to delete tokens for.</param>
|
||||
/// <returns>A count of the deleted tokens.</returns>
|
||||
int DeleteAllDevices(Guid user);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a short code to display to the user to uniquely identify this request.
|
||||
/// </summary>
|
||||
/// <returns>A short, unique alphanumeric string.</returns>
|
||||
string GenerateCode();
|
||||
Task<bool> AuthorizeRequest(Guid userId, string code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,10 +276,9 @@ namespace MediaBrowser.Controller.Session
|
|||
/// <summary>
|
||||
/// Authenticates a new session with quick connect.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="token">Quick connect access token.</param>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <returns>Task{SessionInfo}.</returns>
|
||||
Task<AuthenticationResult> AuthenticateQuickConnect(AuthenticationRequest request, string token);
|
||||
Task<AuthenticationResult> AuthenticateQuickConnect(Guid userId);
|
||||
|
||||
/// <summary>
|
||||
/// Reports the capabilities.
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
|
||||
namespace MediaBrowser.Controller.Sorting
|
||||
{
|
||||
public static class SortExtensions
|
||||
{
|
||||
private static readonly AlphanumComparator _comparer = new AlphanumComparator();
|
||||
private static readonly AlphanumericComparator _comparer = new AlphanumericComparator();
|
||||
|
||||
public static IEnumerable<T> OrderByString<T>(this IEnumerable<T> list, Func<T, string> getName)
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
|
|
@ -11,9 +11,9 @@ using System.Text.Json;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Probing;
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Globalization;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Nikse.SubtitleEdit.Core;
|
||||
|
|
|
@ -2,25 +2,28 @@
|
|||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
/// <summary>
|
||||
/// Delivery method to use during playback of a specific subtitle format.
|
||||
/// </summary>
|
||||
public enum SubtitleDeliveryMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// The encode.
|
||||
/// Burn the subtitles in the video track.
|
||||
/// </summary>
|
||||
Encode = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The embed.
|
||||
/// Embed the subtitles in the file or stream.
|
||||
/// </summary>
|
||||
Embed = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The external.
|
||||
/// Serve the subtitles as an external file.
|
||||
/// </summary>
|
||||
External = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The HLS.
|
||||
/// Serve the subtitles as a separate HLS stream.
|
||||
/// </summary>
|
||||
Hls = 3
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
namespace MediaBrowser.Model.Entities
|
||||
|
|
|
@ -50,7 +50,8 @@
|
|||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" />
|
||||
<ProjectReference Include="../Jellyfin.Data/Jellyfin.Data.csproj" />
|
||||
<ProjectReference Include="../src/Jellyfin.Extensions/Jellyfin.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -3,38 +3,46 @@ using System;
|
|||
namespace MediaBrowser.Model.QuickConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the result of an incoming quick connect request.
|
||||
/// Stores the state of an quick connect request.
|
||||
/// </summary>
|
||||
public class QuickConnectResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuickConnectResult"/> class.
|
||||
/// </summary>
|
||||
/// <param name="secret">The secret used to query the request state.</param>
|
||||
/// <param name="code">The code used to allow the request.</param>
|
||||
/// <param name="dateAdded">The time when the request was created.</param>
|
||||
public QuickConnectResult(string secret, string code, DateTime dateAdded)
|
||||
{
|
||||
Secret = secret;
|
||||
Code = code;
|
||||
DateAdded = dateAdded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this request is authorized.
|
||||
/// </summary>
|
||||
public bool Authenticated => !string.IsNullOrEmpty(Authentication);
|
||||
public bool Authenticated => Authentication != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the secret value used to uniquely identify this request. Can be used to retrieve authentication information.
|
||||
/// Gets the secret value used to uniquely identify this request. Can be used to retrieve authentication information.
|
||||
/// </summary>
|
||||
public string? Secret { get; set; }
|
||||
public string Secret { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user facing code used so the user can quickly differentiate this request from others.
|
||||
/// Gets the user facing code used so the user can quickly differentiate this request from others.
|
||||
/// </summary>
|
||||
public string? Code { get; set; }
|
||||
public string Code { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the private access token.
|
||||
/// </summary>
|
||||
public string? Authentication { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an error message.
|
||||
/// </summary>
|
||||
public string? Error { get; set; }
|
||||
public Guid? Authentication { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DateTime that this request was created.
|
||||
/// </summary>
|
||||
public DateTime? DateAdded { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
namespace MediaBrowser.Model.QuickConnect
|
||||
{
|
||||
/// <summary>
|
||||
/// Quick connect state.
|
||||
/// </summary>
|
||||
public enum QuickConnectState
|
||||
{
|
||||
/// <summary>
|
||||
/// This feature has not been opted into and is unavailable until the server administrator chooses to opt-in.
|
||||
/// </summary>
|
||||
Unavailable = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The feature is enabled for use on the server but is not currently accepting connection requests.
|
||||
/// </summary>
|
||||
Available = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The feature is actively accepting connection requests.
|
||||
/// </summary>
|
||||
Active = 2
|
||||
}
|
||||
}
|
|
@ -3,9 +3,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Net.Http;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
|
@ -11,7 +11,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Net.Http;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Json;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
|
|
|
@ -11,8 +11,8 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.Music
|
|||
}
|
||||
}
|
||||
|
||||
if (HasDiacritics(searchInfo.Name))
|
||||
if (searchInfo.Name.HasDiacritics())
|
||||
{
|
||||
// Try again using the search with accent characters url
|
||||
url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
|
||||
|
@ -251,16 +251,6 @@ namespace MediaBrowser.Providers.Music
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified text has diacritics.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <returns><c>true</c> if the specified text has diacritics; otherwise, <c>false</c>.</returns>
|
||||
private bool HasDiacritics(string text)
|
||||
{
|
||||
return !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes an URL.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace MediaBrowser.Providers.Plugins.Omdb
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a string <c>N/A</c> to <c>string.Empty</c>.
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace MediaBrowser.Providers.Plugins.Omdb
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a string <c>N/A</c> to <c>string.Empty</c>.
|
|
@ -10,8 +10,8 @@ using System.Text.Json;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
|
@ -10,8 +10,8 @@ using System.Text.Json;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
|
@ -15,7 +15,7 @@ using System.Runtime.InteropServices;
|
|||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
[assembly: InternalsVisibleTo("Jellyfin.Common.Tests")]
|
||||
[assembly: InternalsVisibleTo("Jellyfin.Providers.Tests")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Linq;
|
|||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
|
|
2
debian/rules
vendored
2
debian/rules
vendored
|
@ -39,7 +39,7 @@ override_dh_auto_test:
|
|||
override_dh_clistrip:
|
||||
|
||||
override_dh_auto_build:
|
||||
dotnet publish --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
|
||||
dotnet publish -maxcpucount:1 --configuration $(CONFIG) --output='$(CURDIR)/usr/lib/jellyfin/bin' --self-contained --runtime $(DOTNETRUNTIME) \
|
||||
"-p:DebugSymbols=false;DebugType=none" Jellyfin.Server
|
||||
|
||||
override_dh_auto_clean:
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Sorting
|
||||
namespace Jellyfin.Extensions
|
||||
{
|
||||
public class AlphanumComparator : IComparer<string?>
|
||||
/// <summary>
|
||||
/// Alphanumeric <see cref="IComparer{T}" />.
|
||||
/// </summary>
|
||||
public class AlphanumericComparator : IComparer<string?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
|
||||
/// </summary>
|
||||
/// <param name="s1">The first object to compare.</param>
|
||||
/// <param name="s2">The second object to compare.</param>
|
||||
/// <returns>A signed integer that indicates the relative values of <c>x</c> and <c>y</c>.</returns>
|
||||
public static int CompareValues(string? s1, string? s2)
|
||||
{
|
||||
if (s1 == null && s2 == null)
|
|
@ -1,6 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
namespace Jellyfin.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides <c>CopyTo</c> extensions methods for <see cref="IReadOnlyList{T}" />.
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
namespace Jellyfin.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Static extensions for the <see cref="IEnumerable{T}"/> interface.
|
30
src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
Normal file
30
src/Jellyfin.Extensions/Jellyfin.Extensions.csproj
Normal file
|
@ -0,0 +1,30 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../../jellyfin.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>Jellyfin Contributors</Authors>
|
||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="../../SharedVersion.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
||||
</Project>
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a number to a boolean.
|
||||
|
@ -27,4 +27,4 @@ namespace MediaBrowser.Common.Json.Converters
|
|||
writer.WriteBooleanValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert comma delimited string to array of type.
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Json comma delimited array converter factory.
|
|
@ -3,7 +3,7 @@ using System.Globalization;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy DateTime converter.
|
||||
|
@ -31,4 +31,4 @@ namespace MediaBrowser.Common.Json.Converters
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert delimited string to array of type.
|
|
@ -3,7 +3,7 @@ using System.Globalization;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a GUID object or value to/from JSON.
|
|
@ -1,12 +1,8 @@
|
|||
#nullable disable
|
||||
// THIS IS A HACK
|
||||
// TODO: @bond Move to separate project
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Model.Entities
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an object to a lowercase string.
|
||||
|
@ -15,7 +11,7 @@ namespace MediaBrowser.Model.Entities
|
|||
public class JsonLowerCaseConverter<T> : JsonConverter<T>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(ref reader, options);
|
||||
}
|
||||
|
@ -23,7 +19,7 @@ namespace MediaBrowser.Model.Entities
|
|||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value?.ToString().ToLowerInvariant());
|
||||
writer.WriteStringValue(value?.ToString()?.ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using System.Globalization;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a GUID object or value to/from JSON.
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a nullable struct or value to/from JSON.
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Json nullable struct converter factory.
|
|
@ -3,7 +3,7 @@ using System.ComponentModel;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert Pipe delimited string to array of type.
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Json Pipe delimited array converter factory.
|
|
@ -4,7 +4,7 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converter to allow the serializer to read strings.
|
|
@ -2,7 +2,7 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
namespace Jellyfin.Extensions.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Version object or value to/from JSON.
|
|
@ -1,8 +1,8 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using MediaBrowser.Common.Json.Converters;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
|
||||
namespace MediaBrowser.Common.Json
|
||||
namespace Jellyfin.Extensions.Json
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for having compatible JSON throughout the codebase.
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Common.Extensions
|
||||
namespace Jellyfin.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides <c>Shuffle</c> extensions methods for <see cref="IList{T}" />.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user