From 9ec2870b1034e0b64ebde3e29a3779c7f6bb2ac4 Mon Sep 17 00:00:00 2001 From: luz paz Date: Mon, 15 Aug 2022 06:48:34 -0400 Subject: Fix various typos Found via `codespell -q 3 -S ./Emby.Server.Implementations/Localization -L allready,doesnt,inh,receivedfrom,whoknows` --- Jellyfin.Api/Controllers/AudioController.cs | 2 +- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 ++-- Jellyfin.Api/Controllers/SearchController.cs | 6 +++--- Jellyfin.Api/Controllers/UserController.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 54ac06276e..94f7a7b827 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -207,7 +207,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The dlna device profile id to utilize. /// The play session id. /// The segment container. - /// The segment lenght. + /// The segment length. /// The minimum number of segments. /// The media version id, if playing an alternate version. /// The device id of the client requesting. Used to stop encoding processes when needed. diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 1e8d038751..3ed80f6621 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -121,7 +121,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The dlna device profile id to utilize. /// The play session id. /// The segment container. - /// The segment lenght. + /// The segment length. /// The minimum number of segments. /// The media version id, if playing an alternate version. /// The device id of the client requesting. Used to stop encoding processes when needed. @@ -1832,7 +1832,7 @@ namespace Jellyfin.Api.Controllers // Set the key frame params for video encoding to match the hls segment time. args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, isEventPlaylist, startNumber); - // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. + // Currently b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) { args += " -bf 0"; diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 07e113ad3e..3b1f7a52ab 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -60,9 +60,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum number of records to return. /// Optional. Supply a user id to search within a user's library or omit to search all. /// The search term to filter on. - /// If specified, only results with the specified item types are returned. This allows multiple, comma delimeted. - /// If specified, results with these item types are filtered out. This allows multiple, comma delimeted. - /// If specified, only results with the specified media types are returned. This allows multiple, comma delimeted. + /// If specified, only results with the specified item types are returned. This allows multiple, comma delimited. + /// If specified, results with these item types are filtered out. This allows multiple, comma delimited. + /// If specified, only results with the specified media types are returned. This allows multiple, comma delimited. /// If specified, only children of the parent are returned. /// Optional filter for movies. /// Optional filter for series. diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 82c8563a80..d1109bebc8 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -502,7 +502,7 @@ namespace Jellyfin.Api.Controllers if (isLocal) { - _logger.LogWarning("Password reset proccess initiated from outside the local network with IP: {IP}", ip); + _logger.LogWarning("Password reset process initiated from outside the local network with IP: {IP}", ip); } var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false); -- cgit v1.2.3 From 5036afd69127cc6f6b0801329b2b04f21aee71f5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 12 Aug 2022 20:37:31 +0200 Subject: Minor cleanup --- .../AppBase/BaseConfigurationManager.cs | 12 ++--------- Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs | 7 +----- .../Attributes/HttpUnsubscribeAttribute.cs | 7 +----- .../Controllers/DisplayPreferencesController.cs | 7 ++---- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 6 ++++-- .../Users/DisplayPreferencesManager.cs | 2 +- .../SymlinkFollowingPhysicalFileResultExecutor.cs | 11 ++-------- .../Configuration/ConfigurationUpdateEventArgs.cs | 25 ++++++++++++++++------ .../Configuration/IApplicationPaths.cs | 2 -- .../Entities/BasePluginFolder.cs | 4 +--- MediaBrowser.Controller/Entities/Extensions.cs | 8 +++---- .../IDisplayPreferencesManager.cs | 6 ++---- 12 files changed, 38 insertions(+), 59 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 2a4a8fb132..c42cec593a 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -365,11 +365,7 @@ namespace Emby.Server.Implementations.AppBase validatingStore.Validate(currentConfiguration, configuration); } - NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); _configurations.AddOrUpdate(key, configuration, (_, _) => configuration); @@ -391,11 +387,7 @@ namespace Emby.Server.Implementations.AppBase /// The old configuration. protected virtual void OnNamedConfigurationUpdated(string key, object configuration) { - NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); } /// diff --git a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs index af8727552c..7ac089a344 100644 --- a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs +++ b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs @@ -25,11 +25,6 @@ namespace Jellyfin.Api.Attributes /// The route template. May not be null. public HttpSubscribeAttribute(string template) : base(_supportedMethods, template) - { - if (template == null) - { - throw new ArgumentNullException(nameof(template)); - } - } + => ArgumentNullException.ThrowIfNull(template, nameof(template)); } } diff --git a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs index 1c0b70e719..16b3d08160 100644 --- a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs +++ b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs @@ -25,11 +25,6 @@ namespace Jellyfin.Api.Attributes /// The route template. May not be null. public HttpUnsubscribeAttribute(string template) : base(_supportedMethods, template) - { - if (template == null) - { - throw new ArgumentNullException(nameof(template)); - } - } + => ArgumentNullException.ThrowIfNull(template, nameof(template)); } } diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 27eb223390..64ee5680ce 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -89,12 +89,9 @@ namespace Jellyfin.Api.Controllers // Load all custom display preferences var customDisplayPreferences = _displayPreferencesManager.ListCustomItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client); - if (customDisplayPreferences != null) + foreach (var (key, value) in customDisplayPreferences) { - foreach (var (key, value) in customDisplayPreferences) - { - dto.CustomPrefs.TryAdd(key, value); - } + dto.CustomPrefs.TryAdd(key, value); } // This will essentially be a noop if no changes have been made, but new prefs must be saved at least. diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 6875282318..13f155f357 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -145,9 +145,11 @@ namespace Jellyfin.Drawing.Skia /// The file at the specified path could not be used to generate a codec. public string GetImageBlurHash(int xComp, int yComp, string path) { - if (path == null) + ArgumentNullException.ThrowIfNull(path, nameof(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.AsSpan()).TrimStart('.'); diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index f5d38db20f..65edb30ad2 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences) + public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences) { var existingPrefs = _dbContext.CustomItemDisplayPreferences .AsQueryable() diff --git a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs index 7e7e4ca95b..28ed3894fd 100644 --- a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs +++ b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs @@ -69,15 +69,8 @@ namespace Jellyfin.Server.Infrastructure /// protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue? range, long rangeLength) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(context, nameof(context)); + ArgumentNullException.ThrowIfNull(result, nameof(result)); if (range != null && rangeLength == 0) { diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs index 2df87d8792..90b1ff70c2 100644 --- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -1,22 +1,33 @@ -#nullable disable -#pragma warning disable CS1591 - using System; namespace MediaBrowser.Common.Configuration { + /// + /// for the ConfigurationUpdated event. + /// public class ConfigurationUpdateEventArgs : EventArgs { /// - /// Gets or sets the key. + /// Initializes a new instance of the class. + /// + /// The configuration key. + /// The new configuration. + public ConfigurationUpdateEventArgs(string key, object newConfiguration) + { + Key = key; + NewConfiguration = newConfiguration; + } + + /// + /// Gets the key. /// /// The key. - public string Key { get; set; } + public string Key { get; } /// - /// Gets or sets the new configuration. + /// Gets the new configuration. /// /// The new configuration. - public object NewConfiguration { get; set; } + public object NewConfiguration { get; } } } diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index 1370e6d79e..57c6546675 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -1,5 +1,3 @@ -#nullable disable - namespace MediaBrowser.Common.Configuration { /// diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 272a37df1b..afafaf1c24 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System.Text.Json.Serialization; @@ -13,7 +11,7 @@ namespace MediaBrowser.Controller.Entities public abstract class BasePluginFolder : Folder, ICollectionFolder { [JsonIgnore] - public virtual string CollectionType => null; + public virtual string? CollectionType => null; [JsonIgnore] public override bool SupportsInheritedParentImages => false; diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index 9ce8eebe34..14534aa50b 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Linq; using Jellyfin.Extensions; @@ -19,9 +17,11 @@ namespace MediaBrowser.Controller.Entities /// Trailer URL. public static void AddTrailerUrl(this BaseItem item, string url) { - if (string.IsNullOrEmpty(url)) + ArgumentNullException.ThrowIfNull(url, nameof(url)); + + if (url.Length == 0) { - throw new ArgumentNullException(nameof(url)); + throw new ArgumentException("String can't be empty", nameof(url)); } var current = item.RemoteTrailers.FirstOrDefault(i => string.Equals(i.Url, url, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs index 1678d50675..10c0f56e09 100644 --- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs +++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using Jellyfin.Data.Entities; @@ -50,7 +48,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client string. /// The dictionary of custom item display preferences. - Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); + Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); /// /// Sets the custom item display preference for the user and client. @@ -59,7 +57,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client id. /// A dictionary of custom item display preferences. - void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences); + void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences); /// /// Saves changes made to the database. -- cgit v1.2.3 From e9bb448e89f1e75d6571f6685fbefe360b47be78 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 4 Sep 2022 10:12:58 +0200 Subject: Simplify UniversalAudioController code, remove redundant null checks --- .../Controllers/UniversalAudioController.cs | 89 ++++++++-------------- 1 file changed, 31 insertions(+), 58 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 6fcafd426c..43b8e2414d 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -10,13 +10,11 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Session; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -31,7 +29,6 @@ namespace Jellyfin.Api.Controllers public class UniversalAudioController : BaseJellyfinApiController { private readonly IAuthorizationContext _authorizationContext; - private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly MediaInfoHelper _mediaInfoHelper; @@ -42,7 +39,6 @@ namespace Jellyfin.Api.Controllers /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of . @@ -50,7 +46,6 @@ namespace Jellyfin.Api.Controllers /// Instance of . public UniversalAudioController( IAuthorizationContext authorizationContext, - IDeviceManager deviceManager, ILibraryManager libraryManager, ILogger logger, MediaInfoHelper mediaInfoHelper, @@ -58,7 +53,6 @@ namespace Jellyfin.Api.Controllers DynamicHlsHelper dynamicHlsHelper) { _authorizationContext = authorizationContext; - _deviceManager = deviceManager; _libraryManager = libraryManager; _logger = logger; _mediaInfoHelper = mediaInfoHelper; @@ -123,70 +117,49 @@ namespace Jellyfin.Api.Controllers _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", deviceProfile); - if (deviceProfile == null) - { - var clientCapabilities = _deviceManager.GetCapabilities(authInfo.DeviceId); - if (clientCapabilities != null) - { - deviceProfile = clientCapabilities.DeviceProfile; - } - } - var info = await _mediaInfoHelper.GetPlaybackInfo( itemId, userId, mediaSourceId) .ConfigureAwait(false); - if (deviceProfile != null) - { - // set device specific data - var item = _libraryManager.GetItemById(itemId); - - foreach (var sourceInfo in info.MediaSources) - { - _mediaInfoHelper.SetDeviceSpecificData( - item, - sourceInfo, - deviceProfile, - authInfo, - maxStreamingBitrate ?? deviceProfile.MaxStreamingBitrate, - startTimeTicks ?? 0, - mediaSourceId ?? string.Empty, - null, - null, - maxAudioChannels, - info.PlaySessionId!, - userId ?? Guid.Empty, - true, - true, - true, - true, - true, - Request.HttpContext.GetNormalizedRemoteIp()); - } + // set device specific data + var item = _libraryManager.GetItemById(itemId); - _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); + foreach (var sourceInfo in info.MediaSources) + { + _mediaInfoHelper.SetDeviceSpecificData( + item, + sourceInfo, + deviceProfile, + authInfo, + maxStreamingBitrate ?? deviceProfile.MaxStreamingBitrate, + startTimeTicks ?? 0, + mediaSourceId ?? string.Empty, + null, + null, + maxAudioChannels, + info.PlaySessionId!, + userId ?? Guid.Empty, + true, + true, + true, + true, + true, + Request.HttpContext.GetNormalizedRemoteIp()); } - if (info.MediaSources != null) + _mediaInfoHelper.SortMediaSources(info, maxStreamingBitrate); + + foreach (var source in info.MediaSources) { - foreach (var source in info.MediaSources) - { - _mediaInfoHelper.NormalizeMediaSourceContainer(source, deviceProfile!, DlnaProfileType.Video); - } + _mediaInfoHelper.NormalizeMediaSourceContainer(source, deviceProfile, DlnaProfileType.Video); } - var mediaSource = info.MediaSources![0]; - if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http) + var mediaSource = info.MediaSources[0]; + if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http && enableRedirection && mediaSource.IsRemote && enableRemoteMedia.HasValue && enableRemoteMedia.Value) { - if (enableRedirection) - { - if (mediaSource.IsRemote && enableRemoteMedia.HasValue && enableRemoteMedia.Value) - { - return Redirect(mediaSource.Path); - } - } + return Redirect(mediaSource.Path); } var isStatic = mediaSource.SupportsDirectStream; @@ -249,7 +222,7 @@ namespace Jellyfin.Api.Controllers BreakOnNonKeyFrames = breakOnNonKeyFrames, AudioSampleRate = maxAudioSampleRate, MaxAudioChannels = maxAudioChannels, - AudioBitRate = isStatic ? (int?)null : (audioBitRate ?? maxStreamingBitrate), + AudioBitRate = isStatic ? null : (audioBitRate ?? maxStreamingBitrate), MaxAudioBitDepth = maxAudioBitDepth, AudioChannels = maxAudioChannels, CopyTimestamps = true, -- cgit v1.2.3 From 49e56c147ec3c1318e727a34ed916f2a4d4032d0 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 4 Sep 2022 10:21:21 +0200 Subject: Fix QuickConnect operation names --- Jellyfin.Api/Controllers/QuickConnectController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 87b78fe93f..1df26355f0 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -39,7 +39,7 @@ namespace Jellyfin.Api.Controllers /// Whether Quick Connect is enabled on the server or not. [HttpGet("Enabled")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetEnabled() + public ActionResult GetQuickConnectEnabled() { return _quickConnect.IsEnabled; } @@ -52,7 +52,7 @@ namespace Jellyfin.Api.Controllers /// A with a secret and code for future use or an error message. [HttpGet("Initiate")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> Initiate() + public async Task> InitiateQuickConnect() { try { @@ -75,7 +75,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Connect")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult Connect([FromQuery, Required] string secret) + public ActionResult GetQuickConnectState([FromQuery, Required] string secret) { try { @@ -102,7 +102,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public async Task> Authorize([FromQuery, Required] string code) + public async Task> AuthorizeQuickConnect([FromQuery, Required] string code) { var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); if (!userId.HasValue) -- cgit v1.2.3 From 5f5347aee3209383248a6055318ec8883291d406 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:14:23 -0400 Subject: Add Lyrics API Endpoint --- .gitignore | 2 + Jellyfin.Api/Controllers/UserLibraryController.cs | 49 ++++++++++++ Jellyfin.Api/Helpers/ItemHelper.cs | 87 ++++++++++++++++++++++ Jellyfin.Api/Jellyfin.Api.csproj | 12 +++ Jellyfin.Api/Libraries/LrcParser.dll | Bin 0 -> 12288 bytes Jellyfin.Api/Models/UserDtos/Lyrics.cs | 23 ++++++ 6 files changed, 173 insertions(+) create mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs create mode 100644 Jellyfin.Api/Libraries/LrcParser.dll create mode 100644 Jellyfin.Api/Models/UserDtos/Lyrics.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/.gitignore b/.gitignore index c2ae76c1e3..3f80ffcf7d 100644 --- a/.gitignore +++ b/.gitignore @@ -281,3 +281,5 @@ apiclient/generated # Omnisharp crash logs mono_crash.*.json + +!LrcParser.dll \ No newline at end of file diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index e45f9b58cd..89fb56744c 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; +using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; +using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; +using Kfstorm.LrcParser; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -381,5 +385,50 @@ namespace Jellyfin.Api.Controllers return _userDataRepository.GetUserDataDto(item, user); } + + /// + /// Gets an item's lyrics. + /// + /// User id. + /// Item id. + /// Lyrics returned. + /// Something went wrong. No Lyrics will be returned. + /// An containing the intros to play. + [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + { + var user = _userManager.GetUserById(userId); + + if (user == null) + { + List lyricsList = new List + { + new Lyrics { Error = "User Not Found" } + }; + return NotFound(new { Results = lyricsList.ToArray() }); + } + + var item = itemId.Equals(default) + ? _libraryManager.GetUserRootFolder() + : _libraryManager.GetItemById(itemId); + + if (item == null) + { + List lyricsList = new List + { + new Lyrics { Error = "Requested Item Not Found" } + }; + return NotFound(new { Results = lyricsList.ToArray() }); + } + + List result = ItemHelper.GetLyricData(item); + if (string.IsNullOrEmpty(result.ElementAt(0).Error)) + { + return Ok(new { Results = result }); + } + + return NotFound(new { Results = result.ToArray() }); + } } } diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs new file mode 100644 index 0000000000..43ef7aa093 --- /dev/null +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Jellyfin.Api.Models.UserDtos; +using Kfstorm.LrcParser; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Helpers +{ + /// + /// Item helper. + /// + public static class ItemHelper + { + /// + /// Opens lyrics file, converts to a List of Lyrics, and returns it. + /// + /// Requested Item. + /// Collection of Lyrics. + internal static List GetLyricData(BaseItem item) + { + List lyricsList = new List(); + + string lrcFilePath = @Path.ChangeExtension(item.Path, "lrc"); + + // LRC File not found, fallback to TXT file + if (!System.IO.File.Exists(lrcFilePath)) + { + string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); + if (!System.IO.File.Exists(txtFilePath)) + { + lyricsList.Add(new Lyrics { Error = "Lyric File Not Found" }); + return lyricsList; + } + + var lyricTextData = System.IO.File.ReadAllText(txtFilePath); + string[] lyricTextLines = lyricTextData.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + + foreach (var lyricLine in lyricTextLines) + { + lyricsList.Add(new Lyrics { Text = lyricLine }); + } + + return lyricsList; + } + + // Process LRC File + ILrcFile lyricData; + string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); + try + { + lrcFileContent = lrcFileContent.Replace('<', '['); + lrcFileContent = lrcFileContent.Replace('>', ']'); + lyricData = Kfstorm.LrcParser.LrcFile.FromText(lrcFileContent); + } + catch + { + lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); + return lyricsList; + } + + if (lyricData == null) + { + lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); + return lyricsList; + } + + foreach (var lyricLine in lyricData.Lyrics) + { + double ticks = lyricLine.Timestamp.TotalSeconds * 10000000; + lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = lyricLine.Content }); + } + + return lyricsList; + } + } +} diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 894d871383..1b78bb1074 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -46,4 +46,16 @@ + + + Libraries\LrcParser.dll + + + + + + Always + + + diff --git a/Jellyfin.Api/Libraries/LrcParser.dll b/Jellyfin.Api/Libraries/LrcParser.dll new file mode 100644 index 0000000000..46e4fb7033 Binary files /dev/null and b/Jellyfin.Api/Libraries/LrcParser.dll differ diff --git a/Jellyfin.Api/Models/UserDtos/Lyrics.cs b/Jellyfin.Api/Models/UserDtos/Lyrics.cs new file mode 100644 index 0000000000..df1b5d08a2 --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/Lyrics.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Lyric dto. + /// + public class Lyrics + { + /// + /// Gets or sets the start. + /// + public double? Start { get; set; } + + /// + /// Gets or sets the test. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the error. + /// + public string? Error { get; set; } + } +} -- cgit v1.2.3 From 8b78802c0b42d9d55f5bd8c3f34a542d78903f75 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:08:38 -0400 Subject: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 89fb56744c..42367940d3 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -402,11 +402,7 @@ namespace Jellyfin.Api.Controllers if (user == null) { - List lyricsList = new List - { - new Lyrics { Error = "User Not Found" } - }; - return NotFound(new { Results = lyricsList.ToArray() }); + return NotFound(); } var item = itemId.Equals(default) -- cgit v1.2.3 From 92715a74262fbf52f7071b45f5b61b7f057bb28e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:09:39 -0400 Subject: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 42367940d3..a1b911a450 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -424,7 +424,7 @@ namespace Jellyfin.Api.Controllers return Ok(new { Results = result }); } - return NotFound(new { Results = result.ToArray() }); + return NotFound(); } } } -- cgit v1.2.3 From 0aa2780ea7b23aed31765550f707f7ea9fc0daf1 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 9 Sep 2022 21:15:57 -0400 Subject: Update Jellyfin.Api/Controllers/UserLibraryController.cs Co-authored-by: Cody Robibero --- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index a1b911a450..7830df9bc9 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -411,11 +411,7 @@ namespace Jellyfin.Api.Controllers if (item == null) { - List lyricsList = new List - { - new Lyrics { Error = "Requested Item Not Found" } - }; - return NotFound(new { Results = lyricsList.ToArray() }); + return NotFound(); } List result = ItemHelper.GetLyricData(item); -- cgit v1.2.3 From 2e260e5319b0b58290a1e30a28886c69d5a65325 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:29:30 -0400 Subject: Updates based on review --- Jellyfin.Api/Controllers/UserLibraryController.cs | 9 ++- Jellyfin.Api/Helpers/ItemHelper.cs | 68 +++++++++++++--------- Jellyfin.Api/Jellyfin.Api.csproj | 13 +---- Jellyfin.Api/Libraries/LrcParser.dll | Bin 12288 -> 0 bytes 4 files changed, 46 insertions(+), 44 deletions(-) delete mode 100644 Jellyfin.Api/Libraries/LrcParser.dll (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 89fb56744c..afd4013ed3 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -12,7 +12,6 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; -using Kfstorm.LrcParser; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -422,13 +421,13 @@ namespace Jellyfin.Api.Controllers return NotFound(new { Results = lyricsList.ToArray() }); } - List result = ItemHelper.GetLyricData(item); - if (string.IsNullOrEmpty(result.ElementAt(0).Error)) + var result = ItemHelper.GetLyricData(item); + if (result is not null) { - return Ok(new { Results = result }); + return Ok(result); } - return NotFound(new { Results = result.ToArray() }); + return NotFound(); } } } diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index 43ef7aa093..c1b5ea6ccc 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -1,19 +1,13 @@ using System; using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; using System.IO; -using System.Net.Http; -using System.Threading.Tasks; +using System.Linq; using Jellyfin.Api.Models.UserDtos; -using Kfstorm.LrcParser; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Dlna; +using LrcParser.Model; +using LrcParser.Parser; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Helpers { @@ -27,7 +21,7 @@ namespace Jellyfin.Api.Helpers /// /// Requested Item. /// Collection of Lyrics. - internal static List GetLyricData(BaseItem item) + internal static object? GetLyricData(BaseItem item) { List lyricsList = new List(); @@ -39,8 +33,7 @@ namespace Jellyfin.Api.Helpers string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); if (!System.IO.File.Exists(txtFilePath)) { - lyricsList.Add(new Lyrics { Error = "Lyric File Not Found" }); - return lyricsList; + return null; } var lyricTextData = System.IO.File.ReadAllText(txtFilePath); @@ -51,37 +44,58 @@ namespace Jellyfin.Api.Helpers lyricsList.Add(new Lyrics { Text = lyricLine }); } - return lyricsList; + return new { lyrics = lyricsList }; } // Process LRC File - ILrcFile lyricData; + Song lyricData; + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); + try { - lrcFileContent = lrcFileContent.Replace('<', '['); - lrcFileContent = lrcFileContent.Replace('>', ']'); - lyricData = Kfstorm.LrcParser.LrcFile.FromText(lrcFileContent); + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + lyricData = lrcLyricParser.Decode(lrcFileContent); + var _metaData = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string dataRow in _metaData) + { + var data = dataRow.Split(":"); + + string newPropertyName = data[0].Replace("[", string.Empty, StringComparison.Ordinal); + string newPropertyValue = data[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(newPropertyName, newPropertyValue); + } + + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); } catch { - lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); - return lyricsList; + return null; } if (lyricData == null) { - lyricsList.Add(new Lyrics { Error = "No Lyrics Data" }); - return lyricsList; + return null; } - foreach (var lyricLine in lyricData.Lyrics) + for (int i = 0; i < sortedLyricData.Count; i++) { - double ticks = lyricLine.Timestamp.TotalSeconds * 10000000; - lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = lyricLine.Content }); + if (sortedLyricData[i].TimeTags.Count > 0) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } } - return lyricsList; + return new { MetaData = metaData, lyrics = lyricsList }; } } } diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 1b78bb1074..972387e02a 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,6 +17,7 @@ + @@ -46,16 +47,4 @@ - - - Libraries\LrcParser.dll - - - - - - Always - - - diff --git a/Jellyfin.Api/Libraries/LrcParser.dll b/Jellyfin.Api/Libraries/LrcParser.dll deleted file mode 100644 index 46e4fb7033..0000000000 Binary files a/Jellyfin.Api/Libraries/LrcParser.dll and /dev/null differ -- cgit v1.2.3 From d2e18547b16fa30684d7c9ceb8a117d5d9bcef56 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 11 Sep 2022 17:47:01 -0600 Subject: Require properly typed ActionResult (#8382) --- Jellyfin.Api/BaseJellyfinApiController.cs | 37 ++++++++++++++++++++++ .../Controllers/ConfigurationController.cs | 1 - Jellyfin.Api/Controllers/DlnaServerController.cs | 8 ++--- Jellyfin.Api/Controllers/MediaInfoController.cs | 1 - Jellyfin.Api/Controllers/MoviesController.cs | 2 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 1 - Jellyfin.Api/Controllers/UserViewsController.cs | 10 ++---- Jellyfin.Api/Results/OkResultOfT.cs | 21 ++++++++++++ 8 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 Jellyfin.Api/Results/OkResultOfT.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 59d6b75137..0c63d24b70 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.Net.Mime; +using Jellyfin.Api.Results; using Jellyfin.Extensions.Json; using Microsoft.AspNetCore.Mvc; @@ -15,5 +17,40 @@ namespace Jellyfin.Api JsonDefaults.PascalCaseMediaType)] public class BaseJellyfinApiController : ControllerBase { + /// + /// Create a new . + /// + /// The value to return. + /// The type to return. + /// The . + protected ActionResult> Ok(List value) + => new OkResult>(value); + + /// + /// Create a new . + /// + /// The value to return. + /// The type to return. + /// The . + protected ActionResult> Ok(IReadOnlyList value) + => new OkResult>(value); + + /// + /// Create a new . + /// + /// The value to return. + /// The type to return. + /// The . + protected ActionResult> Ok(IEnumerable? value) + => new OkResult?>(value); + + /// + /// Create a new . + /// + /// The value to return. + /// The type to return. + /// The . + protected ActionResult Ok(T value) + => new OkResult(value); } } diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 464fadc060..bbe1633120 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -2,7 +2,6 @@ using System; using System.ComponentModel.DataAnnotations; using System.Net.Mime; using System.Text.Json; -using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.ConfigurationDtos; diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 401c0197ac..8859d60207 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -54,7 +54,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status503ServiceUnavailable)] [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] - public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) + public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) { var url = GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); @@ -77,7 +77,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetContentDirectory([FromRoute, Required] string serverId) + public ActionResult GetContentDirectory([FromRoute, Required] string serverId) { return Ok(_contentDirectory.GetServiceXml()); } @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId) + public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId) { return Ok(_mediaReceiverRegistrar.GetServiceXml()); } @@ -117,7 +117,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetConnectionManager([FromRoute, Required] string serverId) + public ActionResult GetConnectionManager([FromRoute, Required] string serverId) { return Ok(_connectionManager.GetServiceXml()); } diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 75df18204d..d2852ed011 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -12,7 +12,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 420dd99233..4669447049 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -170,7 +170,7 @@ namespace Jellyfin.Api.Controllers } } - return Ok(categories.OrderBy(i => i.RecommendationType)); + return Ok(categories.OrderBy(i => i.RecommendationType).AsEnumerable()); } private IEnumerable GetWithDirector( diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index e45f9b58cd..1656a1e98c 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -8,7 +8,6 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; -using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 5cc8c906fb..04732ccf25 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; -using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; @@ -11,9 +10,7 @@ using Jellyfin.Api.Models.UserViewDtos; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; @@ -32,7 +29,6 @@ namespace Jellyfin.Api.Controllers private readonly IUserManager _userManager; private readonly IUserViewManager _userViewManager; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; private readonly ILibraryManager _libraryManager; /// @@ -41,19 +37,16 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. public UserViewsController( IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, - IAuthorizationContext authContext, ILibraryManager libraryManager) { _userManager = userManager; _userViewManager = userViewManager; _dtoService = dtoService; - _authContext = authContext; _libraryManager = libraryManager; } @@ -138,7 +131,8 @@ namespace Jellyfin.Api.Controllers Name = i.Name, Id = i.Id.ToString("N", CultureInfo.InvariantCulture) }) - .OrderBy(i => i.Name)); + .OrderBy(i => i.Name) + .AsEnumerable()); } } } diff --git a/Jellyfin.Api/Results/OkResultOfT.cs b/Jellyfin.Api/Results/OkResultOfT.cs new file mode 100644 index 0000000000..f60cbbceea --- /dev/null +++ b/Jellyfin.Api/Results/OkResultOfT.cs @@ -0,0 +1,21 @@ +#pragma warning disable SA1649 // File name should match type name. + +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Results; + +/// +/// Ok result with type specified. +/// +/// The type to return. +public class OkResult : OkObjectResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The value to return. + public OkResult(T value) + : base(value) + { + } +} -- cgit v1.2.3 From c71d6f2358ba8a5b1f80a2bef41b6f438c06d03c Mon Sep 17 00:00:00 2001 From: Zach Phelan Date: Tue, 13 Sep 2022 16:40:47 -0600 Subject: Sort special features same as other spots, removing unnecssary function Added to contributors --- CONTRIBUTORS.md | 1 + Jellyfin.Api/Controllers/UserLibraryController.cs | 3 ++- MediaBrowser.Controller/Entities/BaseItem.cs | 13 ------------- 3 files changed, 3 insertions(+), 14 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 98acd4449f..8daaae4d94 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -148,6 +148,7 @@ - [xosdy](https://github.com/xosdy) - [XVicarious](https://github.com/XVicarious) - [YouKnowBlom](https://github.com/YouKnowBlom) + - [ZachPhelan](https://github.com/ZachPhelan) - [KristupasSavickas](https://github.com/KristupasSavickas) - [Pusta](https://github.com/pusta) - [nielsvanvelzen](https://github.com/nielsvanvelzen) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1656a1e98c..940fa27a70 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -233,7 +233,8 @@ namespace Jellyfin.Api.Controllers var dtoOptions = new DtoOptions().AddClientFields(Request); return Ok(item - .GetExtras(BaseItem.DisplayExtraTypes) + .GetExtras() + .Where(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value)) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))); } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 5cee6ce406..988d0a534a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2606,19 +2606,6 @@ namespace MediaBrowser.Controller.Entities .OrderBy(i => i.SortName); } - /// - /// Get all extras with specific types that are associated with this item. - /// - /// The types of extras to retrieve. - /// An enumerable containing the extras. - public IEnumerable GetExtras(IReadOnlyCollection extraTypes) - { - return ExtraIds - .Select(LibraryManager.GetItemById) - .Where(i => i != null) - .Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value)); - } - public virtual long GetRunTimeTicksForPlayState() { return RunTimeTicks ?? 0; -- cgit v1.2.3 From c0dae0fef5255f27071b8dd84e8468a3e1ad29bf Mon Sep 17 00:00:00 2001 From: Jamie Introcaso Date: Wed, 14 Sep 2022 20:39:26 -0400 Subject: Adds lyric providers to DI pipeline This is adding those lyric providers to the DI pipeline along with a super simple implementation of how to use them in the controller method. Probably should be refactored into a lyric service of some sort that would have the providers injected into it. --- Emby.Server.Implementations/ApplicationHost.cs | 3 +++ Jellyfin.Api/Controllers/UserLibraryController.cs | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199d..3e9c540e7b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,6 +46,7 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; +using Jellyfin.Api.Models.UserDtos; using Jellyfin.MediaEncoding.Hls.Playlist; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; @@ -580,6 +581,8 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index ed8a98d23f..123c5e0795 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; -using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; -using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -40,6 +37,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; + private readonly IEnumerable _lyricProviders; /// /// Initializes a new instance of the class. @@ -50,13 +48,15 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Collection of all registered interfaces. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IUserViewManager userViewManager, - IFileSystem fileSystem) + IFileSystem fileSystem, + IEnumerable lyricProviders) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -64,6 +64,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; + _lyricProviders = lyricProviders; } /// @@ -413,10 +414,14 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = ItemHelper.GetLyricData(item); - if (result is not null) + // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. + foreach (var provider in _lyricProviders) { - return Ok(result); + provider.Process(item); + if (provider.HasData) + { + return Ok(provider.Data); + } } return NotFound(); -- cgit v1.2.3 From d9be3874ba3842d5888c5cbbe583614ed990849e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:45:26 -0400 Subject: Auto stash before merge of "lyric-lrc-file-support" and "origin/lyric-lrc-file-support" --- Emby.Server.Implementations/ApplicationHost.cs | 2 + Emby.Server.Implementations/Dto/DtoService.cs | 9 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 5 +- Jellyfin.Api/Helpers/ItemHelper.cs | 106 ------------------- Jellyfin.Api/Jellyfin.Api.csproj | 1 - Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs | 34 ------ Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs | 117 --------------------- Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 ---- Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs | 81 -------------- MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 +++++ MediaBrowser.Controller/Lyrics/Lyric.cs | 18 ++++ MediaBrowser.Controller/Lyrics/LyricInfo.cs | 87 +++++++++++++++ MediaBrowser.Controller/Lyrics/LyricResponse.cs | 15 +++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 +- MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 ++++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 +++++++++++++ .../MediaBrowser.Providers.csproj | 2 + 17 files changed, 345 insertions(+), 362 deletions(-) delete mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/Lyric.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/Lyric.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricInfo.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricResponse.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3e9c540e7b..5487e5e023 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -68,6 +68,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; @@ -95,6 +96,7 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; +using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 96717cff53..bed82a4bbb 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; @@ -51,6 +52,8 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; + private readonly IEnumerable _lyricProviders; + public DtoService( ILogger logger, ILibraryManager libraryManager, @@ -60,7 +63,8 @@ namespace Emby.Server.Implementations.Dto IProviderManager providerManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, - Lazy livetvManagerFactory) + Lazy livetvManagerFactory, + IEnumerable lyricProviders) { _logger = logger; _libraryManager = libraryManager; @@ -71,6 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; + _lyricProviders = lyricProviders; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -142,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLocalLyricsFile = ItemHelper.HasLyricFile(item.Path); + dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 123c5e0795..3da78c1169 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -13,6 +13,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -414,8 +415,8 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. - foreach (var provider in _lyricProviders) + var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + if (result is not null) { provider.Process(item); if (provider.HasData) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs deleted file mode 100644 index 49bb8af8e3..0000000000 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using System.IO; -using System.Linq; -using Jellyfin.Api.Models.UserDtos; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Helpers -{ - /// - /// Item helper. - /// - public static class ItemHelper - { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Requested Item. - /// Collection of Lyrics. - internal static object? GetLyricData(BaseItem item) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? newProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (newProvider is not null) - { - newProvider.Process(item); - if (newProvider.HasData) - { - return newProvider.Data; - } - } - } - - return null; - } - - /// - /// Checks if requested item has a matching lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(string itemPath) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - // Iterate over all found lyric providers - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? foundProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (foundProvider?.FileExtensions is null) - { - continue; - } - - if (foundProvider.FileExtensions.Any()) - { - foreach (string lyricFileExtension in foundProvider.FileExtensions) - { - string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); - if (System.IO.File.Exists(lyricFilePath)) - { - return lyricFilePath; - } - } - } - } - - return null; - } - - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(string itemPath) - { - string? lyricFilePath = GetLyricFilePath(itemPath); - return !string.IsNullOrEmpty(lyricFilePath); - } - } -} diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 972387e02a..894d871383 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,6 @@ - diff --git a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs deleted file mode 100644 index 37f1f5bbe4..0000000000 --- a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.ObjectModel; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Interface ILyricsProvider. - /// - public interface ILyricsProvider - { - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - bool HasData { get; } - - /// - /// Gets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - object? Data { get; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - void Process(BaseItem item); - } -} diff --git a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs deleted file mode 100644 index 029acd6ca5..0000000000 --- a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// LRC File Lyric Provider. - /// - public class LrcLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricsProvider() - { - FileExtensions = new Collection - { - "lrc" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - public void Process(BaseItem item) - { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return; - } - - List lyricsList = new List(); - - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); - - try - { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - var metaDataField = metaDataRow.Split(":"); - - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); - - metaData.Add(metaDataFieldName, metaDataFieldValue); - } - } - catch - { - return; - } - - if (!sortedLyricData.Any()) - { - return; - } - - for (int i = 0; i < sortedLyricData.Count; i++) - { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); - } - - this.HasData = true; - if (metaData.Any()) - { - this.Data = new { MetaData = metaData, lyrics = lyricsList }; - } - else - { - this.Data = new { lyrics = lyricsList }; - } - } - } -} diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/Jellyfin.Api/Models/UserDtos/Lyric.cs deleted file mode 100644 index f83fc9839b..0000000000 --- a/Jellyfin.Api/Models/UserDtos/Lyric.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Lyric dto. - /// - public class Lyric - { - /// - /// Gets or sets the start time (ticks). - /// - public double? Start { get; set; } - - /// - /// Gets or sets the text. - /// - public string Text { get; set; } = string.Empty; - } -} diff --git a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs deleted file mode 100644 index 03cce1ffbb..0000000000 --- a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// TXT File Lyric Provider. - /// - public class TxtLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricsProvider() - { - FileExtensions = new Collection - { - "lrc", "txt" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - public void Process(BaseItem item) - { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return; - } - - List lyricsList = new List(); - - string lyricData = System.IO.File.ReadAllText(lyricFilePath); - - // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); - - if (!lyricTextLines.Any()) - { - return; - } - - foreach (string lyricLine in lyricTextLines) - { - lyricsList.Add(new Lyric { Text = lyricLine }); - } - - this.HasData = true; - this.Data = new { lyrics = lyricsList }; - } - } -} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs new file mode 100644 index 0000000000..bac32a398a --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricsProvider + { + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets the lyrics. + /// + /// The item to to process. + /// Task{LyricResponse}. + LyricResponse? GetLyrics(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs new file mode 100644 index 0000000000..d44546dd39 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -0,0 +1,18 @@ +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Lyric dto. + /// + public class Lyric + { + /// + /// Gets or sets the start time (ticks). + /// + public double? Start { get; set; } + + /// + /// Gets or sets the text. + /// + public string Text { get; set; } = string.Empty; + } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs new file mode 100644 index 0000000000..83a10701a2 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Mvc; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Item helper. + /// + public class LyricInfo + { + /// + /// Opens lyrics file, converts to a List of Lyrics, and returns it. + /// + /// Collection of all registered interfaces. + /// Requested Item. + /// Collection of Lyrics. + public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) + { + + foreach (var provider in lyricProviders) + { + var result = provider.GetLyrics(item); + if (result is not null) + { + return result; + } + } + + return new LyricResponse + { + Lyrics = new List + { + new Lyric { Start = 0, Text = "Test" } + } + }; + } + + /// + /// Checks if requested item has a matching lyric file. + /// + /// The current lyricProvider interface. + /// Path of requested item. + /// True if item has a matching lyrics file. + public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + { + if (lyricProvider.SupportedMediaTypes.Any()) + { + foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) + { + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) + { + return lyricFilePath; + } + } + } + + return null; + } + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// Collection of all registered interfaces. + /// Path of requested item. + /// True if item has a matching lyrics file; otherwise false. + public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) + { + foreach (var provider in lyricProviders) + { + if (GetLyricFilePath(provider, itemPath) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs new file mode 100644 index 0000000000..e312638ecc --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -0,0 +1,15 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Lyrics +{ + public class LyricResponse + { + public IDictionary MetaData { get; set; } + + public IEnumerable Lyrics { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b40a0210ad..2a86fded22 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } - public bool? HasLocalLyricsFile { get; set; } + public bool? HasLyrics { get; set; } public bool? HasSubtitles { get; set; } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs new file mode 100644 index 0000000000..e30d563087 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return null; + } + + if (!sortedLyricData.Any()) + { + return null; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs new file mode 100644 index 0000000000..2a5da4e4d8 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9864db9ac2..8514489f8a 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -6,6 +6,7 @@ + @@ -16,6 +17,7 @@ + -- cgit v1.2.3 From f4fd908f8d7ffcdea6acaf75928f6c2960ed6338 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:49:25 -0400 Subject: Create ILyricManager --- Emby.Server.Implementations/ApplicationHost.cs | 4 +- Emby.Server.Implementations/Dto/DtoService.cs | 8 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 16 ++- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 37 +++++++ MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 29 ++++++ MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 ----- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 50 +-------- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 119 ++++++++++++++++++++++ MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 -------------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 97 ++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 82 +++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 -------------- 12 files changed, 378 insertions(+), 274 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/ILyricManager.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricProvider.cs delete mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LyricManager.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5487e5e023..409fc04b16 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -583,8 +583,6 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); - serviceCollection.AddTransient(); - serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -603,6 +601,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -790,6 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); + Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index bed82a4bbb..6ab574c5c5 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; public DtoService( ILogger logger, @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Dto IApplicationHost appHost, IMediaSourceManager mediaSourceManager, Lazy livetvManagerFactory, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _logger = logger; _libraryManager = libraryManager; @@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); + dto.HasLyrics = _lyricManager.HasLyricFile(item); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 3da78c1169..1421ab444a 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -38,7 +38,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Collection of all registered interfaces. + /// Instance of the interface. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, @@ -57,7 +57,7 @@ namespace Jellyfin.Api.Controllers IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } /// @@ -415,14 +415,10 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + var result = _lyricManager.GetLyric(item); if (result is not null) { - provider.Process(item); - if (provider.HasData) - { - return Ok(provider.Data); - } + return Ok(result); } return NotFound(); diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs new file mode 100644 index 0000000000..4fd11b9e02 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -0,0 +1,37 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Controller.Lyrics +{ + public interface ILyricManager + { + /// + /// Adds the parts. + /// + /// The lyric providers. + void AddParts(IEnumerable lyricProviders); + + /// + /// Gets the lyrics. + /// + /// The media item. + /// Lyrics for passed item. + LyricResponse GetLyric(BaseItem item); + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// The media item. + /// True if item has a matching lyrics file; otherwise false. + bool HasLyricFile(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs new file mode 100644 index 0000000000..691fed1fd2 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricProvider + { + /// + /// Gets a value indicating the provider name. + /// + string Name { get; } + + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets the lyrics. + /// + /// The item to to process. + /// Task{LyricResponse}. + LyricResponse? GetLyrics(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs deleted file mode 100644 index bac32a398a..0000000000 --- a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; - -namespace MediaBrowser.Controller.Lyrics -{ - /// - /// Interface ILyricsProvider. - /// - public interface ILyricsProvider - { - /// - /// Gets the supported media types for this provider. - /// - /// The supported media types. - IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets the lyrics. - /// - /// The item to to process. - /// Task{LyricResponse}. - LyricResponse? GetLyrics(BaseItem item); - } -} diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 83a10701a2..d44e14237a 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -13,42 +13,15 @@ namespace MediaBrowser.Controller.Lyrics /// /// Item helper. /// - public class LyricInfo + public static class LyricInfo { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Collection of all registered interfaces. - /// Requested Item. - /// Collection of Lyrics. - public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) - { - - foreach (var provider in lyricProviders) - { - var result = provider.GetLyrics(item); - if (result is not null) - { - return result; - } - } - - return new LyricResponse - { - Lyrics = new List - { - new Lyric { Start = 0, Text = "Test" } - } - }; - } - /// /// Checks if requested item has a matching lyric file. /// /// The current lyricProvider interface. /// Path of requested item. /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) { @@ -64,24 +37,5 @@ namespace MediaBrowser.Controller.Lyrics return null; } - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Collection of all registered interfaces. - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) - { - foreach (var provider in lyricProviders) - { - if (GetLyricFilePath(provider, itemPath) is not null) - { - return true; - } - } - - return false; - } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs new file mode 100644 index 0000000000..18a85c93ac --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricProvider() + { + Name = "LrcLyricProvider"; + + SupportedMediaTypes = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return null; + } + + if (!sortedLyricData.Any()) + { + return null; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs deleted file mode 100644 index e30d563087..0000000000 --- a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; - -namespace MediaBrowser.Providers.Lyric -{ - /// - /// LRC File Lyric Provider. - /// - public class LrcLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricsList = new List(); - - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); - - try - { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - var metaDataField = metaDataRow.Split(":"); - - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); - - metaData.Add(metaDataFieldName, metaDataFieldValue); - } - } - catch - { - return null; - } - - if (!sortedLyricData.Any()) - { - return null; - } - - for (int i = 0; i < sortedLyricData.Count; i++) - { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); - } - - if (metaData.Any()) - { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs new file mode 100644 index 0000000000..48572c63e4 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -0,0 +1,97 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Extensions; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Lyric +{ + public class LyricManager : ILyricManager + { + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly ILibraryMonitor _monitor; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly ILocalizationManager _localization; + + private ILyricProvider[] _lyricProviders; + + public LyricManager( + ILogger logger, + IFileSystem fileSystem, + ILibraryMonitor monitor, + IMediaSourceManager mediaSourceManager, + ILocalizationManager localizationManager) + { + _logger = logger; + _fileSystem = fileSystem; + _monitor = monitor; + _mediaSourceManager = mediaSourceManager; + _localization = localizationManager; + } + + /// + public void AddParts(IEnumerable lyricProviders) + { + _lyricProviders = lyricProviders + .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) + .ToArray(); + } + + /// + public LyricResponse GetLyric(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + var results = provider.GetLyrics(item); + if (results is not null) + { + return results; + } + } + + return null; + } + + /// + public bool HasLyricFile(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + if (item is null) + { + continue; + } + + if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs new file mode 100644 index 0000000000..939d8708b2 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricProvider() + { + Name = "TxtLyricProvider"; + + SupportedMediaTypes = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs deleted file mode 100644 index 2a5da4e4d8..0000000000 --- a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; - -namespace MediaBrowser.Providers.Lyric -{ - /// - /// TXT File Lyric Provider. - /// - public class TxtLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc", "txt" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricsList = new List(); - - string lyricData = System.IO.File.ReadAllText(lyricFilePath); - - // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); - - if (!lyricTextLines.Any()) - { - return null; - } - - foreach (string lyricLine in lyricTextLines) - { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} -- cgit v1.2.3 From f740d1b9f00d91bfad970f56abed67d8c8c16c9c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 16 Sep 2022 20:52:40 -0400 Subject: Remove use of AddParts. Cleanup use of Lyric vs Lyrics. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +++--- Jellyfin.Server/CoreAppHost.cs | 6 ++++++ MediaBrowser.Controller/Lyrics/ILyricManager.cs | 18 ++-------------- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 4 ++-- MediaBrowser.Controller/Lyrics/Lyric.cs | 4 ++-- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 8 +++---- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 9 ++++++++ MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 24 ++++++++------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 16 +++++--------- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 26 +++++++++-------------- 11 files changed, 53 insertions(+), 70 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 409fc04b16..5edc25952d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); - Resolve().AddParts(GetExports()); + //Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1421ab444a..2cb2e93284 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -394,10 +394,10 @@ namespace Jellyfin.Api.Controllers /// Item id. /// Lyrics returned. /// Something went wrong. No Lyrics will be returned. - /// An containing the intros to play. + /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -415,7 +415,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = _lyricManager.GetLyric(item); + var result = _lyricManager.GetLyrics(item); if (result is not null) { return Ok(result); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 67e50b92d9..984711dc2d 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Activity; @@ -95,6 +96,11 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); + foreach (var type in GetExportTypes()) + { + serviceCollection.AddSingleton(typeof(ILyricProvider), type); + } + base.RegisterServices(serviceCollection); } diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index 4fd11b9e02..c0f78d177d 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,37 +1,23 @@ -#nullable disable - #pragma warning disable CS1591 -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Lyrics { public interface ILyricManager { - /// - /// Adds the parts. - /// - /// The lyric providers. - void AddParts(IEnumerable lyricProviders); - /// /// Gets the lyrics. /// /// The media item. /// Lyrics for passed item. - LyricResponse GetLyric(BaseItem item); + LyricResponse GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. /// /// The media item. - /// True if item has a matching lyrics file; otherwise false. + /// True if item has a matching lyric file; otherwise false. bool HasLyricFile(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 691fed1fd2..5e677ab26f 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -22,8 +22,8 @@ namespace MediaBrowser.Controller.Lyrics /// /// Gets the lyrics. /// - /// The item to to process. - /// Task{LyricResponse}. + /// The media item. + /// If found, returns lyrics for passed item; otherwise, null. LyricResponse? GetLyrics(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs index d44546dd39..56a0a8a72d 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -1,12 +1,12 @@ namespace MediaBrowser.Controller.Lyrics { /// - /// Lyric dto. + /// Lyric model. /// public class Lyric { /// - /// Gets or sets the start time (ticks). + /// Gets or sets the start time in ticks. /// public double? Start { get; set; } diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index d44e14237a..018f296b1a 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -11,16 +11,16 @@ using Microsoft.AspNetCore.Mvc; namespace MediaBrowser.Controller.Lyrics { /// - /// Item helper. + /// Lyric helper methods. /// public static class LyricInfo { /// - /// Checks if requested item has a matching lyric file. + /// Gets matching lyric file for a requested item. /// - /// The current lyricProvider interface. + /// The lyricProvider interface to use. /// Path of requested item. - /// True if item has a matching lyrics file. + /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index e312638ecc..498eb873c7 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -6,10 +6,19 @@ using System.Collections.Generic; namespace MediaBrowser.Controller.Lyrics { + /// + /// LyricResponse model. + /// public class LyricResponse { + /// + /// Gets or sets MetaData. + /// public IDictionary MetaData { get; set; } + /// + /// Gets or sets Lyrics. + /// public IEnumerable Lyrics { get; set; } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 18a85c93ac..10db10ac6f 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -14,7 +14,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// LRC File Lyric Provider. + /// LRC Lyric Provider. /// public class LrcLyricProvider : ILyricProvider { @@ -37,21 +37,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -61,9 +55,9 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); - + List lyricList = new List(); List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); @@ -105,15 +99,15 @@ namespace MediaBrowser.Providers.Lyric { var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + lyricList.Add(new Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); } if (metaData.Any()) { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + return new LyricResponse { MetaData = metaData, Lyrics = lyricList }; } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 48572c63e4..698da46867 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -36,32 +36,26 @@ namespace MediaBrowser.Providers.Lyric private readonly IMediaSourceManager _mediaSourceManager; private readonly ILocalizationManager _localization; - private ILyricProvider[] _lyricProviders; + private IEnumerable _lyricProviders; public LyricManager( ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, - ILocalizationManager localizationManager) + ILocalizationManager localizationManager, + IEnumerable lyricProviders) { _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _mediaSourceManager = mediaSourceManager; _localization = localizationManager; + _lyricProviders = lyricProviders; } /// - public void AddParts(IEnumerable lyricProviders) - { - _lyricProviders = lyricProviders - .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) - .ToArray(); - } - - /// - public LyricResponse GetLyric(BaseItem item) + public LyricResponse GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 939d8708b2..aa222ed97b 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -11,7 +11,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// TXT File Lyric Provider. + /// TXT Lyric Provider. /// public class TxtLyricProvider : ILyricProvider { @@ -34,21 +34,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a ; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -58,25 +52,25 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); + List lyricList = new List(); string lyricData = System.IO.File.ReadAllText(lyricFilePath); // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + char[] newLineDelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLineDelims, StringSplitOptions.RemoveEmptyEntries); if (!lyricTextLines.Any()) { return null; } - foreach (string lyricLine in lyricTextLines) + foreach (string lyricTextLine in lyricTextLines) { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + lyricList.Add(new Controller.Lyrics.Lyric { Text = lyricTextLine }); } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } -- cgit v1.2.3 From 8912f618f59c1e798e406b8ed7fed4504e2c2de3 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 16 Sep 2022 21:11:28 -0400 Subject: Change API GetLyrics return type --- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 2cb2e93284..df91a8efc7 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public ActionResult GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); -- cgit v1.2.3 From a50bdb47709be0412b3abb2729f8c657b5d0c779 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 22 Sep 2022 08:13:53 -0400 Subject: Use async functions --- Jellyfin.Api/Controllers/UserLibraryController.cs | 4 ++-- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 5 +++-- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 5 +++-- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 23 +++++++++++++++++------ MediaBrowser.Providers/Lyric/LyricManager.cs | 5 +++-- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 5 +++-- 6 files changed, 31 insertions(+), 16 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index df91a8efc7..682a6e832d 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public async Task> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -415,7 +415,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = _lyricManager.GetLyrics(item); + var result = await _lyricManager.GetLyrics(item).ConfigureAwait(false); if (result is not null) { return Ok(result); diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index 5920bcc628..bb93e1e4c6 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Lyrics; @@ -11,8 +12,8 @@ public interface ILyricManager /// Gets the lyrics. /// /// The media item. - /// Lyrics for passed item. - LyricResponse? GetLyrics(BaseItem item); + /// A task representing found lyrics the passed item. + Task GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index c5b6252267..2a04c61520 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Resolvers; @@ -30,6 +31,6 @@ public interface ILyricProvider /// Gets the lyrics. /// /// The media item. - /// If found, returns lyrics for passed item; otherwise, null. - LyricResponse? GetLyrics(BaseItem item); + /// A task representing found lyrics. + Task GetLyrics(BaseItem item); } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 1dbe5958e8..d06db3afc6 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Threading.Tasks; using LrcParser.Model; using LrcParser.Parser; using MediaBrowser.Controller.Entities; @@ -50,7 +51,7 @@ public class LrcLyricProvider : ILyricProvider /// /// The item to to process. /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { string? lyricFilePath = this.GetLyricFilePath(item.Path); @@ -60,7 +61,7 @@ public class LrcLyricProvider : ILyricProvider } var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); - string lrcFileContent = File.ReadAllText(lyricFilePath); + string lrcFileContent = await Task.FromResult(File.ReadAllText(lyricFilePath)).ConfigureAwait(false); Song lyricData; @@ -94,15 +95,15 @@ public class LrcLyricProvider : ILyricProvider // Remove square bracket before field name, and after field value // Example 1: [au: 1hitsong] // Example 2: [ar: Calabrese] - var metaDataFieldNameSpan = metaDataRow.AsSpan(1, index - 1).Trim(); - var metaDataFieldValueSpan = metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim(); + var metaDataFieldName = GetMetadataFieldName(metaDataRow, index); + var metaDataFieldValue = GetMetadataValue(metaDataRow, index); - if (metaDataFieldValueSpan.IsEmpty || metaDataFieldValueSpan.IsEmpty) + if (string.IsNullOrEmpty(metaDataFieldName) || string.IsNullOrEmpty(metaDataFieldValue)) { continue; } - fileMetaData[metaDataFieldNameSpan.ToString()] = metaDataFieldValueSpan.ToString(); + fileMetaData[metaDataFieldName.ToString()] = metaDataFieldValue.ToString(); } if (sortedLyricData.Count == 0) @@ -197,4 +198,14 @@ public class LrcLyricProvider : ILyricProvider return lyricMetadata; } + + private static string GetMetadataFieldName(string metaDataRow, int index) + { + return metaDataRow.AsSpan(1, index - 1).Trim().ToString(); + } + + private static string GetMetadataValue(string metaDataRow, int index) + { + return metaDataRow.AsSpan(index + 1, metaDataRow.Length - index - 2).Trim().ToString(); + } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 336b324a7f..f9547e0f05 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; @@ -22,11 +23,11 @@ public class LyricManager : ILyricManager } /// - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { - var results = provider.GetLyrics(item); + var results = await provider.GetLyrics(item).ConfigureAwait(false); if (results is not null) { return results; diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index bce8810545..9df4ec83e7 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Resolvers; @@ -29,7 +30,7 @@ public class TxtLyricProvider : ILyricProvider /// /// The item to to process. /// If provider can determine lyrics, returns a ; otherwise, null. - public LyricResponse? GetLyrics(BaseItem item) + public async Task GetLyrics(BaseItem item) { string? lyricFilePath = this.GetLyricFilePath(item.Path); @@ -38,7 +39,7 @@ public class TxtLyricProvider : ILyricProvider return null; } - string[] lyricTextLines = File.ReadAllLines(lyricFilePath); + string[] lyricTextLines = await Task.FromResult(File.ReadAllLines(lyricFilePath)).ConfigureAwait(false); if (lyricTextLines.Length == 0) { -- cgit v1.2.3 From 1e72ba4c09eddf92c5a30f225505825b93336b6f Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Fri, 23 Sep 2022 23:09:35 -0400 Subject: Backport pull request #8214 from jellyfin/release-10.8.z Make userId truly optional in UniversalAudioController Original-merge: af877063795469ac3d94d356b2787e296606f571 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- Jellyfin.Api/Controllers/UniversalAudioController.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 43b8e2414d..c463fb08aa 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -111,7 +111,13 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableRedirection = true) { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - (await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).DeviceId = deviceId; + var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); + authorizationInfo.DeviceId = deviceId; + + if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) + { + userId = authorizationInfo.UserId; + } var authInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); -- cgit v1.2.3 From e8893f3d416c7eab96fef2fb8330fc20d29efafc Mon Sep 17 00:00:00 2001 From: LogicalPhallacy <44458166+LogicalPhallacy@users.noreply.github.com> Date: Fri, 23 Sep 2022 23:09:38 -0400 Subject: Backport pull request #8399 from jellyfin/release-10.8.z Respect visibility for people items (rebased) Original-merge: e6124bc154c9f95fdd491f309623def6b3df0171 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- Emby.Server.Implementations/Dto/DtoService.cs | 8 ++++++-- Emby.Server.Implementations/Library/LibraryManager.cs | 10 ++++++++-- Jellyfin.Api/Controllers/PersonsController.cs | 5 ++++- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 09ba368514..3d2b8f7f63 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.People)) { - AttachPeople(dto, item); + AttachPeople(dto, item, user); } if (options.ContainsField(ItemFields.PrimaryImageAspectRatio)) @@ -503,7 +503,8 @@ namespace Emby.Server.Implementations.Dto /// /// The dto. /// The item. - private void AttachPeople(BaseItemDto dto, BaseItem item) + /// The requesting user. + private void AttachPeople(BaseItemDto dto, BaseItem item, User user = null) { // Ordering by person type to ensure actors and artists are at the front. // This is taking advantage of the fact that they both begin with A @@ -560,6 +561,9 @@ namespace Emby.Server.Implementations.Dto return null; } }).Where(i => i != null) + .Where(i => user == null ? + true : + i.IsVisible(user)) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .Select(x => x.First()) .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6796845525..250e7619fb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2766,7 +2766,8 @@ namespace Emby.Server.Implementations.Library public List GetPeopleItems(InternalPeopleQuery query) { - return _itemRepository.GetPeopleNames(query).Select(i => + return _itemRepository.GetPeopleNames(query) + .Select(i => { try { @@ -2777,7 +2778,12 @@ namespace Emby.Server.Implementations.Library _logger.LogError(ex, "Error getting person"); return null; } - }).Where(i => i != null).ToList(); + }) + .Where(i => i != null) + .Where(i => query.User == null ? + true : + i.IsVisible(query.User)) + .ToList(); } public List GetPeopleNames(InternalPeopleQuery query) diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index be4b9eded5..33f1aea394 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -98,7 +98,10 @@ namespace Jellyfin.Api.Controllers Limit = limit ?? 0 }); - return new QueryResult(peopleItems.Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)).ToArray()); + return new QueryResult( + peopleItems + .Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)) + .ToArray()); } /// -- cgit v1.2.3 From 5dc30c6a6d8af9a758fd730c9da69c13847c21c3 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 6 Oct 2022 13:57:47 +0200 Subject: fix: use HttpContext and ClaimsPrincipal instead of IAuthorizationContext --- Emby.Server.Implementations/ApplicationHost.cs | 2 - .../HttpServer/Security/SessionContext.cs | 59 --------------- .../Session/SessionWebSocketListener.cs | 16 ++-- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 9 ++- .../SyncPlayAccessPolicy/SyncPlayAccessHandler.cs | 9 ++- Jellyfin.Api/Controllers/ArtistsController.cs | 6 +- Jellyfin.Api/Controllers/ClientLogController.cs | 7 +- Jellyfin.Api/Controllers/CollectionController.cs | 11 +-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 14 +--- Jellyfin.Api/Controllers/GenresController.cs | 4 +- Jellyfin.Api/Controllers/ImageController.cs | 19 ++--- Jellyfin.Api/Controllers/InstantMixController.cs | 14 ++-- Jellyfin.Api/Controllers/ItemsController.cs | 20 ++--- Jellyfin.Api/Controllers/LibraryController.cs | 37 ++++----- Jellyfin.Api/Controllers/LiveTvController.cs | 39 ++++++---- Jellyfin.Api/Controllers/MediaInfoController.cs | 16 ++-- Jellyfin.Api/Controllers/MoviesController.cs | 2 +- Jellyfin.Api/Controllers/MusicGenresController.cs | 4 +- Jellyfin.Api/Controllers/PersonsController.cs | 4 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 2 +- Jellyfin.Api/Controllers/PlaystateController.cs | 28 +++---- Jellyfin.Api/Controllers/QuickConnectController.cs | 7 +- Jellyfin.Api/Controllers/SessionController.cs | 33 ++++---- Jellyfin.Api/Controllers/StudiosController.cs | 4 +- Jellyfin.Api/Controllers/SubtitleController.cs | 8 +- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- Jellyfin.Api/Controllers/SyncPlayController.cs | 54 ++++++------- Jellyfin.Api/Controllers/TvShowsController.cs | 8 +- .../Controllers/UniversalAudioController.cs | 14 +--- Jellyfin.Api/Controllers/UserController.cs | 23 +++--- Jellyfin.Api/Controllers/UserLibraryController.cs | 12 +-- Jellyfin.Api/Controllers/UserViewsController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 9 +-- Jellyfin.Api/Controllers/YearsController.cs | 4 +- .../Extensions/ClaimsPrincipalExtensions.cs | 81 ++++++++++++++++++++ Jellyfin.Api/Extensions/DtoExtensions.cs | 8 +- Jellyfin.Api/Helpers/AudioHelper.cs | 8 +- Jellyfin.Api/Helpers/ClaimHelpers.cs | 88 ---------------------- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 11 +-- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 33 ++++---- Jellyfin.Api/Helpers/RequestHelpers.cs | 48 +++++++----- Jellyfin.Api/Helpers/StreamingHelpers.cs | 15 ++-- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 17 +++-- MediaBrowser.Controller/Net/ISessionContext.cs | 20 ----- 44 files changed, 335 insertions(+), 496 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpServer/Security/SessionContext.cs create mode 100644 Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs delete mode 100644 Jellyfin.Api/Helpers/ClaimHelpers.cs delete mode 100644 MediaBrowser.Controller/Net/ISessionContext.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199d..9a9de1059e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -630,8 +630,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddScoped(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs deleted file mode 100644 index e0d20e2100..0000000000 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ /dev/null @@ -1,59 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using Microsoft.AspNetCore.Http; - -namespace Emby.Server.Implementations.HttpServer.Security -{ - public class SessionContext : ISessionContext - { - private readonly IUserManager _userManager; - private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authContext; - - public SessionContext(IUserManager userManager, IAuthorizationContext authContext, ISessionManager sessionManager) - { - _userManager = userManager; - _authContext = authContext; - _sessionManager = sessionManager; - } - - public async Task GetSession(HttpContext requestContext) - { - var authorization = await _authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - - var user = authorization.User; - return await _sessionManager.LogSessionActivity( - authorization.Client, - authorization.Version, - authorization.DeviceId, - authorization.Device, - requestContext.GetNormalizedRemoteIp().ToString(), - user).ConfigureAwait(false); - } - - public Task GetSession(object requestContext) - { - return GetSession((HttpContext)requestContext); - } - - public async Task GetUser(HttpContext requestContext) - { - var session = await GetSession(requestContext).ConfigureAwait(false); - - return session.UserId.Equals(default) - ? null - : _userManager.GetUserById(session.UserId); - } - - public Task GetUser(object requestContext) - { - return GetUser(((HttpRequest)requestContext).HttpContext); - } - } -} diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 35fd5caae7..c654828b1e 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; @@ -53,7 +54,6 @@ namespace Emby.Server.Implementations.Session private readonly ISessionManager _sessionManager; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IAuthorizationContext _authorizationContext; /// /// The KeepAlive cancellation token. @@ -66,17 +66,14 @@ namespace Emby.Server.Implementations.Session /// The logger. /// The session manager. /// The logger factory. - /// The authorization context. public SessionWebSocketListener( ILogger logger, ISessionManager sessionManager, - ILoggerFactory loggerFactory, - IAuthorizationContext authorizationContext) + ILoggerFactory loggerFactory) { _logger = logger; _sessionManager = sessionManager; _loggerFactory = loggerFactory; - _authorizationContext = authorizationContext; } /// @@ -110,21 +107,18 @@ namespace Emby.Server.Implementations.Session private async Task GetSession(HttpContext httpContext, string remoteEndpoint) { - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(httpContext) - .ConfigureAwait(false); - - if (!authorizationInfo.IsAuthenticated) + if (!httpContext.User.Identity?.IsAuthenticated ?? false) { return null; } - var deviceId = authorizationInfo.DeviceId; + var deviceId = httpContext.User.GetDeviceId(); if (httpContext.Request.Query.TryGetValue("deviceId", out var queryDeviceId)) { deviceId = queryDeviceId; } - return await _sessionManager.GetSessionByAuthenticationToken(authorizationInfo.Token, deviceId, remoteEndpoint) + return await _sessionManager.GetSessionByAuthenticationToken(httpContext.User.GetToken(), deviceId, remoteEndpoint) .ConfigureAwait(false); } diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index 13d3257dff..92ee1dd59a 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; @@ -51,21 +52,21 @@ namespace Jellyfin.Api.Auth bool requiredDownloadPermission = false) { // ApiKey is currently global admin, always allow. - var isApiKey = ClaimHelpers.GetIsApiKey(claimsPrincipal); + var isApiKey = claimsPrincipal.GetIsApiKey(); if (isApiKey) { return true; } // Ensure claim has userId. - var userId = ClaimHelpers.GetUserId(claimsPrincipal); - if (!userId.HasValue) + var userId = claimsPrincipal.GetUserId(); + if (userId.Equals(default)) { return false; } // Ensure userId links to a valid user. - var user = _userManager.GetUserById(userId.Value); + var user = _userManager.GetUserById(userId); if (user == null) { return false; diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs index e6c04eb082..cdd7d8a52b 100644 --- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs +++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Net; @@ -44,14 +45,14 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy return Task.CompletedTask; } - var userId = ClaimHelpers.GetUserId(context.User); - var user = _userManager.GetUserById(userId!.Value); + var userId = context.User.GetUserId(); + var user = _userManager.GetUserById(userId); if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess) { if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups || user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups - || _syncPlayManager.IsUserActive(userId.Value)) + || _syncPlayManager.IsUserActive(userId)) { context.Succeed(requirement); } @@ -85,7 +86,7 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy } else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup) { - if (_syncPlayManager.IsUserActive(userId.Value)) + if (_syncPlayManager.IsUserActive(userId)) { context.Succeed(requirement); } diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 44796bcc4c..c059cb198a 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = null; @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = null; @@ -463,7 +463,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var item = _libraryManager.GetArtist(name, dtoOptions); diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 98fd224307..ed073a687e 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; @@ -69,10 +70,10 @@ namespace Jellyfin.Api.Controllers private (string ClientName, string ClientVersion) GetRequestInformation() { - var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client"; - var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User) + var clientName = HttpContext.User.GetClient() ?? "unknown-client"; + var clientVersion = HttpContext.User.GetIsApiKey() ? "apikey" - : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version"; + : HttpContext.User.GetVersion() ?? "unknown-version"; return (clientName, clientVersion); } diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 8a98d856c4..effc9ed7aa 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -6,7 +6,6 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Collections; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -23,22 +22,18 @@ namespace Jellyfin.Api.Controllers { private readonly ICollectionManager _collectionManager; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. /// /// Instance of interface. /// Instance of interface. - /// Instance of interface. public CollectionController( ICollectionManager collectionManager, - IDtoService dtoService, - IAuthorizationContext authContext) + IDtoService dtoService) { _collectionManager = collectionManager; _dtoService = dtoService; - _authContext = authContext; } /// @@ -58,7 +53,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] Guid? parentId, [FromQuery] bool isLocked = false) { - var userId = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).UserId; + var userId = User.GetUserId(); var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions { @@ -69,7 +64,7 @@ namespace Jellyfin.Api.Controllers UserIds = new[] { userId } }).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var dto = _dtoService.GetBaseItemDto(item, dtoOptions); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 3ed80f6621..0f4d3c1ebc 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -20,7 +20,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.IO; @@ -46,7 +45,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -65,7 +63,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -80,7 +77,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -95,7 +91,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _userManager = userManager; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -287,8 +282,7 @@ namespace Jellyfin.Api.Controllers var cancellationToken = cancellationTokenSource.Token; var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -1393,8 +1387,7 @@ namespace Jellyfin.Api.Controllers { using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -1434,8 +1427,7 @@ namespace Jellyfin.Api.Controllers var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index e28a50750a..611643bd8a 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -157,7 +157,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); Genre? item; if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 6c7842c7b9..f092bd8820 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -17,7 +17,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Branding; using MediaBrowser.Model.Drawing; @@ -44,11 +43,9 @@ namespace Jellyfin.Api.Controllers private readonly IProviderManager _providerManager; private readonly IImageProcessor _imageProcessor; private readonly IFileSystem _fileSystem; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IApplicationPaths _appPaths; - private readonly IImageEncoder _imageEncoder; /// /// Initializes a new instance of the class. @@ -58,33 +55,27 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public ImageController( IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IImageProcessor imageProcessor, IFileSystem fileSystem, - IAuthorizationContext authContext, ILogger logger, IServerConfigurationManager serverConfigurationManager, - IApplicationPaths appPaths, - IImageEncoder imageEncoder) + IApplicationPaths appPaths) { _userManager = userManager; _libraryManager = libraryManager; _providerManager = providerManager; _imageProcessor = imageProcessor; _fileSystem = fileSystem; - _authContext = authContext; _logger = logger; _serverConfigurationManager = serverConfigurationManager; _appPaths = appPaths; - _imageEncoder = imageEncoder; } /// @@ -108,7 +99,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromQuery] int? index = null) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } @@ -155,7 +146,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromRoute] int index) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } @@ -201,7 +192,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromQuery] int? index = null) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } @@ -245,7 +236,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromRoute] int index) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 9abea5938f..2e0d3cb99e 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(album, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -151,7 +151,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(playlist, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -186,7 +186,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromGenres(new[] { name }, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -222,7 +222,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -258,7 +258,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -331,7 +331,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 4d09070dbb..3c7c375d4e 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -10,7 +10,6 @@ using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -34,7 +33,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; @@ -45,7 +43,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public ItemsController( @@ -53,7 +50,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, - IAuthorizationContext authContext, ILogger logger, ISessionManager sessionManager) { @@ -61,7 +57,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _localization = localization; _dtoService = dtoService; - _authContext = authContext; _logger = logger; _sessionManager = sessionManager; } @@ -244,21 +239,20 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - + var isApiKey = User.GetIsApiKey(); // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method - var user = !auth.IsApiKey && userId.HasValue && !userId.Value.Equals(default) + var user = !isApiKey && userId.HasValue && !userId.Value.Equals(default) ? _userManager.GetUserById(userId.Value) : null; // beyond this point, we're either using an api key or we have a valid user - if (!auth.IsApiKey && user is null) + if (!isApiKey && user is null) { return BadRequest("userId is required"); } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); if (includeItemTypes.Length == 1 @@ -288,12 +282,12 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { BaseItemKind.Playlist }; } - var enabledChannels = auth.IsApiKey + var enabledChannels = isApiKey ? Array.Empty() : user!.GetPreferenceValues(PreferenceKind.EnabledChannels); // api keys are always enabled for all folders - bool isInEnabledFolder = auth.IsApiKey + bool isInEnabledFolder = isApiKey || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 @@ -850,7 +844,7 @@ namespace Jellyfin.Api.Controllers var user = _userManager.GetUserById(userId); var parentIdGuid = parentId ?? Guid.Empty; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var ancestorIds = Array.Empty(); diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 4cc17dd0fc..e9492a6a47 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -24,7 +24,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; @@ -50,7 +49,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; private readonly IActivityManager _activityManager; private readonly ILocalizationManager _localization; private readonly ILibraryMonitor _libraryMonitor; @@ -64,7 +62,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -75,7 +72,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, - IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILibraryMonitor libraryMonitor, @@ -86,7 +82,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _userManager = userManager; _dtoService = dtoService; - _authContext = authContext; _activityManager = activityManager; _localization = localization; _libraryMonitor = libraryMonitor; @@ -184,7 +179,7 @@ namespace Jellyfin.Api.Controllers item = parent; } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var items = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); @@ -250,7 +245,7 @@ namespace Jellyfin.Api.Controllers item = parent; } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var items = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); @@ -331,11 +326,10 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task DeleteItem(Guid itemId) + public ActionResult DeleteItem(Guid itemId) { var item = _libraryManager.GetItemById(itemId); - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (!item.CanDelete(user)) { @@ -361,7 +355,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids) + public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids) { if (ids.Length == 0) { @@ -371,8 +365,7 @@ namespace Jellyfin.Api.Controllers foreach (var i in ids) { var item = _libraryManager.GetItemById(i); - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (!item.CanDelete(user)) { @@ -453,7 +446,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); BaseItem? parent = item.GetParent(); while (parent != null) @@ -505,7 +498,7 @@ namespace Jellyfin.Api.Controllers items = items.Where(i => i.IsHidden == val).ToList(); } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions); return new QueryResult(resultArray); } @@ -622,9 +615,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (user != null) { @@ -643,7 +634,7 @@ namespace Jellyfin.Api.Controllers if (user != null) { - await LogDownloadAsync(item, user, auth).ConfigureAwait(false); + await LogDownloadAsync(item, user).ConfigureAwait(false); } var path = item.Path; @@ -704,7 +695,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request); + .AddClientFields(User); var program = item as IHasProgramAttributes; bool? isMovie = item is Movie || (program != null && program.IsMovie) || item is Trailer; @@ -892,16 +883,16 @@ namespace Jellyfin.Api.Controllers : item; } - private async Task LogDownloadAsync(BaseItem item, User user, AuthorizationInfo auth) + private async Task LogDownloadAsync(BaseItem item, User user) { try { await _activityManager.CreateAsync(new ActivityLog( string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name), "UserDownloadingContent", - auth.UserId) + User.GetUserId()) { - ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device), + ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), User.GetClient(), User.GetDevice()), }).ConfigureAwait(false); } catch diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 05340099bf..394df0f58b 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -17,6 +17,7 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LiveTvDtos; using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -24,6 +25,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -45,10 +47,10 @@ namespace Jellyfin.Api.Controllers private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; - private readonly ISessionContext _sessionContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IConfigurationManager _configurationManager; private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ISessionManager _sessionManager; /// /// Initializes a new instance of the class. @@ -58,30 +60,30 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the class. + /// Instance of the interface. public LiveTvController( ILiveTvManager liveTvManager, IUserManager userManager, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IDtoService dtoService, - ISessionContext sessionContext, IMediaSourceManager mediaSourceManager, IConfigurationManager configurationManager, - TranscodingJobHelper transcodingJobHelper) + TranscodingJobHelper transcodingJobHelper, + ISessionManager sessionManager) { _liveTvManager = liveTvManager; _userManager = userManager; _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _dtoService = dtoService; - _sessionContext = sessionContext; _mediaSourceManager = mediaSourceManager; _configurationManager = configurationManager; _transcodingJobHelper = transcodingJobHelper; + _sessionManager = sessionManager; } /// @@ -154,7 +156,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool addCurrentProgram = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var channelResult = _liveTvManager.GetInternalChannels( @@ -219,7 +221,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(channelId); var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -272,7 +274,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return _liveTvManager.GetRecordings( @@ -410,7 +412,7 @@ namespace Jellyfin.Api.Controllers var item = recordingId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -599,7 +601,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -653,7 +655,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = body.Fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -719,7 +721,7 @@ namespace Jellyfin.Api.Controllers }; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -1210,9 +1212,16 @@ namespace Jellyfin.Api.Controllers private async Task AssertUserCanManageLiveTv() { - var user = await _sessionContext.GetUser(Request).ConfigureAwait(false); - - if (user == null) + var user = _userManager.GetUserById(User.GetUserId()); + var session = await _sessionManager.LogSessionActivity( + User.GetClient(), + User.GetVersion(), + User.GetDeviceId(), + User.GetDevice(), + HttpContext.GetNormalizedRemoteIp().ToString(), + user).ConfigureAwait(false); + + if (session.UserId.Equals(default)) { throw new SecurityException("Anonymous live tv management is not allowed."); } diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index d2852ed011..c111e92186 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -6,12 +6,12 @@ using System.Net.Mime; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.MediaInfoDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -31,7 +31,6 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly MediaInfoHelper _mediaInfoHelper; @@ -41,21 +40,18 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the . public MediaInfoController( IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, - IAuthorizationContext authContext, ILogger logger, MediaInfoHelper mediaInfoHelper) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; _libraryManager = libraryManager; - _authContext = authContext; _logger = logger; _mediaInfoHelper = mediaInfoHelper; } @@ -122,14 +118,12 @@ namespace Jellyfin.Api.Controllers [FromQuery, ParameterObsolete] bool? allowAudioStreamCopy, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var profile = playbackInfoDto?.DeviceProfile; _logger.LogDebug("GetPostedPlaybackInfo profile: {@Profile}", profile); if (profile == null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + var caps = _deviceManager.GetCapabilities(User.GetDeviceId()); if (caps != null) { profile = caps.DeviceProfile; @@ -176,7 +170,7 @@ namespace Jellyfin.Api.Controllers item, mediaSource, profile, - authInfo, + User, maxStreamingBitrate ?? profile.MaxStreamingBitrate, startTimeTicks ?? 0, mediaSourceId ?? string.Empty, @@ -203,7 +197,7 @@ namespace Jellyfin.Api.Controllers if (mediaSource != null && mediaSource.RequiresOpening && string.IsNullOrWhiteSpace(mediaSource.LiveStreamId)) { var openStreamResult = await _mediaInfoHelper.OpenMediaSource( - Request, + HttpContext, new LiveStreamRequest { AudioStreamIndex = audioStreamIndex, @@ -276,7 +270,7 @@ namespace Jellyfin.Api.Controllers EnableDirectStream = enableDirectStream ?? openLiveStreamDto?.EnableDirectStream ?? true, DirectPlayProtocols = openLiveStreamDto?.DirectPlayProtocols ?? new[] { MediaProtocol.Http } }; - return await _mediaInfoHelper.OpenMediaSource(Request, request).ConfigureAwait(false); + return await _mediaInfoHelper.OpenMediaSource(HttpContext, request).ConfigureAwait(false); } /// diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 4669447049..8195fc7609 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request); + .AddClientFields(User); var categories = new List(); diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 0499b29857..f4fb5f44ab 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); MusicGenre? item; diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index 33f1aea394..42be969b29 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? enableImages = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -119,7 +119,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); var item = _libraryManager.GetPerson(name); if (item == null) diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index ad85f2fb24..fb045f891e 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -200,7 +200,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 6dee1c2192..0dd4bf8035 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -3,11 +3,11 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; @@ -29,7 +29,6 @@ namespace Jellyfin.Api.Controllers private readonly IUserDataManager _userDataRepository; private readonly ILibraryManager _libraryManager; private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly TranscodingJobHelper _transcodingJobHelper; @@ -40,7 +39,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Th singleton. public PlaystateController( @@ -48,7 +46,6 @@ namespace Jellyfin.Api.Controllers IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, - IAuthorizationContext authContext, ILoggerFactory loggerFactory, TranscodingJobHelper transcodingJobHelper) { @@ -56,7 +53,6 @@ namespace Jellyfin.Api.Controllers _userDataRepository = userDataRepository; _libraryManager = libraryManager; _sessionManager = sessionManager; - _authContext = authContext; _logger = loggerFactory.CreateLogger(); _transcodingJobHelper = transcodingJobHelper; @@ -78,7 +74,7 @@ namespace Jellyfin.Api.Controllers [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed) { var user = _userManager.GetUserById(userId); - var session = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var dto = UpdatePlayedStatus(user, itemId, true, datePlayed); foreach (var additionalUserInfo in session.AdditionalUsers) { @@ -101,7 +97,7 @@ namespace Jellyfin.Api.Controllers public async Task> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); - var session = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var dto = UpdatePlayedStatus(user, itemId, false, null); foreach (var additionalUserInfo in session.AdditionalUsers) { @@ -123,7 +119,7 @@ namespace Jellyfin.Api.Controllers public async Task ReportPlaybackStart([FromBody] PlaybackStartInfo playbackStartInfo) { playbackStartInfo.PlayMethod = ValidatePlayMethod(playbackStartInfo.PlayMethod, playbackStartInfo.PlaySessionId); - playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStart(playbackStartInfo).ConfigureAwait(false); return NoContent(); } @@ -139,7 +135,7 @@ namespace Jellyfin.Api.Controllers public async Task ReportPlaybackProgress([FromBody] PlaybackProgressInfo playbackProgressInfo) { playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -171,11 +167,10 @@ namespace Jellyfin.Api.Controllers _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - await _transcodingJobHelper.KillTranscodingJobs(authInfo.DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } @@ -221,7 +216,7 @@ namespace Jellyfin.Api.Controllers }; playbackStartInfo.PlayMethod = ValidatePlayMethod(playbackStartInfo.PlayMethod, playbackStartInfo.PlaySessionId); - playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStart(playbackStartInfo).ConfigureAwait(false); return NoContent(); } @@ -279,7 +274,7 @@ namespace Jellyfin.Api.Controllers }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -321,11 +316,10 @@ namespace Jellyfin.Api.Controllers _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - await _transcodingJobHelper.KillTranscodingJobs(authInfo.DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 1df26355f0..77d88475ff 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; @@ -104,15 +105,15 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> AuthorizeQuickConnect([FromQuery, Required] string code) { - var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); - if (!userId.HasValue) + var userId = User.GetUserId(); + if (userId.Equals(default)) { return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id"); } try { - return await _quickConnect.AuthorizeRequest(userId.Value, code).ConfigureAwait(false); + return await _quickConnect.AuthorizeRequest(userId, code).ConfigureAwait(false); } catch (AuthenticationException) { diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 860bccb9bd..28415555ed 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -5,13 +5,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.SessionDtos; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; @@ -29,7 +29,6 @@ namespace Jellyfin.Api.Controllers { private readonly ISessionManager _sessionManager; private readonly IUserManager _userManager; - private readonly IAuthorizationContext _authContext; private readonly IDeviceManager _deviceManager; /// @@ -37,17 +36,14 @@ namespace Jellyfin.Api.Controllers /// /// Instance of interface. /// Instance of interface. - /// Instance of interface. /// Instance of interface. public SessionController( ISessionManager sessionManager, IUserManager userManager, - IAuthorizationContext authContext, IDeviceManager deviceManager) { _sessionManager = sessionManager; _userManager = userManager; - _authContext = authContext; _deviceManager = deviceManager; } @@ -139,7 +135,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendBrowseCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, command, CancellationToken.None) @@ -186,7 +182,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendPlayCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, playRequest, CancellationToken.None) @@ -214,7 +210,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? controllingUserId) { await _sessionManager.SendPlaystateCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, new PlaystateRequest() { @@ -242,14 +238,14 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromRoute, Required] GeneralCommandType command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var generalCommand = new GeneralCommand { Name = command, ControllingUserId = currentSession.UserId }; - await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None); + await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -268,7 +264,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromRoute, Required] GeneralCommandType command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var generalCommand = new GeneralCommand { @@ -296,8 +292,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromBody, Required] GeneralCommand command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request) - .ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); if (command == null) { @@ -336,7 +331,7 @@ namespace Jellyfin.Api.Controllers } await _sessionManager.SendMessageCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, command, CancellationToken.None) @@ -405,7 +400,7 @@ namespace Jellyfin.Api.Controllers { if (string.IsNullOrWhiteSpace(id)) { - id = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); } _sessionManager.ReportCapabilities(id, new ClientCapabilities @@ -435,7 +430,7 @@ namespace Jellyfin.Api.Controllers { if (string.IsNullOrWhiteSpace(id)) { - id = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); } _sessionManager.ReportCapabilities(id, capabilities.ToClientCapabilities()); @@ -457,7 +452,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? sessionId, [FromQuery, Required] string? itemId) { - string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); _sessionManager.ReportNowViewingItem(session, itemId); return NoContent(); @@ -473,9 +468,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ReportSessionEnded() { - AuthorizationInfo auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - - await _sessionManager.Logout(auth.Token).ConfigureAwait(false); + await _sessionManager.Logout(User.GetToken()).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 053c7baaa1..1288fb5124 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var item = _libraryManager.GetStudio(name); if (userId.HasValue && !userId.Equals(default)) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 16acedcf35..1258a9876b 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -11,13 +11,13 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.SubtitleDtos; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; @@ -45,7 +45,6 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; /// @@ -58,7 +57,6 @@ namespace Jellyfin.Api.Controllers /// Instance of interface. /// Instance of interface. /// Instance of interface. - /// Instance of interface. /// Instance of interface. public SubtitleController( IServerConfigurationManager serverConfigurationManager, @@ -68,7 +66,6 @@ namespace Jellyfin.Api.Controllers IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem, - IAuthorizationContext authContext, ILogger logger) { _serverConfigurationManager = serverConfigurationManager; @@ -78,7 +75,6 @@ namespace Jellyfin.Api.Controllers _mediaSourceManager = mediaSourceManager; _providerManager = providerManager; _fileSystem = fileSystem; - _authContext = authContext; _logger = logger; } @@ -361,7 +357,7 @@ namespace Jellyfin.Api.Controllers long positionTicks = 0; - var accessToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var accessToken = User.GetToken(); while (positionTicks < runtime) { diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index e9c46dcf3a..1cf528153f 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -67,7 +67,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) }, diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index c6b70f3d20..e194fc556e 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.SyncPlayDtos; -using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.SyncPlay.PlaybackRequests; @@ -24,23 +24,23 @@ namespace Jellyfin.Api.Controllers public class SyncPlayController : BaseJellyfinApiController { private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authorizationContext; private readonly ISyncPlayManager _syncPlayManager; + private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SyncPlayController( ISessionManager sessionManager, - IAuthorizationContext authorizationContext, - ISyncPlayManager syncPlayManager) + ISyncPlayManager syncPlayManager, + IUserManager userManager) { _sessionManager = sessionManager; - _authorizationContext = authorizationContext; _syncPlayManager = syncPlayManager; + _userManager = userManager; } /// @@ -55,7 +55,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayCreateGroup( [FromBody, Required] NewGroupRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new NewGroupRequest(requestData.GroupName); _syncPlayManager.NewGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayJoinGroup( [FromBody, Required] JoinGroupRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new JoinGroupRequest(requestData.GroupId); _syncPlayManager.JoinGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayLeaveGroup() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new LeaveGroupRequest(); _syncPlayManager.LeaveGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -105,7 +105,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayJoinGroup)] public async Task>> SyncPlayGetGroups() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new ListGroupsRequest(); return Ok(_syncPlayManager.ListGroups(currentSession, syncPlayRequest)); } @@ -122,7 +122,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetNewQueue( [FromBody, Required] PlayRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PlayGroupRequest( requestData.PlayingQueue, requestData.PlayingItemPosition, @@ -143,7 +143,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetPlaylistItem( [FromBody, Required] SetPlaylistItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetPlaylistItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayRemoveFromPlaylist( [FromBody, Required] RemoveFromPlaylistRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new RemoveFromPlaylistGroupRequest(requestData.PlaylistItemIds, requestData.ClearPlaylist, requestData.ClearPlayingItem); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayMovePlaylistItem( [FromBody, Required] MovePlaylistItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new MovePlaylistItemGroupRequest(requestData.PlaylistItemId, requestData.NewIndex); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -197,7 +197,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayQueue( [FromBody, Required] QueueRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new QueueGroupRequest(requestData.ItemIds, requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -213,7 +213,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayUnpause() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new UnpauseGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayPause() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PauseGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -245,7 +245,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayStop() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new StopGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -263,7 +263,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySeek( [FromBody, Required] SeekRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SeekGroupRequest(requestData.PositionTicks); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayBuffering( [FromBody, Required] BufferRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new BufferGroupRequest( requestData.When, requestData.PositionTicks, @@ -303,7 +303,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayReady( [FromBody, Required] ReadyRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new ReadyGroupRequest( requestData.When, requestData.PositionTicks, @@ -325,7 +325,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetIgnoreWait( [FromBody, Required] IgnoreWaitRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new IgnoreWaitGroupRequest(requestData.IgnoreWait); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -343,7 +343,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayNextItem( [FromBody, Required] NextItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new NextItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -361,7 +361,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayPreviousItem( [FromBody, Required] PreviousItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PreviousItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -379,7 +379,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetRepeatMode( [FromBody, Required] SetRepeatModeRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetRepeatModeGroupRequest(requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetShuffleMode( [FromBody, Required] SetShuffleModeRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetShuffleModeGroupRequest(requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -414,7 +414,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayPing( [FromBody, Required] PingRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PingGroupRequest(requestData.Ping); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index e39d05a6f5..ea13ceb91c 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableRewatching = false) { var options = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var result = _tvSeriesManager.GetNextUp( @@ -154,7 +154,7 @@ namespace Jellyfin.Api.Controllers var parentIdGuid = parentId ?? Guid.Empty; var options = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) @@ -223,7 +223,7 @@ namespace Jellyfin.Api.Controllers List episodes; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); if (seasonId.HasValue) // Season id was supplied. Get episodes by season id. @@ -349,7 +349,7 @@ namespace Jellyfin.Api.Controllers }); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user); diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index c463fb08aa..01e13b4fe3 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -6,13 +6,13 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; @@ -28,7 +28,6 @@ namespace Jellyfin.Api.Controllers [Route("")] public class UniversalAudioController : BaseJellyfinApiController { - private readonly IAuthorizationContext _authorizationContext; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly MediaInfoHelper _mediaInfoHelper; @@ -38,21 +37,18 @@ namespace Jellyfin.Api.Controllers /// /// Initializes a new instance of the class. /// - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of . /// Instance of . /// Instance of . public UniversalAudioController( - IAuthorizationContext authorizationContext, ILibraryManager libraryManager, ILogger logger, MediaInfoHelper mediaInfoHelper, AudioHelper audioHelper, DynamicHlsHelper dynamicHlsHelper) { - _authorizationContext = authorizationContext; _libraryManager = libraryManager; _logger = logger; _mediaInfoHelper = mediaInfoHelper; @@ -111,16 +107,12 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableRedirection = true) { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - authorizationInfo.DeviceId = deviceId; if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) { - userId = authorizationInfo.UserId; + userId = User.GetUserId(); } - var authInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", deviceProfile); var info = await _mediaInfoHelper.GetPlaybackInfo( @@ -138,7 +130,7 @@ namespace Jellyfin.Api.Controllers item, sourceInfo, deviceProfile, - authInfo, + User, maxStreamingBitrate ?? deviceProfile.MaxStreamingBitrate, startTimeTicks ?? 0, mediaSourceId ?? string.Empty, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index d1109bebc8..004690541b 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; @@ -264,7 +265,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UpdateUserPassword request) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password."); } @@ -282,7 +283,7 @@ namespace Jellyfin.Api.Controllers } else { - if (!HttpContext.User.IsInRole(UserRoles.Administrator)) + if (!User.IsInRole(UserRoles.Administrator)) { var success = await _userManager.AuthenticateUser( user.Username, @@ -299,7 +300,7 @@ namespace Jellyfin.Api.Controllers await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false); - var currentToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var currentToken = User.GetToken(); await _sessionManager.RevokeUserTokens(user.Id, currentToken).ConfigureAwait(false); } @@ -325,7 +326,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UpdateUserEasyPassword request) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the easy password."); } @@ -367,7 +368,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UserDto updateUser) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed."); } @@ -427,7 +428,7 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one enabled user in the system."); } - var currentToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var currentToken = User.GetToken(); await _sessionManager.RevokeUserTokens(user.Id, currentToken).ConfigureAwait(false); } @@ -452,7 +453,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UserConfiguration userConfig) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed"); } @@ -536,13 +537,13 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult GetCurrentUser() { - var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); - if (userId is null) + var userId = User.GetUserId(); + if (userId.Equals(default)) { return BadRequest(); } - var user = _userManager.GetUserById(userId.Value); + var user = _userManager.GetUserById(userId); if (user == null) { return BadRequest(); @@ -567,7 +568,7 @@ namespace Jellyfin.Api.Controllers if (filterByDevice) { - var deviceId = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).DeviceId; + var deviceId = User.GetDeviceId(); if (!string.IsNullOrWhiteSpace(deviceId)) { diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 940fa27a70..ee8a17b62d 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetUserRootFolder(); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(itemId); var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); return new QueryResult(dtos); @@ -200,7 +200,7 @@ namespace Jellyfin.Api.Controllers ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); if (item is IHasTrailers hasTrailers) { @@ -230,7 +230,7 @@ namespace Jellyfin.Api.Controllers ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return Ok(item .GetExtras() @@ -280,7 +280,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var list = _userViewManager.GetLatestItems( diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 04732ccf25..85d154cac2 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -85,7 +85,7 @@ namespace Jellyfin.Api.Controllers var folders = _userViewManager.GetUserViews(query); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var fields = dtoOptions.Fields.ToList(); fields.Add(ItemFields.PrimaryImageAspectRatio); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 4e28959345..bf08ad376b 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -43,7 +43,6 @@ namespace Jellyfin.Api.Controllers private readonly IUserManager _userManager; private readonly IDtoService _dtoService; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -61,7 +60,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -74,7 +72,6 @@ namespace Jellyfin.Api.Controllers IUserManager userManager, IDtoService dtoService, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -87,7 +84,6 @@ namespace Jellyfin.Api.Controllers _userManager = userManager; _dtoService = dtoService; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -120,7 +116,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(itemId); var dtoOptions = new DtoOptions(); - dtoOptions = dtoOptions.AddClientFields(Request); + dtoOptions = dtoOptions.AddClientFields(User); BaseItemDto[] items; if (item is Video video) @@ -429,8 +425,7 @@ namespace Jellyfin.Api.Controllers var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index 7c02e25501..b732bdff3c 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -87,7 +87,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? enableImages = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); if (userId.HasValue && !userId.Value.Equals(default)) { diff --git a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000000..6b3e78d4d1 --- /dev/null +++ b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Security.Claims; +using Jellyfin.Api.Constants; + +namespace Jellyfin.Api.Extensions; + +/// +/// Extensions for . +/// +public static class ClaimsPrincipalExtensions +{ + /// + /// Get user id from claims. + /// + /// Current claims principal. + /// User id. + public static Guid GetUserId(this ClaimsPrincipal user) + { + var value = GetClaimValue(user, InternalClaimTypes.UserId); + return string.IsNullOrEmpty(value) + ? default + : Guid.Parse(value); + } + + /// + /// Get device id from claims. + /// + /// Current claims principal. + /// Device id. + public static string? GetDeviceId(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.DeviceId); + + /// + /// Get device from claims. + /// + /// Current claims principal. + /// Device. + public static string? GetDevice(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Device); + + /// + /// Get client from claims. + /// + /// Current claims principal. + /// Client. + public static string? GetClient(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Client); + + /// + /// Get version from claims. + /// + /// Current claims principal. + /// Version. + public static string? GetVersion(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Version); + + /// + /// Get token from claims. + /// + /// Current claims principal. + /// Token. + public static string? GetToken(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Token); + + /// + /// Gets a flag specifying whether the request is using an api key. + /// + /// Current claims principal. + /// The flag specifying whether the request is using an api key. + public static bool GetIsApiKey(this ClaimsPrincipal user) + { + var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); + return !string.IsNullOrEmpty(claimValue) + && bool.TryParse(claimValue, out var parsedClaimValue) + && parsedClaimValue; + } + + private static string? GetClaimValue(in ClaimsPrincipal user, string name) + => user.Claims.FirstOrDefault(claim => claim.Type.Equals(name, StringComparison.OrdinalIgnoreCase))?.Value; +} diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs index 5e338b67da..9e784f7c45 100644 --- a/Jellyfin.Api/Extensions/DtoExtensions.cs +++ b/Jellyfin.Api/Extensions/DtoExtensions.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Jellyfin.Api.Helpers; +using System.Security.Claims; using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Model.Entities; @@ -22,14 +22,14 @@ namespace Jellyfin.Api.Extensions /// Legacy order: 2. /// /// DtoOptions object. - /// Current request. + /// Current claims principal. /// Modified DtoOptions object. internal static DtoOptions AddClientFields( - this DtoOptions dtoOptions, HttpRequest request) + this DtoOptions dtoOptions, ClaimsPrincipal user) { dtoOptions.Fields ??= Array.Empty(); - string? client = ClaimHelpers.GetClient(request.HttpContext.User); + string? client = user.GetClient(); // No client in claim if (string.IsNullOrEmpty(client)) diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 27497cd599..bc83ff48a8 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -11,7 +11,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Http; @@ -25,7 +24,6 @@ namespace Jellyfin.Api.Helpers public class AudioHelper { private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; @@ -41,7 +39,6 @@ namespace Jellyfin.Api.Helpers /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -54,7 +51,6 @@ namespace Jellyfin.Api.Helpers /// Instance of . public AudioHelper( IDlnaManager dlnaManager, - IAuthorizationContext authContext, IUserManager userManager, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, @@ -67,7 +63,6 @@ namespace Jellyfin.Api.Helpers EncodingHelper encodingHelper) { _dlnaManager = dlnaManager; - _authContext = authContext; _userManager = userManager; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; @@ -102,8 +97,7 @@ namespace Jellyfin.Api.Helpers using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - _httpContextAccessor.HttpContext.Request, - _authContext, + _httpContextAccessor.HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Helpers/ClaimHelpers.cs b/Jellyfin.Api/Helpers/ClaimHelpers.cs deleted file mode 100644 index c1c2f93b49..0000000000 --- a/Jellyfin.Api/Helpers/ClaimHelpers.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Linq; -using System.Security.Claims; -using Jellyfin.Api.Constants; - -namespace Jellyfin.Api.Helpers -{ - /// - /// Claim Helpers. - /// - public static class ClaimHelpers - { - /// - /// Get user id from claims. - /// - /// Current claims principal. - /// User id. - public static Guid? GetUserId(in ClaimsPrincipal user) - { - var value = GetClaimValue(user, InternalClaimTypes.UserId); - return string.IsNullOrEmpty(value) - ? null - : Guid.Parse(value); - } - - /// - /// Get device id from claims. - /// - /// Current claims principal. - /// Device id. - public static string? GetDeviceId(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.DeviceId); - - /// - /// Get device from claims. - /// - /// Current claims principal. - /// Device. - public static string? GetDevice(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Device); - - /// - /// Get client from claims. - /// - /// Current claims principal. - /// Client. - public static string? GetClient(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Client); - - /// - /// Get version from claims. - /// - /// Current claims principal. - /// Version. - public static string? GetVersion(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Version); - - /// - /// Get token from claims. - /// - /// Current claims principal. - /// Token. - public static string? GetToken(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Token); - - /// - /// Gets a flag specifying whether the request is using an api key. - /// - /// Current claims principal. - /// The flag specifying whether the request is using an api key. - public static bool GetIsApiKey(in ClaimsPrincipal user) - { - var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); - return !string.IsNullOrEmpty(claimValue) - && bool.TryParse(claimValue, out var parsedClaimValue) - && parsedClaimValue; - } - - private static string? GetClaimValue(in ClaimsPrincipal user, string name) - { - return user?.Identities - .SelectMany(c => c.Claims) - .Where(claim => claim.Type.Equals(name, StringComparison.OrdinalIgnoreCase)) - .Select(claim => claim.Value) - .FirstOrDefault(); - } - } -} diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 83c9141a93..fa392e5674 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -7,6 +7,7 @@ using System.Security.Claims; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -15,7 +16,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Net; @@ -34,7 +34,6 @@ namespace Jellyfin.Api.Helpers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -51,7 +50,6 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -65,7 +63,6 @@ namespace Jellyfin.Api.Helpers ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -79,7 +76,6 @@ namespace Jellyfin.Api.Helpers _libraryManager = libraryManager; _userManager = userManager; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -128,8 +124,7 @@ namespace Jellyfin.Api.Helpers using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - _httpContextAccessor.HttpContext.Request, - _authContext, + _httpContextAccessor.HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -483,7 +478,7 @@ namespace Jellyfin.Api.Helpers state.Request.MediaSourceId, stream.Index.ToString(CultureInfo.InvariantCulture), 30.ToString(CultureInfo.InvariantCulture), - ClaimHelpers.GetToken(user)); + user.GetToken()); var line = string.Format( CultureInfo.InvariantCulture, diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 5c05c57a61..11f490bb41 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -2,9 +2,11 @@ using System.Globalization; using System.Linq; using System.Net; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; @@ -15,7 +17,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -39,7 +40,6 @@ namespace Jellyfin.Api.Helpers private readonly ILogger _logger; private readonly INetworkManager _networkManager; private readonly IDeviceManager _deviceManager; - private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. @@ -52,7 +52,6 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public MediaInfoHelper( IUserManager userManager, ILibraryManager libraryManager, @@ -61,8 +60,7 @@ namespace Jellyfin.Api.Helpers IServerConfigurationManager serverConfigurationManager, ILogger logger, INetworkManager networkManager, - IDeviceManager deviceManager, - IAuthorizationContext authContext) + IDeviceManager deviceManager) { _userManager = userManager; _libraryManager = libraryManager; @@ -72,7 +70,6 @@ namespace Jellyfin.Api.Helpers _logger = logger; _networkManager = networkManager; _deviceManager = deviceManager; - _authContext = authContext; } /// @@ -147,7 +144,7 @@ namespace Jellyfin.Api.Helpers /// Item to set data for. /// Media source info. /// Device profile. - /// Authorization info. + /// Current claims principal. /// Max bitrate. /// Start time ticks. /// Media source id. @@ -166,7 +163,7 @@ namespace Jellyfin.Api.Helpers BaseItem item, MediaSourceInfo mediaSource, DeviceProfile profile, - AuthorizationInfo auth, + ClaimsPrincipal claimsPrincipal, int? maxBitrate, long startTimeTicks, string mediaSourceId, @@ -188,7 +185,7 @@ namespace Jellyfin.Api.Helpers { MediaSources = new[] { mediaSource }, Context = EncodingContext.Streaming, - DeviceId = auth.DeviceId, + DeviceId = claimsPrincipal.GetDeviceId(), ItemId = item.Id, Profile = profile, MaxAudioChannels = maxAudioChannels, @@ -290,7 +287,7 @@ namespace Jellyfin.Api.Helpers mediaSource.SupportsDirectPlay = false; mediaSource.SupportsDirectStream = false; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", claimsPrincipal.GetToken()).TrimStart('-'); mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; @@ -301,7 +298,7 @@ namespace Jellyfin.Api.Helpers if (!mediaSource.SupportsDirectPlay && (mediaSource.SupportsTranscoding || mediaSource.SupportsDirectStream)) { streamInfo.PlayMethod = PlayMethod.Transcode; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", claimsPrincipal.GetToken()).TrimStart('-'); if (!allowVideoStreamCopy) { @@ -316,7 +313,7 @@ namespace Jellyfin.Api.Helpers } // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, claimsPrincipal.GetToken()); mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } @@ -384,19 +381,17 @@ namespace Jellyfin.Api.Helpers /// /// Open media source. /// - /// Http Request. + /// Http Context. /// Live stream request. /// A containing the . - public async Task OpenMediaSource(HttpRequest httpRequest, LiveStreamRequest request) + public async Task OpenMediaSource(HttpContext httpContext, LiveStreamRequest request) { - var authInfo = await _authContext.GetAuthorizationInfo(httpRequest).ConfigureAwait(false); - var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false); var profile = request.DeviceProfile; if (profile == null) { - var clientCapabilities = _deviceManager.GetCapabilities(authInfo.DeviceId); + var clientCapabilities = _deviceManager.GetCapabilities(httpContext.User.GetDeviceId()); if (clientCapabilities != null) { profile = clientCapabilities.DeviceProfile; @@ -411,7 +406,7 @@ namespace Jellyfin.Api.Helpers item, result.MediaSource, profile, - authInfo, + httpContext.User, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, @@ -425,7 +420,7 @@ namespace Jellyfin.Api.Helpers true, true, true, - httpRequest.HttpContext.GetNormalizedRemoteIp()); + httpContext.GetNormalizedRemoteIp()); } else { diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 20427d7fab..8c5af013aa 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; +using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; @@ -55,37 +58,42 @@ namespace Jellyfin.Api.Helpers /// /// Checks if the user can update an entry. /// - /// Instance of the interface. - /// The . + /// An instance of the interface. + /// The for the current request. /// The user id. /// Whether to restrict the user preferences. /// A whether the user can update the entry. - internal static async Task AssertCanUpdateUser(IAuthorizationContext authContext, HttpRequest requestContext, Guid userId, bool restrictUserPreferences) + internal static bool AssertCanUpdateUser(IUserManager userManager, ClaimsPrincipal claimsPrincipal, Guid userId, bool restrictUserPreferences) { - var auth = await authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - - var authenticatedUser = auth.User; + var authenticatedUserId = claimsPrincipal.GetUserId(); + var isAdministrator = claimsPrincipal.IsInRole(UserRoles.Administrator); // If they're going to update the record of another user, they must be an administrator - if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator)) - || (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess)) + if (!userId.Equals(authenticatedUserId) && !isAdministrator) { return false; } - return true; + // TODO the EnableUserPreferenceAccess policy does not seem to be used elsewhere + if (!restrictUserPreferences || isAdministrator) + { + return true; + } + + var user = userManager.GetUserById(userId); + return user.EnableUserPreferenceAccess; } - internal static async Task GetSession(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) + internal static async Task GetSession(ISessionManager sessionManager, IUserManager userManager, HttpContext httpContext) { - var authorization = await authContext.GetAuthorizationInfo(request).ConfigureAwait(false); - var user = authorization.User; + var userId = httpContext.User.GetUserId(); + var user = userManager.GetUserById(userId); var session = await sessionManager.LogSessionActivity( - authorization.Client, - authorization.Version, - authorization.DeviceId, - authorization.Device, - request.HttpContext.GetNormalizedRemoteIp().ToString(), + httpContext.User.GetClient(), + httpContext.User.GetVersion(), + httpContext.User.GetDeviceId(), + httpContext.User.GetDevice(), + httpContext.GetNormalizedRemoteIp().ToString(), user).ConfigureAwait(false); if (session == null) @@ -96,9 +104,9 @@ namespace Jellyfin.Api.Helpers return session; } - internal static async Task GetSessionId(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) + internal static async Task GetSessionId(ISessionManager sessionManager, IUserManager userManager, HttpContext httpContext) { - var session = await GetSession(sessionManager, authContext, request).ConfigureAwait(false); + var session = await GetSession(sessionManager, userManager, httpContext).ConfigureAwait(false); return session.Id; } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index b552df0a45..3705737739 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; @@ -14,7 +15,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -33,8 +33,7 @@ namespace Jellyfin.Api.Helpers /// Gets the current streaming state. /// /// The . - /// The . - /// Instance of the interface. + /// The . /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -49,8 +48,7 @@ namespace Jellyfin.Api.Helpers /// A containing the current . public static async Task GetStreamingState( StreamingRequestDto streamingRequest, - HttpRequest httpRequest, - IAuthorizationContext authorizationContext, + HttpContext httpContext, IMediaSourceManager mediaSourceManager, IUserManager userManager, ILibraryManager libraryManager, @@ -63,6 +61,7 @@ namespace Jellyfin.Api.Helpers TranscodingJobType transcodingJobType, CancellationToken cancellationToken) { + var httpRequest = httpContext.Request; // Parse the DLNA time seek header if (!streamingRequest.StartTimeTicks.HasValue) { @@ -101,10 +100,10 @@ namespace Jellyfin.Api.Helpers EnableDlnaHeaders = enableDlnaHeaders }; - var auth = await authorizationContext.GetAuthorizationInfo(httpRequest).ConfigureAwait(false); - if (!auth.UserId.Equals(default)) + var userId = httpContext.User.GetUserId(); + if (!userId.Equals(default)) { - state.User = userManager.GetUserById(auth.UserId); + state.User = userManager.GetUserById(userId); } if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.Request.VideoCodec)) diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 2adb006e48..c663c6e310 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; @@ -17,7 +18,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; @@ -46,7 +46,6 @@ namespace Jellyfin.Api.Helpers private readonly IAttachmentExtractor _attachmentExtractor; private readonly IApplicationPaths _appPaths; - private readonly IAuthorizationContext _authorizationContext; private readonly EncodingHelper _encodingHelper; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; @@ -55,6 +54,7 @@ namespace Jellyfin.Api.Helpers private readonly IServerConfigurationManager _serverConfigurationManager; private readonly ISessionManager _sessionManager; private readonly ILoggerFactory _loggerFactory; + private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. @@ -67,9 +67,9 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of . /// Instance of the interface. + /// Instance of the interface. public TranscodingJobHelper( IAttachmentExtractor attachmentExtractor, IApplicationPaths appPaths, @@ -79,9 +79,9 @@ namespace Jellyfin.Api.Helpers IMediaEncoder mediaEncoder, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, - IAuthorizationContext authorizationContext, EncodingHelper encodingHelper, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IUserManager userManager) { _attachmentExtractor = attachmentExtractor; _appPaths = appPaths; @@ -91,9 +91,9 @@ namespace Jellyfin.Api.Helpers _mediaEncoder = mediaEncoder; _serverConfigurationManager = serverConfigurationManager; _sessionManager = sessionManager; - _authorizationContext = authorizationContext; _encodingHelper = encodingHelper; _loggerFactory = loggerFactory; + _userManager = userManager; DeleteEncodedMediaCache(); @@ -512,8 +512,9 @@ namespace Jellyfin.Api.Helpers if (state.VideoRequest != null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { - var auth = await _authorizationContext.GetAuthorizationInfo(request).ConfigureAwait(false); - if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) + var userId = request.HttpContext.User.GetUserId(); + var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); + if (user != null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) { this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state); diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs deleted file mode 100644 index b48181b3fb..0000000000 --- a/MediaBrowser.Controller/Net/ISessionContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Session; -using Microsoft.AspNetCore.Http; - -namespace MediaBrowser.Controller.Net -{ - public interface ISessionContext - { - Task GetSession(object requestContext); - - Task GetUser(object requestContext); - - Task GetSession(HttpContext requestContext); - - Task GetUser(HttpContext requestContext); - } -} -- cgit v1.2.3 From 6afc9110439c3c5345beb9a6bac1968ee4dedce8 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 6 Oct 2022 14:15:40 +0200 Subject: fix Release build --- Jellyfin.Api/Controllers/ItemsController.cs | 4 ++-- Jellyfin.Api/Controllers/TrailersController.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 12 ++++++------ Jellyfin.Api/Helpers/MediaInfoHelper.cs | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 3c7c375d4e..80ae5abcbf 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -152,7 +152,7 @@ namespace Jellyfin.Api.Controllers /// A with the items. [HttpGet("Items")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetItems( + public ActionResult> GetItems( [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, @@ -627,7 +627,7 @@ namespace Jellyfin.Api.Controllers /// A with the items. [HttpGet("Users/{userId}/Items")] [ProducesResponseType(StatusCodes.Status200OK)] - public Task>> GetItemsByUserId( + public ActionResult> GetItemsByUserId( [FromRoute] Guid userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index cf812fa237..b296d1c960 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -119,7 +119,7 @@ namespace Jellyfin.Api.Controllers /// A with the trailers. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public Task>> GetTrailers( + public ActionResult> GetTrailers( [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 004690541b..ff653fe6bb 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -83,11 +83,11 @@ namespace Jellyfin.Api.Controllers [HttpGet] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetUsers( + public ActionResult> GetUsers( [FromQuery] bool? isHidden, [FromQuery] bool? isDisabled) { - var users = await Get(isHidden, isDisabled, false, false).ConfigureAwait(false); + var users = Get(isHidden, isDisabled, false, false); return Ok(users); } @@ -98,15 +98,15 @@ namespace Jellyfin.Api.Controllers /// An containing the public users. [HttpGet("Public")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetPublicUsers() + public ActionResult> GetPublicUsers() { // If the startup wizard hasn't been completed then just return all users if (!_config.Configuration.IsStartupWizardCompleted) { - return Ok(await Get(false, false, false, false).ConfigureAwait(false)); + return Ok(Get(false, false, false, false)); } - return Ok(await Get(false, false, true, true).ConfigureAwait(false)); + return Ok(Get(false, false, true, true)); } /// @@ -552,7 +552,7 @@ namespace Jellyfin.Api.Controllers return _userManager.GetUserDto(user); } - private async Task> Get(bool? isHidden, bool? isDisabled, bool filterByDevice, bool filterByNetwork) + private IEnumerable Get(bool? isHidden, bool? isDisabled, bool filterByDevice, bool filterByNetwork) { var users = _userManager.Users; diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 11f490bb41..4441ae023e 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -313,7 +313,8 @@ namespace Jellyfin.Api.Helpers } // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, claimsPrincipal.GetToken()); + // The token must not be null + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, claimsPrincipal.GetToken()!); mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } -- cgit v1.2.3 From f6af28cf96624f0f3ec6073fcee099623491c0b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 11 Oct 2022 23:37:29 +0200 Subject: Fix some things that slipped through the cracks --- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../Library/Resolvers/Books/BookResolver.cs | 5 +- .../Library/Resolvers/GenericFolderResolver.cs | 1 + .../Library/Resolvers/ItemResolver.cs | 58 ---------------------- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Library/Resolvers/PhotoResolver.cs | 1 + .../Library/Resolvers/TV/EpisodeResolver.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 4 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 19 +++---- Jellyfin.Api/Controllers/PlaystateController.cs | 4 +- Jellyfin.Api/Controllers/SessionController.cs | 4 +- .../Controllers/UniversalAudioController.cs | 2 +- MediaBrowser.Controller/Properties/AssemblyInfo.cs | 2 + MediaBrowser.Controller/Resolvers/ItemResolver.cs | 4 +- jellyfin.ruleset | 10 ++++ 15 files changed, 36 insertions(+), 84 deletions(-) delete mode 100644 Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs (limited to 'Jellyfin.Api/Controllers') diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 3d6b9f3b62..b2a7abb1bd 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// /// The args. /// `0. - public override T Resolve(ItemResolveArgs args) + protected override T Resolve(ItemResolveArgs args) { return ResolveVideo(args, false); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 8f224f547a..6fc200e3b1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -8,15 +8,16 @@ using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers.Books { - public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver + public class BookResolver : ItemResolver { private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" }; - public override Book Resolve(ItemResolveArgs args) + protected override Book Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs index f109a5e9a2..0799622825 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; namespace Emby.Server.Implementations.Library.Resolvers { diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs deleted file mode 100644 index 3f29ab191f..0000000000 --- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs +++ /dev/null @@ -1,58 +0,0 @@ -#nullable disable - -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace Emby.Server.Implementations.Library.Resolvers -{ - /// - /// Class ItemResolver. - /// - /// The type of BaseItem. - public abstract class ItemResolver : IItemResolver - where T : BaseItem, new() - { - /// - /// Gets the priority. - /// - /// The priority. - public virtual ResolverPriority Priority => ResolverPriority.First; - - /// - /// Resolves the specified args. - /// - /// The args. - /// `0. - protected virtual T Resolve(ItemResolveArgs args) - { - return null; - } - - /// - /// Sets initial values on the newly resolved item. - /// - /// The item. - /// The args. - protected virtual void SetInitialItemValues(T item, ItemResolveArgs args) - { - } - - /// - /// Resolves the path. - /// - /// The args. - /// BaseItem. - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) - { - var item = Resolve(args); - - if (item != null) - { - SetInitialItemValues(item, args); - } - - return item; - } - } -} diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index b2f388a667..8f9e5f01b1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// /// The args. /// Video. - public override Video Resolve(ItemResolveArgs args) + protected override Video Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index af4abfb805..e11fb262eb 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -12,6 +12,7 @@ using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index bfa73af2f4..9ba079edfd 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// /// The args. /// Episode. - public override Episode Resolve(ItemResolveArgs args) + protected override Episode Resolve(ItemResolveArgs args) { var parent = args.Parent; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3db422c2f0..74321a256e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2218,9 +2218,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { continue; } - + // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856 - if (group.Key.EndsWith("0000")) + if (group.Key.EndsWith("0000", StringComparison.Ordinal)) { continue; } diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 8ed8e1fc6e..0232843b16 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -88,25 +88,20 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } else { - await using var stream = new FileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - + await using var stream = AsyncFile.OpenRead(info.Path); return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } } private async Task UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) { - int index = originalUrl.IndexOf('?', StringComparison.CurrentCulture); - string ext = Path.GetExtension(index > -1 ? originalUrl.Remove(index) : originalUrl); - - await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) + if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) { try { @@ -166,16 +161,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null, - HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), - OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, + ImageUrl = !string.IsNullOrEmpty(program.Icon?.Source) ? program.Icon.Source : null, + HasImage = !string.IsNullOrEmpty(program.Icon?.Source), + OfficialRating = !string.IsNullOrEmpty(program.Rating?.Value) ? program.Rating.Value : null, CommunityRating = program.StarRating, SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) { - string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty) /*+ (p.IceTvEpisodeNumber ?? string.Empty)*/; + string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty); if (programInfo.SeasonNumber.HasValue) { diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 0dd4bf8035..3a2ba033ea 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -274,7 +274,7 @@ namespace Jellyfin.Api.Controllers }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -319,7 +319,7 @@ namespace Jellyfin.Api.Controllers await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 28415555ed..31b95162d4 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendPlayCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, playRequest, CancellationToken.None) @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? controllingUserId) { await _sessionManager.SendPlaystateCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, new PlaystateRequest() { diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 01e13b4fe3..d77126a353 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) + if (!userId.HasValue || userId.Value.Equals(default)) { userId = User.GetUserId(); } diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs index 60e7923091..4cd5c76c1e 100644 --- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -14,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.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 diff --git a/MediaBrowser.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs index 7fd54fcc69..e7bf013fa2 100644 --- a/MediaBrowser.Controller/Resolvers/ItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// `0. - public virtual T Resolve(ItemResolveArgs args) + protected internal virtual T Resolve(ItemResolveArgs args) { return null; } @@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// BaseItem. - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) + public BaseItem ResolvePath(ItemResolveArgs args) { var item = Resolve(args); diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 5ac5f49239..8144db93d5 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -5,8 +5,16 @@ + + + + + + + + @@ -69,6 +77,8 @@ + + -- cgit v1.2.3 From fb9023f2d83c9e0947c63d4a0c27b35d6c711d9c Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 9 Nov 2022 18:02:49 -0500 Subject: Fix items endpoint not honoring library access control --- Jellyfin.Api/Controllers/ItemsController.cs | 36 ++++------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 80ae5abcbf..33b67b3898 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -282,39 +282,13 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { BaseItemKind.Playlist }; } - var enabledChannels = isApiKey - ? Array.Empty() - : user!.GetPreferenceValues(PreferenceKind.EnabledChannels); - - // api keys are always enabled for all folders - bool isInEnabledFolder = isApiKey - || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 - // Assume all folders inside an EnabledChannel are enabled - || Array.IndexOf(enabledChannels, item.Id) != -1 - // Assume all items inside an EnabledChannel are enabled - || Array.IndexOf(enabledChannels, item.ChannelId) != -1; - - if (!isInEnabledFolder) - { - var collectionFolders = _libraryManager.GetCollectionFolders(item); - foreach (var collectionFolder in collectionFolders) - { - // api keys never enter this block, so user is never null - if (user!.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) - { - isInEnabledFolder = true; - } - } - } - - // api keys are always enabled for all folders, so user is never null if (item is not UserRootFolder - && !isInEnabledFolder - && !user!.HasPermission(PermissionKind.EnableAllFolders) - && !user.HasPermission(PermissionKind.EnableAllChannels) - && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase)) + // api keys can always access all folders + && !isApiKey + // check the item is visible for the user + && !item.IsVisible(user)) { - _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user.Username, item.Name); + _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user!.Username, item.Name); return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}."); } -- cgit v1.2.3 From 9f352ccb5b5ab85eac064f70fc819f04984fa0d7 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Wed, 9 Nov 2022 18:31:30 -0500 Subject: Fix media folders endpoint access control --- Jellyfin.Api/Controllers/LibraryController.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index e9492a6a47..b056215b92 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -491,6 +491,12 @@ namespace Jellyfin.Api.Controllers { var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList(); + if (!User.GetIsApiKey() && !User.IsInRole(UserRoles.Administrator)) + { + var user = _userManager.GetUserById(User.GetUserId()); + items = items.Where(i => i.IsVisible(user)).ToList(); + } + if (isHidden.HasValue) { var val = isHidden.Value; -- cgit v1.2.3 From c6dbcb661bec6fc02347cd0bdce2e5e6e4ee0dbe Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Thu, 10 Nov 2022 01:04:16 -0500 Subject: Use elevated access control for media folders endpoint --- Jellyfin.Api/Controllers/LibraryController.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'Jellyfin.Api/Controllers') diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index b056215b92..7a57bf1a21 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -485,18 +485,12 @@ namespace Jellyfin.Api.Controllers /// Media folders returned. /// List of user media folders. [HttpGet("Library/MediaFolders")] - [Authorize(Policy = Policies.DefaultAuthorization)] + [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetMediaFolders([FromQuery] bool? isHidden) { var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList(); - if (!User.GetIsApiKey() && !User.IsInRole(UserRoles.Administrator)) - { - var user = _userManager.GetUserById(User.GetUserId()); - items = items.Where(i => i.IsVisible(user)).ToList(); - } - if (isHidden.HasValue) { var val = isHidden.Value; -- cgit v1.2.3