From 259fe4522c4fdcd7467500780fc70ccd75108bf2 Mon Sep 17 00:00:00 2001 From: LJQ Date: Tue, 17 Oct 2023 17:25:41 +0800 Subject: [PATCH 001/132] Update /Device endpoint to return CustomName --- .../Devices/DeviceManager.cs | 10 +++++++++- MediaBrowser.Model/Devices/DeviceInfo.cs | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index a4b4c19599..80abe01abc 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -177,7 +177,7 @@ namespace Jellyfin.Server.Implementations.Devices .OrderByDescending(d => d.DateLastActivity) .ThenBy(d => d.DeviceId) .AsAsyncEnumerable(); - + IAsyncEnumerable deviceOptions = dbContext.DeviceOptions.AsAsyncEnumerable(); if (supportsSync.HasValue) { sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value); @@ -195,6 +195,14 @@ namespace Jellyfin.Server.Implementations.Devices } var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false); + await foreach (var deviceOption in deviceOptions) + { + var deviceInfo = array.FirstOrDefault(d => d.Id.Equals(deviceOption.DeviceId, StringComparison.OrdinalIgnoreCase)); + if (deviceInfo != null) + { + deviceInfo.CustomName = deviceOption.CustomName; + } + } return new QueryResult(array); } diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs index 7a1c7a7382..4962992a0a 100644 --- a/MediaBrowser.Model/Devices/DeviceInfo.cs +++ b/MediaBrowser.Model/Devices/DeviceInfo.cs @@ -15,6 +15,8 @@ namespace MediaBrowser.Model.Devices public string Name { get; set; } + public string CustomName { get; set; } + /// /// Gets or sets the access token. /// From 2f9b44fcd04bf7974d42b6db3f9e7e500ae8093c Mon Sep 17 00:00:00 2001 From: LJQ Date: Wed, 18 Oct 2023 00:41:33 +0800 Subject: [PATCH 002/132] Switch to LINQ and updated /Device/Info endpoint to return in accordance to the updated API doc --- .../Devices/DeviceManager.cs | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 80abe01abc..807d703eb7 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -110,21 +110,21 @@ namespace Jellyfin.Server.Implementations.Devices /// public async Task GetDevice(string id) { - Device? device; var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - device = await dbContext.Devices + var device = await dbContext.Devices .Where(d => d.DeviceId == id) .OrderByDescending(d => d.DateLastActivity) .Include(d => d.User) + .SelectMany(d => dbContext.DeviceOptions.Where(o => o.DeviceId == d.DeviceId).DefaultIfEmpty(), (d, o) => new { Device = d, Options = o }) .FirstOrDefaultAsync() .ConfigureAwait(false); + + var deviceInfo = device is null ? null : ToDeviceInfo(device.Device, device.Options); + + return deviceInfo; } - - var deviceInfo = device is null ? null : ToDeviceInfo(device); - - return deviceInfo; } /// @@ -172,15 +172,15 @@ namespace Jellyfin.Server.Implementations.Devices var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - IAsyncEnumerable sessions = dbContext.Devices + var sessions = dbContext.Devices .Include(d => d.User) .OrderByDescending(d => d.DateLastActivity) .ThenBy(d => d.DeviceId) + .SelectMany(d => dbContext.DeviceOptions.Where(o => o.DeviceId == d.DeviceId).DefaultIfEmpty(), (d, o) => new { Device = d, Options = o }) .AsAsyncEnumerable(); - IAsyncEnumerable deviceOptions = dbContext.DeviceOptions.AsAsyncEnumerable(); if (supportsSync.HasValue) { - sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value); + sessions = sessions.Where(i => GetCapabilities(i.Device.DeviceId).SupportsSync == supportsSync.Value); } if (userId.HasValue) @@ -191,18 +191,10 @@ namespace Jellyfin.Server.Implementations.Devices throw new ResourceNotFoundException(); } - sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)); + sessions = sessions.Where(i => CanAccessDevice(user, i.Device.DeviceId)); } - var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false); - await foreach (var deviceOption in deviceOptions) - { - var deviceInfo = array.FirstOrDefault(d => d.Id.Equals(deviceOption.DeviceId, StringComparison.OrdinalIgnoreCase)); - if (deviceInfo != null) - { - deviceInfo.CustomName = deviceOption.CustomName; - } - } + var array = await sessions.Select(device => ToDeviceInfo(device.Device, device.Options)).ToArrayAsync().ConfigureAwait(false); return new QueryResult(array); } @@ -250,5 +242,23 @@ namespace Jellyfin.Server.Implementations.Devices IconUrl = caps.IconUrl }; } + + private DeviceInfo ToDeviceInfo(Device authInfo, DeviceOptions? options) + { + var caps = GetCapabilities(authInfo.DeviceId); + + return new DeviceInfo + { + AppName = authInfo.AppName, + AppVersion = authInfo.AppVersion, + Id = authInfo.DeviceId, + LastUserId = authInfo.UserId, + LastUserName = authInfo.User.Username, + Name = authInfo.DeviceName, + DateLastActivity = authInfo.DateLastActivity, + IconUrl = caps.IconUrl, + CustomName = options?.CustomName, + }; + } } } From 98bc2fea8b20a52edfd142fbbf2ffbf79b0608b0 Mon Sep 17 00:00:00 2001 From: LJQ Date: Wed, 18 Oct 2023 01:40:36 +0800 Subject: [PATCH 003/132] Removed dupe ToDeviceInfo --- .../Devices/DeviceManager.cs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 807d703eb7..d8eee12467 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -226,24 +226,7 @@ namespace Jellyfin.Server.Implementations.Devices || !GetCapabilities(deviceId).SupportsPersistentIdentifier; } - private DeviceInfo ToDeviceInfo(Device authInfo) - { - var caps = GetCapabilities(authInfo.DeviceId); - - return new DeviceInfo - { - AppName = authInfo.AppName, - AppVersion = authInfo.AppVersion, - Id = authInfo.DeviceId, - LastUserId = authInfo.UserId, - LastUserName = authInfo.User.Username, - Name = authInfo.DeviceName, - DateLastActivity = authInfo.DateLastActivity, - IconUrl = caps.IconUrl - }; - } - - private DeviceInfo ToDeviceInfo(Device authInfo, DeviceOptions? options) + private DeviceInfo ToDeviceInfo(Device authInfo, DeviceOptions? options = null) { var caps = GetCapabilities(authInfo.DeviceId); From bce22fcc2a50d533e4d69760ec99cd09c4cb7ad7 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Sun, 12 Nov 2023 17:52:24 +0300 Subject: [PATCH 004/132] Added new API endpoint to update User item data --- Jellyfin.Api/Controllers/ItemsController.cs | 106 +++++++++++++++++- .../Entities/UserDataSaveReason.cs | 7 +- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 4e46e808ae..38a7d115e5 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Threading; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; @@ -34,6 +35,7 @@ public class ItemsController : BaseJellyfinApiController private readonly IDtoService _dtoService; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; + private readonly IUserDataManager _userDataRepository; /// /// Initializes a new instance of the class. @@ -44,13 +46,15 @@ public class ItemsController : BaseJellyfinApiController /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public ItemsController( IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, ILogger logger, - ISessionManager sessionManager) + ISessionManager sessionManager, + IUserDataManager userDataRepository) { _userManager = userManager; _libraryManager = libraryManager; @@ -58,6 +62,7 @@ public class ItemsController : BaseJellyfinApiController _dtoService = dtoService; _logger = logger; _sessionManager = sessionManager; + _userDataRepository = userDataRepository; } /// @@ -881,4 +886,103 @@ public class ItemsController : BaseJellyfinApiController itemsResult.TotalRecordCount, returnItems); } + + /// + /// Get Item User Data. + /// + /// The user id. + /// The item id. + /// item user data returned. + /// When item is not found. + /// Return Item . + [HttpGet("Users/{userId}/Items/{itemId}/UserData")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GetItemUserData( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId) + { + var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException(); + var item = _libraryManager.GetItemById(itemId); + + return (item == null) ? NotFound() : _userDataRepository.GetUserDataDto(item, user); + } + + /// + /// Update Item User Data. + /// + /// The user id. + /// The item id. + /// Optional. Whether to mark the item as played. + /// Optional. Whether to mark the item as favorite. + /// Optional. Whether mark the item as liked. + /// Optional. User item rating. + /// Optional. User Item playback Position Ticks. 1 tick = 10000 ms. + /// Item user data. + /// Optional. The date the item was played. + /// update user data returned. + /// When item is not found. + /// . + [HttpPost("Users/{userId}/Items/{itemId}/UserData")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult UpdateItemUserData( + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, + [FromQuery] bool? played, + [FromQuery] bool? favorite, + [FromQuery] bool? likes, + [FromQuery] double? rating, + [FromQuery] long? playbackPositionTicks, + [FromQuery] int? playCount, + [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? lastPlayedDate) + { + var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException(); + var item = _libraryManager.GetItemById(itemId); + if (item == null) + { + return NotFound(); + } + + var userData = _userDataRepository.GetUserData(user, item); + + if (played.HasValue) + { + userData.Played = played.Value; + } + + if (favorite.HasValue) + { + userData.IsFavorite = favorite.Value; + } + + if (likes.HasValue) + { + userData.Likes = likes.Value; + } + + if (rating.HasValue) + { + userData.Rating = rating.Value; + } + + if (playbackPositionTicks.HasValue) + { + userData.PlaybackPositionTicks = playbackPositionTicks.Value; + } + + if (playCount.HasValue) + { + userData.PlayCount = playCount.Value; + } + + if (lastPlayedDate.HasValue) + { + userData.LastPlayedDate = lastPlayedDate.Value; + } + + _userDataRepository.SaveUserData(user.Id, item, userData, UserDataSaveReason.UpdateUserData, CancellationToken.None); + + return _userDataRepository.GetUserDataDto(item, user); + } } diff --git a/MediaBrowser.Model/Entities/UserDataSaveReason.cs b/MediaBrowser.Model/Entities/UserDataSaveReason.cs index 20404e6f4d..b8e73a98cd 100644 --- a/MediaBrowser.Model/Entities/UserDataSaveReason.cs +++ b/MediaBrowser.Model/Entities/UserDataSaveReason.cs @@ -33,6 +33,11 @@ namespace MediaBrowser.Model.Entities /// /// The import. /// - Import = 6 + Import = 6, + + /// + /// API call updated item user data. + /// + UpdateUserData = 7, } } From 250e795c3be21586f50414efff2a9846588ac864 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Sun, 12 Nov 2023 18:12:34 +0300 Subject: [PATCH 005/132] Typo fixes. --- Jellyfin.Api/Controllers/ItemsController.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 38a7d115e5..8c816f8022 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -892,9 +892,9 @@ public class ItemsController : BaseJellyfinApiController /// /// The user id. /// The item id. - /// item user data returned. - /// When item is not found. - /// Return Item . + /// return item user data. + /// Item is not found. + /// Return . [HttpGet("Users/{userId}/Items/{itemId}/UserData")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -915,14 +915,14 @@ public class ItemsController : BaseJellyfinApiController /// The item id. /// Optional. Whether to mark the item as played. /// Optional. Whether to mark the item as favorite. - /// Optional. Whether mark the item as liked. + /// Optional. Whether to mark the item as liked. /// Optional. User item rating. - /// Optional. User Item playback Position Ticks. 1 tick = 10000 ms. - /// Item user data. + /// Optional. Item playback position ticks. 1 tick = 10000 ms. + /// Optional. How many times the user played the item. /// Optional. The date the item was played. - /// update user data returned. - /// When item is not found. - /// . + /// return updated user item data. + /// Item is not found. + /// Return . [HttpPost("Users/{userId}/Items/{itemId}/UserData")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] From 2a25c5a2e3e37e734993d17b7462598babcb0b97 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Mon, 13 Nov 2023 15:51:06 +0300 Subject: [PATCH 006/132] Refactored api call logic handling. --- .../Library/UserDataManager.cs | 37 ++++++++++++ Jellyfin.Api/Controllers/ItemsController.cs | 60 +++---------------- .../Library/IUserDataManager.cs | 9 +++ MediaBrowser.Model/Dto/UserDataDto.cs | 43 +++++++++++++ 4 files changed, 97 insertions(+), 52 deletions(-) create mode 100644 MediaBrowser.Model/Dto/UserDataDto.cs diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index a0a90b129f..0d67f2cda3 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; +using System.Reflection; using System.Threading; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; @@ -81,6 +82,42 @@ namespace Emby.Server.Implementations.Library }); } + public void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason) + { + ArgumentNullException.ThrowIfNull(user); + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(reason); + ArgumentNullException.ThrowIfNull(userDataDto); + + var userData = GetUserData(user, item); + + var parentProperties = userDataDto.GetType().GetProperties(); + var childProperties = userData.GetType().GetProperties(); + + foreach (var parentProperty in parentProperties) + { + foreach (var childProperty in childProperties) + { + if (parentProperty.Name != childProperty.Name) + { + continue; + } + + var value = parentProperty.GetValue(userDataDto, null); + + if (value is null) + { + continue; + } + + childProperty.SetValue(userData, value, null); + break; + } + } + + SaveUserData(user, item, userData, reason, CancellationToken.None); + } + /// /// Save the provided user data for the given user. Batch operation. Does not fire any events or update the cache. /// diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 8c816f8022..2a346be685 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -913,13 +913,7 @@ public class ItemsController : BaseJellyfinApiController /// /// The user id. /// The item id. - /// Optional. Whether to mark the item as played. - /// Optional. Whether to mark the item as favorite. - /// Optional. Whether to mark the item as liked. - /// Optional. User item rating. - /// Optional. Item playback position ticks. 1 tick = 10000 ms. - /// Optional. How many times the user played the item. - /// Optional. The date the item was played. + /// New user data object. /// return updated user item data. /// Item is not found. /// Return . @@ -929,14 +923,13 @@ public class ItemsController : BaseJellyfinApiController public ActionResult UpdateItemUserData( [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, - [FromQuery] bool? played, - [FromQuery] bool? favorite, - [FromQuery] bool? likes, - [FromQuery] double? rating, - [FromQuery] long? playbackPositionTicks, - [FromQuery] int? playCount, - [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? lastPlayedDate) + [FromBody, Required] UserDataDto userDataDto) { + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) + { + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data."); + } + var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException(); var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -944,44 +937,7 @@ public class ItemsController : BaseJellyfinApiController return NotFound(); } - var userData = _userDataRepository.GetUserData(user, item); - - if (played.HasValue) - { - userData.Played = played.Value; - } - - if (favorite.HasValue) - { - userData.IsFavorite = favorite.Value; - } - - if (likes.HasValue) - { - userData.Likes = likes.Value; - } - - if (rating.HasValue) - { - userData.Rating = rating.Value; - } - - if (playbackPositionTicks.HasValue) - { - userData.PlaybackPositionTicks = playbackPositionTicks.Value; - } - - if (playCount.HasValue) - { - userData.PlayCount = playCount.Value; - } - - if (lastPlayedDate.HasValue) - { - userData.LastPlayedDate = lastPlayedDate.Value; - } - - _userDataRepository.SaveUserData(user.Id, item, userData, UserDataSaveReason.UpdateUserData, CancellationToken.None); + _userDataRepository.SaveUserData(user, item, userDataDto, UserDataSaveReason.UpdateUserData); return _userDataRepository.GetUserDataDto(item, user); } diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 034c405910..8849c098fb 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -35,6 +35,15 @@ namespace MediaBrowser.Controller.Library void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken); + /// + /// Save the provided user data for the given user. + /// + /// The user. + /// The item. + /// The reason for updating the user data. + /// The reason. + void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason); + UserItemData GetUserData(User user, BaseItem item); UserItemData GetUserData(Guid userId, BaseItem item); diff --git a/MediaBrowser.Model/Dto/UserDataDto.cs b/MediaBrowser.Model/Dto/UserDataDto.cs new file mode 100644 index 0000000000..3012916f84 --- /dev/null +++ b/MediaBrowser.Model/Dto/UserDataDto.cs @@ -0,0 +1,43 @@ +#nullable disable +using System; + +namespace MediaBrowser.Model.Dto +{ + /// + /// Class UserDataDto extends UserItemDataDto to allow nullable members. + /// This change allow us to implement the new /Users/{UserId}/Items/{ItemId}/UserData endpoint. + /// This object allows the requestor to update all or specific user data fields without altering the non-nullable members state. + /// + public class UserDataDto : UserItemDataDto + { + /// + /// Gets or sets the playback position ticks. + /// + /// The playback position ticks. + public new long? PlaybackPositionTicks { get; set; } + + /// + /// Gets or sets the play count. + /// + /// The play count. + public new int? PlayCount { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is favorite. + /// + /// true if this instance is favorite; otherwise, false. + public new bool? IsFavorite { get; set; } + + /// + /// Gets or sets a value indicating whether this is likes. + /// + /// null if [likes] contains no value, true if [likes]; otherwise, false. + public new bool? Likes { get; set; } + + /// + /// Gets or sets a value indicating whether this is played. + /// + /// true if played; otherwise, false. + public new bool? Played { get; set; } + } +} From faa036aa7b696fd5091e52fd2bed6ed67ca63481 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Mon, 13 Nov 2023 15:55:12 +0300 Subject: [PATCH 007/132] Added access validation to view item user data. --- Jellyfin.Api/Controllers/ItemsController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 2a346be685..dd54e6ca78 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -902,6 +902,11 @@ public class ItemsController : BaseJellyfinApiController [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) + { + return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data."); + } + var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException(); var item = _libraryManager.GetItemById(itemId); From 07db2025a177bf5bed1cc898426a2442803ae151 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Mon, 13 Nov 2023 17:32:24 +0300 Subject: [PATCH 008/132] Refactored the code to not use reflection. --- .../Library/UserDataManager.cs | 47 ++++++++++++------- MediaBrowser.Model/Dto/UserDataDto.cs | 3 -- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 0d67f2cda3..585fdd4f09 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -91,28 +91,39 @@ namespace Emby.Server.Implementations.Library var userData = GetUserData(user, item); - var parentProperties = userDataDto.GetType().GetProperties(); - var childProperties = userData.GetType().GetProperties(); - - foreach (var parentProperty in parentProperties) + if (userDataDto.PlaybackPositionTicks.HasValue) { - foreach (var childProperty in childProperties) - { - if (parentProperty.Name != childProperty.Name) - { - continue; - } + userData.PlaybackPositionTicks = userDataDto.PlaybackPositionTicks.Value; + } - var value = parentProperty.GetValue(userDataDto, null); + if (userDataDto.PlayCount.HasValue) + { + userData.PlayCount = userDataDto.PlayCount.Value; + } - if (value is null) - { - continue; - } + if (userDataDto.IsFavorite.HasValue) + { + userData.IsFavorite = userDataDto.IsFavorite.Value; + } - childProperty.SetValue(userData, value, null); - break; - } + if (userDataDto.Likes.HasValue) + { + userData.Likes = userDataDto.Likes.Value; + } + + if (userDataDto.Played.HasValue) + { + userData.Played = userDataDto.Played.Value; + } + + if (userDataDto.LastPlayedDate.HasValue) + { + userData.LastPlayedDate = userDataDto.LastPlayedDate.Value; + } + + if (userDataDto.Rating.HasValue) + { + userData.Rating = userDataDto.Rating.Value; } SaveUserData(user, item, userData, reason, CancellationToken.None); diff --git a/MediaBrowser.Model/Dto/UserDataDto.cs b/MediaBrowser.Model/Dto/UserDataDto.cs index 3012916f84..6d8c8969b0 100644 --- a/MediaBrowser.Model/Dto/UserDataDto.cs +++ b/MediaBrowser.Model/Dto/UserDataDto.cs @@ -1,6 +1,3 @@ -#nullable disable -using System; - namespace MediaBrowser.Model.Dto { /// From c4013d2e10c2c526e21dd0229d60ab9e6c51d252 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Wed, 15 Nov 2023 13:55:14 +0300 Subject: [PATCH 009/132] Updated the summary and the Dto name. --- Emby.Server.Implementations/Library/UserDataManager.cs | 3 +-- Jellyfin.Api/Controllers/ItemsController.cs | 3 +-- MediaBrowser.Controller/Library/IUserDataManager.cs | 2 +- .../Dto/{UserDataDto.cs => UpdateUserItemDataDto.cs} | 6 ++---- 4 files changed, 5 insertions(+), 9 deletions(-) rename MediaBrowser.Model/Dto/{UserDataDto.cs => UpdateUserItemDataDto.cs} (78%) diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 585fdd4f09..8beeb8041f 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; -using System.Reflection; using System.Threading; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; @@ -82,7 +81,7 @@ namespace Emby.Server.Implementations.Library }); } - public void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason) + public void SaveUserData(User user, BaseItem item, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason) { ArgumentNullException.ThrowIfNull(user); ArgumentNullException.ThrowIfNull(item); diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index dd54e6ca78..ae80d15e60 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Threading; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; @@ -928,7 +927,7 @@ public class ItemsController : BaseJellyfinApiController public ActionResult UpdateItemUserData( [FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, - [FromBody, Required] UserDataDto userDataDto) + [FromBody, Required] UpdateUserItemDataDto userDataDto) { if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 8849c098fb..43cccfc65c 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Library /// The item. /// The reason for updating the user data. /// The reason. - void SaveUserData(User user, BaseItem item, UserDataDto userDataDto, UserDataSaveReason reason); + void SaveUserData(User user, BaseItem item, UpdateUserItemDataDto userDataDto, UserDataSaveReason reason); UserItemData GetUserData(User user, BaseItem item); diff --git a/MediaBrowser.Model/Dto/UserDataDto.cs b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs similarity index 78% rename from MediaBrowser.Model/Dto/UserDataDto.cs rename to MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs index 6d8c8969b0..9ff09cb22c 100644 --- a/MediaBrowser.Model/Dto/UserDataDto.cs +++ b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs @@ -1,11 +1,9 @@ namespace MediaBrowser.Model.Dto { /// - /// Class UserDataDto extends UserItemDataDto to allow nullable members. - /// This change allow us to implement the new /Users/{UserId}/Items/{ItemId}/UserData endpoint. - /// This object allows the requestor to update all or specific user data fields without altering the non-nullable members state. + /// This is used by the api to get information about a item user data. /// - public class UserDataDto : UserItemDataDto + public class UpdateUserItemDataDto : UserItemDataDto { /// /// Gets or sets the playback position ticks. From 22c90141e7eaed43be399df06354f993636c64a3 Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Thu, 23 Nov 2023 12:14:31 +0300 Subject: [PATCH 010/132] Made UpdateUserItemDataDto standalone object. --- .../Dto/UpdateUserItemDataDto.cs | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs index 9ff09cb22c..5c89c2470d 100644 --- a/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs +++ b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs @@ -1,38 +1,77 @@ +#nullable disable +using System; + namespace MediaBrowser.Model.Dto { /// /// This is used by the api to get information about a item user data. /// - public class UpdateUserItemDataDto : UserItemDataDto + public class UpdateUserItemDataDto { + /// + /// Gets or sets the rating. + /// + /// The rating. + public double? Rating { get; set; } + + /// + /// Gets or sets the played percentage. + /// + /// The played percentage. + public double? PlayedPercentage { get; set; } + + /// + /// Gets or sets the unplayed item count. + /// + /// The unplayed item count. + public int? UnplayedItemCount { get; set; } + /// /// Gets or sets the playback position ticks. /// /// The playback position ticks. - public new long? PlaybackPositionTicks { get; set; } + public long? PlaybackPositionTicks { get; set; } /// /// Gets or sets the play count. /// /// The play count. - public new int? PlayCount { get; set; } + public int? PlayCount { get; set; } /// /// Gets or sets a value indicating whether this instance is favorite. /// /// true if this instance is favorite; otherwise, false. - public new bool? IsFavorite { get; set; } + public bool? IsFavorite { get; set; } /// - /// Gets or sets a value indicating whether this is likes. + /// Gets or sets a value indicating whether this is likes. /// /// null if [likes] contains no value, true if [likes]; otherwise, false. - public new bool? Likes { get; set; } + public bool? Likes { get; set; } + + /// + /// Gets or sets the last played date. + /// + /// The last played date. + public DateTime? LastPlayedDate { get; set; } /// /// Gets or sets a value indicating whether this is played. /// /// true if played; otherwise, false. - public new bool? Played { get; set; } + public bool? Played { get; set; } + + /// + /// Gets or sets the key. + /// + /// The key. + public string Key { get; set; } + + /// + /// Gets or sets the item identifier. + /// + /// The item identifier. + public string ItemId { get; set; } } } From 324c86e14d9d76670b4e3439c2ed5fcbbfb9f8ff Mon Sep 17 00:00:00 2001 From: ArabCoders Date: Thu, 23 Nov 2023 14:37:37 +0300 Subject: [PATCH 011/132] Made key & itemId nullable --- MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs index 5c89c2470d..7bfedf9735 100644 --- a/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs +++ b/MediaBrowser.Model/Dto/UpdateUserItemDataDto.cs @@ -1,4 +1,3 @@ -#nullable disable using System; namespace MediaBrowser.Model.Dto @@ -66,12 +65,12 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the key. /// /// The key. - public string Key { get; set; } + public string? Key { get; set; } /// /// Gets or sets the item identifier. /// /// The item identifier. - public string ItemId { get; set; } + public string? ItemId { get; set; } } } From 59ed6ab8ea1c97938fd832618d916719c27887a0 Mon Sep 17 00:00:00 2001 From: joncrall Date: Sun, 3 Dec 2023 15:58:26 -0500 Subject: [PATCH 012/132] Quote bash paths to prevent word splitting --- deployment/build.centos.amd64 | 8 ++++---- deployment/build.debian.amd64 | 8 ++++---- deployment/build.debian.arm64 | 8 ++++---- deployment/build.debian.armhf | 8 ++++---- deployment/build.fedora.amd64 | 8 ++++---- deployment/build.linux.amd64 | 8 ++++---- deployment/build.linux.amd64-musl | 8 ++++---- deployment/build.linux.arm64 | 8 ++++---- deployment/build.linux.armhf | 8 ++++---- deployment/build.linux.musl-linux-arm64 | 8 ++++---- deployment/build.macos.amd64 | 8 ++++---- deployment/build.macos.arm64 | 8 ++++---- deployment/build.portable | 8 ++++---- deployment/build.ubuntu.amd64 | 8 ++++---- deployment/build.ubuntu.arm64 | 8 ++++---- deployment/build.ubuntu.armhf | 8 ++++---- deployment/build.windows.amd64 | 8 ++++---- 17 files changed, 68 insertions(+), 68 deletions(-) diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64 index 0374624d80..a0ab93e4e0 100755 --- a/deployment/build.centos.amd64 +++ b/deployment/build.centos.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove BuildRequires for dotnet, since it's installed manually @@ -39,10 +39,10 @@ make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm # Move the artifacts out -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi rm -f fedora/jellyfin*.tar.gz @@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then pushd fedora cp -a /tmp/spec.orig jellyfin.spec - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" popd fi diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 7e968192bb..1a59d02e91 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -32,12 +32,12 @@ fi # Build DEB dpkg-buildpackage -us -uc --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index 7b7b603d63..e1e30fab4a 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -33,12 +33,12 @@ fi export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index 3d894ba20e..e3e8ae004e 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -33,12 +33,12 @@ fi export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64 index 1b629289f0..da345ec08a 100755 --- a/deployment/build.fedora.amd64 +++ b/deployment/build.fedora.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove BuildRequires for dotnet, since it's installed manually @@ -39,10 +39,10 @@ make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm # Move the artifacts out -mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/ +mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi rm -f fedora/jellyfin*.tar.gz @@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then pushd fedora cp -a /tmp/spec.orig jellyfin.spec - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" popd fi diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64 index 05059e4edf..c6baa61f6e 100755 --- a/deployment/build.linux.amd64 +++ b/deployment/build.linux.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_$ rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.amd64-musl b/deployment/build.linux.amd64-musl index 0ee4b05fb3..6523f8319a 100755 --- a/deployment/build.linux.amd64-musl +++ b/deployment/build.linux.amd64-musl @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_linux-amd64-musl.tar.gz -C dist jellyfin-ser rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.arm64 b/deployment/build.linux.arm64 index 6e36db0eb8..6d6a8f803a 100755 --- a/deployment/build.linux.arm64 +++ b/deployment/build.linux.arm64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_linux-arm64.tar.gz -C dist jellyfin-server_$ rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.armhf b/deployment/build.linux.armhf index f83eeebf1c..5167dfcb8d 100755 --- a/deployment/build.linux.armhf +++ b/deployment/build.linux.armhf @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_linux-armhf.tar.gz -C dist jellyfin-server_$ rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.musl-linux-arm64 b/deployment/build.linux.musl-linux-arm64 index 38826ae7fc..57980314d4 100755 --- a/deployment/build.linux.musl-linux-arm64 +++ b/deployment/build.linux.musl-linux-arm64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_linux-arm64-musl.tar.gz -C dist jellyfin-ser rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.macos.amd64 b/deployment/build.macos.amd64 index eac3538771..c7711e82c7 100755 --- a/deployment/build.macos.amd64 +++ b/deployment/build.macos.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_$ rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.macos.arm64 b/deployment/build.macos.arm64 index 42da07e2f6..b07eaad4e0 100755 --- a/deployment/build.macos.arm64 +++ b/deployment/build.macos.arm64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_macos-arm64.tar.gz -C dist jellyfin-server_$ rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.portable b/deployment/build.portable index 27e5e987fe..ec151d2956 100755 --- a/deployment/build.portable +++ b/deployment/build.portable @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -21,11 +21,11 @@ tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${ve rm -rf dist/jellyfin-server_${version} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 5f25cb610f..17968a6e93 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -32,12 +32,12 @@ fi # Build DEB dpkg-buildpackage -us -uc --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index 334ced9970..ee7da9bb98 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -33,12 +33,12 @@ fi export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a arm64 --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 77e33c3071..85c993282e 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -6,7 +6,7 @@ set -o errexit set -o xtrace # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove build-dep for dotnet-sdk-8.0, since it's installed manually @@ -33,12 +33,12 @@ fi export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} dpkg-buildpackage -us -uc -a armhf --pre-clean --post-clean -mkdir -p ${ARTIFACT_DIR}/ -mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64 index 0786358bd5..20f976365d 100755 --- a/deployment/build.windows.amd64 +++ b/deployment/build.windows.amd64 @@ -11,7 +11,7 @@ NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" FFMPEG_URL="https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg-portable_win64.zip"; # Move to source directory -pushd ${SOURCE_DIR} +pushd "${SOURCE_DIR}" # Get version if [[ ${IS_UNSTABLE} == 'yes' ]]; then @@ -42,11 +42,11 @@ popd rm -rf ${output_dir} # Move the artifacts out -mkdir -p ${ARTIFACT_DIR}/ -mv dist/jellyfin[-_]*.zip ${ARTIFACT_DIR}/ +mkdir -p "${ARTIFACT_DIR}/" +mv dist/jellyfin[-_]*.zip "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} + chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" fi popd From 67b4cef77a4b391d5dcf0769d0c70762128ddfd1 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 11:59:51 -0500 Subject: [PATCH 013/132] Use implementation types in LiveTv --- Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs | 4 ++-- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 2 +- Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 5369c9b3d1..b548a92b19 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -34,7 +34,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IServerConfigurationManager _serverConfigurationManager; private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private bool _hasExited; - private Stream _logFileStream; + private FileStream _logFileStream; private string _targetPath; private Process _process; private bool _disposed = false; @@ -308,7 +308,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private async Task StartStreamingLog(Stream source, Stream target) + private async Task StartStreamingLog(Stream source, FileStream target) { try { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index dd427c7368..dcd1e8613c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1101,7 +1101,7 @@ namespace Emby.Server.Implementations.LiveTv progress.Report(100); } - private async Task, List>> RefreshChannelsInternal(ILiveTvService service, IProgress progress, CancellationToken cancellationToken) + private async Task, List>> RefreshChannelsInternal(ILiveTvService service, ActionableProgress progress, CancellationToken cancellationToken) { progress.Report(10); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 767b941366..c0ba8710ff 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -134,7 +134,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } } - private void TrySeek(Stream stream, long offset) + private void TrySeek(FileStream stream, long offset) { if (!stream.CanSeek) { From f1a5bc955b6a182aaa19a80831560d43c50ede2a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:01:59 -0500 Subject: [PATCH 014/132] Use string.Contains in LiveTv --- .../LiveTv/Listings/SchedulesDirect.cs | 4 ++-- .../LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 6b0520ad0f..5be3a7488a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsMovie = IsMovie(details), Etag = programInfo.Md5, IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase), - IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1 + IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).Contains("premiere", StringComparison.OrdinalIgnoreCase) }; var showId = programId; @@ -414,7 +414,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings return null; } - if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1) + if (uri.Contains("http", StringComparison.OrdinalIgnoreCase)) { return uri; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs index 42068cd340..39b3571422 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var model = ModelNumber ?? string.Empty; - if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1) + if (model.Contains("hdtc", StringComparison.OrdinalIgnoreCase)) { return true; } From 01480c7f209654a992be6c3d3ad360e9bd343090 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:03:53 -0500 Subject: [PATCH 015/132] Fix disposable warnings in EmbyTV --- .../LiveTv/EmbyTV/EmbyTV.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 74b62ca3f2..cba24ca459 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -37,12 +37,11 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV { - public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable + public sealed class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable { public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; @@ -74,7 +73,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1); - private bool _disposed = false; + private bool _disposed; public EmbyTV( IServerApplicationHost appHost, @@ -2524,22 +2523,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV /// public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { if (_disposed) { return; } - if (disposing) - { - _recordingDeleteSemaphore.Dispose(); - } + _recordingDeleteSemaphore.Dispose(); foreach (var pair in _activeRecordings.ToList()) { From ce6c0ad02bc77b5a43ba2f28d4a93813738d0c53 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:06:30 -0500 Subject: [PATCH 016/132] Use ConfigureAwait in DirectRecorder --- .../LiveTv/EmbyTV/DirectRecorder.cs | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 49833de737..ec4dbde30d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -46,7 +46,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile))); - await using (var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) + var output = new FileStream( + targetFile, + FileMode.CreateNew, + FileAccess.Write, + FileShare.Read, + IODefaults.FileStreamBufferSize, + FileOptions.Asynchronous); + + await using (output.ConfigureAwait(false)) { onStarted(); @@ -80,24 +88,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile))); - await using var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + await using (output.ConfigureAwait(false)) + { + onStarted(); - onStarted(); + _logger.LogInformation("Copying recording stream to file {0}", targetFile); - _logger.LogInformation("Copying recording stream to file {0}", targetFile); + // The media source if infinite so we need to handle stopping ourselves + using var durationToken = new CancellationTokenSource(duration); + using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token); + cancellationToken = linkedCancellationToken.Token; - // The media source if infinite so we need to handle stopping ourselves - using var durationToken = new CancellationTokenSource(duration); - using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token); - cancellationToken = linkedCancellationToken.Token; + await _streamHelper.CopyUntilCancelled( + await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false), + output, + IODefaults.CopyToBufferSize, + cancellationToken).ConfigureAwait(false); - await _streamHelper.CopyUntilCancelled( - await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false), - output, - IODefaults.CopyToBufferSize, - cancellationToken).ConfigureAwait(false); - - _logger.LogInformation("Recording completed to file {0}", targetFile); + _logger.LogInformation("Recording completed to file {0}", targetFile); + } } } } From 7bcfc5e925295d386a8bc70b47200254ae731813 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:22:23 -0500 Subject: [PATCH 017/132] Use ConfigureAwait in XmlTvListingsProvider --- .../LiveTv/Listings/XmlTvListingsProvider.cs | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 066afb956b..e60e9dcc1c 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -84,38 +84,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); 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); + var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + await using (stream.ConfigureAwait(false)) + { + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); + } } else { - await using var stream = AsyncFile.OpenRead(info.Path); - return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); + var stream = AsyncFile.OpenRead(info.Path); + await using (stream.ConfigureAwait(false)) + { + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); + } } } private async Task UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) { - await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); + var fileStream = new FileStream( + file, + FileMode.CreateNew, + FileAccess.Write, + FileShare.None, + IODefaults.FileStreamBufferSize, + FileOptions.Asynchronous); - if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) + await using (fileStream.ConfigureAwait(false)) { - try + if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) { - using var reader = new GZipStream(stream, CompressionMode.Decompress); - await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); + try + { + using var reader = new GZipStream(stream, CompressionMode.Decompress); + await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl); + } } - catch (Exception ex) + else { - _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl); + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } - } - else - { - await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); - } - return file; + return file; + } } public async Task> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) From 42c95da6b08a5295e1d2f98922158d0702fd8d7e Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:24:31 -0500 Subject: [PATCH 018/132] Use ConfigureAwait in BaseTunerHost --- .../LiveTv/TunerHosts/BaseTunerHost.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index ff25ee5854..a4e45f4796 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -96,8 +96,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { Directory.CreateDirectory(Path.GetDirectoryName(channelCacheFile)); - await using var writeStream = AsyncFile.OpenWrite(channelCacheFile); - await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false); + var writeStream = AsyncFile.OpenWrite(channelCacheFile); + await using (writeStream.ConfigureAwait(false)) + { + await JsonSerializer.SerializeAsync(writeStream, channels, cancellationToken: cancellationToken).ConfigureAwait(false); + } } catch (IOException) { @@ -112,10 +115,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { try { - await using var readStream = AsyncFile.OpenRead(channelCacheFile); - var channels = await JsonSerializer.DeserializeAsync>(readStream, cancellationToken: cancellationToken) - .ConfigureAwait(false); - list.AddRange(channels); + var readStream = AsyncFile.OpenRead(channelCacheFile); + await using (readStream.ConfigureAwait(false)) + { + var channels = await JsonSerializer + .DeserializeAsync>(readStream, cancellationToken: cancellationToken) + .ConfigureAwait(false); + list.AddRange(channels); + } } catch (IOException) { From 4ec32b71f5fc765886e457ae2c626ea7227e6a37 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:26:29 -0500 Subject: [PATCH 019/132] Use ConfigureAwait in M3uParser --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 341782d9d3..0b5575b994 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -66,7 +66,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts .ConfigureAwait(false); response.EnsureSuccessStatusCode(); - return await response.Content.ReadAsStreamAsync(cancellationToken); + return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); } private async Task> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId) From 3ecd68d832b1849b3bebd052933ce8ad7612bad7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:28:17 -0500 Subject: [PATCH 020/132] Use ConfigureAwait in SharedHttpStream --- .../LiveTv/TunerHosts/SharedHttpStream.cs | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 51f46f4dac..efb84a5152 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -83,14 +83,27 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogInformation("Beginning {StreamType} stream to {FilePath}", GetType().Name, TempFilePath); using (response) { - await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - await StreamHelper.CopyToAsync( - stream, - fileStream, - IODefaults.CopyToBufferSize, - () => Resolve(openTaskCompletionSource), - cancellationToken).ConfigureAwait(false); + var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + await using (stream.ConfigureAwait(false)) + { + var fileStream = new FileStream( + TempFilePath, + FileMode.Create, + FileAccess.Write, + FileShare.Read, + IODefaults.FileStreamBufferSize, + FileOptions.Asynchronous); + + await using (fileStream.ConfigureAwait(false)) + { + await StreamHelper.CopyToAsync( + stream, + fileStream, + IODefaults.CopyToBufferSize, + () => Resolve(openTaskCompletionSource), + cancellationToken).ConfigureAwait(false); + } + } } } catch (OperationCanceledException ex) From 5c593b120a5f3b90daa6305a96086eecfe546039 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:36:24 -0500 Subject: [PATCH 021/132] Don't use List directly in ITunerHost method signatures --- .../LiveTv/TunerHosts/BaseTunerHost.cs | 6 +++--- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index a4e45f4796..da597056a4 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return list; } - protected virtual List GetTunerHosts() + protected virtual IList GetTunerHosts() { return GetConfiguration().TunerHosts .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) @@ -166,9 +166,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return new List(); } - protected abstract Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List currentLiveStreams, CancellationToken cancellationToken); + protected abstract Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, IList currentLiveStreams, CancellationToken cancellationToken); - public async Task GetChannelStream(string channelId, string streamId, List currentLiveStreams, CancellationToken cancellationToken) + public async Task GetChannelStream(string channelId, string streamId, IList currentLiveStreams, CancellationToken cancellationToken) { ArgumentException.ThrowIfNullOrEmpty(channelId); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 8cd0c4ffb7..79e15a82e2 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -527,7 +527,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return list; } - protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List currentLiveStreams, CancellationToken cancellationToken) + protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, IList currentLiveStreams, CancellationToken cancellationToken) { var tunerCount = tunerHost.TunerCount; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index db5e81df5f..11bf03b182 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(list); } - protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, List currentLiveStreams, CancellationToken cancellationToken) + protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, IList currentLiveStreams, CancellationToken cancellationToken) { var tunerCount = tunerHost.TunerCount; diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 24820abb90..b983091588 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Controller.LiveTv /// The current live streams. /// The cancellation token to cancel operation. /// Live stream wrapped in a task. - Task GetChannelStream(string channelId, string streamId, List currentLiveStreams, CancellationToken cancellationToken); + Task GetChannelStream(string channelId, string streamId, IList currentLiveStreams, CancellationToken cancellationToken); /// /// Gets the channel stream media sources. From 669baf98a577094c5b038c7782045b873d468c0b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 12:41:27 -0500 Subject: [PATCH 022/132] Make IRecorder an IDisposable --- .../LiveTv/EmbyTV/DirectRecorder.cs | 7 ++++++- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 4 ++-- Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index ec4dbde30d..ddf7b882a7 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV { - public class DirectRecorder : IRecorder + public sealed class DirectRecorder : IRecorder { private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; @@ -109,5 +109,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _logger.LogInformation("Recording completed to file {0}", targetFile); } } + + /// + public void Dispose() + { + } } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index cba24ca459..abe3ff349f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1269,7 +1269,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV directStreamProvider = liveStreamResponse.Item2; } - var recorder = GetRecorder(mediaStreamInfo); + using var recorder = GetRecorder(mediaStreamInfo); recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = EnsureFileUnique(recordPath, timer.Id); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index b548a92b19..9a9fd02739 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -25,7 +25,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV { - public class EncodedRecorder : IRecorder, IDisposable + public class EncodedRecorder : IRecorder { private readonly ILogger _logger; private readonly IMediaEncoder _mediaEncoder; @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private FileStream _logFileStream; private string _targetPath; private Process _process; - private bool _disposed = false; + private bool _disposed; public EncodedRecorder( ILogger logger, diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs index 7705132da2..de14d6d086 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs @@ -8,7 +8,7 @@ using MediaBrowser.Model.Dto; namespace Emby.Server.Implementations.LiveTv.EmbyTV { - public interface IRecorder + public interface IRecorder : IDisposable { /// /// Records the specified media source. From 192559db32ef60b2a56b7acf689b6edc3cdc3487 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 5 Dec 2023 14:26:35 -0500 Subject: [PATCH 023/132] Make ILiveStream an IDisposable --- .../Library/ExclusiveLiveStream.cs | 7 ++++++- .../LiveTv/TunerHosts/LiveStream.cs | 15 +++++++++++++++ MediaBrowser.Controller/Library/ILiveStream.cs | 3 ++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs index 868071a992..b1649afad5 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs @@ -12,7 +12,7 @@ using MediaBrowser.Model.Dto; namespace Emby.Server.Implementations.Library { - public class ExclusiveLiveStream : ILiveStream + public sealed class ExclusiveLiveStream : ILiveStream { private readonly Func _closeFn; @@ -51,5 +51,10 @@ namespace Emby.Server.Implementations.Library { return Task.CompletedTask; } + + /// + public void Dispose() + { + } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index c0ba8710ff..c18594a29b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -112,6 +112,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return stream; } + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + LiveStreamCancellationTokenSource?.Dispose(); + } + } + protected async Task DeleteTempFiles(string path, int retryCount = 0) { if (retryCount == 0) diff --git a/MediaBrowser.Controller/Library/ILiveStream.cs b/MediaBrowser.Controller/Library/ILiveStream.cs index 4c44a17fdd..bf64aca0f0 100644 --- a/MediaBrowser.Controller/Library/ILiveStream.cs +++ b/MediaBrowser.Controller/Library/ILiveStream.cs @@ -2,6 +2,7 @@ #pragma warning disable CA1711, CS1591 +using System; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -9,7 +10,7 @@ using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Library { - public interface ILiveStream + public interface ILiveStream : IDisposable { int ConsumerCount { get; set; } From 3e5e600dc76efcaf776f15776112f6cf57356e8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:27:03 +0000 Subject: [PATCH 024/132] chore(deps): update github/codeql-action action to v2.22.9 --- .github/workflows/ci-codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml index b525966453..b35a79efc6 100644 --- a/.github/workflows/ci-codeql-analysis.yml +++ b/.github/workflows/ci-codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '8.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/init@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/autobuild@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75 # v2.22.8 + uses: github/codeql-action/analyze@c0d1daa7f7e14667747d73a7dbbe8c074bc8bfe2 # v2.22.9 From 8abc653c296b4f754b78a57a027bb2becf82b580 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:27:09 +0000 Subject: [PATCH 025/132] chore(deps): update actions/stale action to v9 --- .github/workflows/issue-stale.yml | 2 +- .github/workflows/pull-request-stale.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issue-stale.yml b/.github/workflows/issue-stale.yml index 926a7fbfb0..5a1ca9f7a2 100644 --- a/.github/workflows/issue-stale.yml +++ b/.github/workflows/issue-stale.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest if: ${{ contains(github.repository, 'jellyfin/') }} steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: repo-token: ${{ secrets.JF_BOT_TOKEN }} ascending: true diff --git a/.github/workflows/pull-request-stale.yaml b/.github/workflows/pull-request-stale.yaml index de093a9887..d01b3f4a1f 100644 --- a/.github/workflows/pull-request-stale.yaml +++ b/.github/workflows/pull-request-stale.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest if: ${{ contains(github.repository, 'jellyfin/') }} steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: repo-token: ${{ secrets.JF_BOT_TOKEN }} ascending: true From 033cfa59c499e5adbb949b708f06e56bf8932401 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 8 Dec 2023 15:45:36 -0700 Subject: [PATCH 026/132] Convert CollectionType to use lowercase enum names --- .../Images/CollectionFolderImageProvider.cs | 18 ++--- .../Images/DynamicImageProvider.cs | 8 +-- .../Library/LibraryManager.cs | 4 +- .../Library/Resolvers/Audio/AudioResolver.cs | 6 +- .../Resolvers/Audio/MusicAlbumResolver.cs | 2 +- .../Resolvers/Audio/MusicArtistResolver.cs | 2 +- .../Library/Resolvers/Books/BookResolver.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 34 +++++----- .../Library/Resolvers/PhotoAlbumResolver.cs | 4 +- .../Library/Resolvers/PhotoResolver.cs | 4 +- .../Library/Resolvers/PlaylistResolver.cs | 2 +- .../Library/Resolvers/TV/EpisodeResolver.cs | 2 +- .../Library/Resolvers/TV/SeriesResolver.cs | 4 +- .../Library/UserViewManager.cs | 22 +++--- .../LiveTv/LiveTvManager.cs | 2 +- .../Playlists/PlaylistsFolder.cs | 2 +- Jellyfin.Api/Controllers/GenresController.cs | 4 +- .../Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 2 +- Jellyfin.Api/Controllers/LibraryController.cs | 18 ++--- Jellyfin.Data/Enums/CollectionType.cs | 57 ++++++++-------- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.Controller/Entities/UserView.cs | 18 ++--- .../Entities/UserViewBuilder.cs | 68 +++++++++---------- .../Library/AudioResolverTests.cs | 2 +- .../Library/EpisodeResolverTest.cs | 4 +- 26 files changed, 148 insertions(+), 147 deletions(-) diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs index 6e8f77977e..34c722e41d 100644 --- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs +++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs @@ -32,26 +32,26 @@ namespace Emby.Server.Implementations.Images switch (viewType) { - case CollectionType.Movies: + case CollectionType.movies: includeItemTypes = new[] { BaseItemKind.Movie }; break; - case CollectionType.TvShows: + case CollectionType.tvshows: includeItemTypes = new[] { BaseItemKind.Series }; break; - case CollectionType.Music: + case CollectionType.music: includeItemTypes = new[] { BaseItemKind.MusicAlbum }; break; - case CollectionType.MusicVideos: + case CollectionType.musicvideos: includeItemTypes = new[] { BaseItemKind.MusicVideo }; break; - case CollectionType.Books: + case CollectionType.books: includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook }; break; - case CollectionType.BoxSets: + case CollectionType.boxsets: includeItemTypes = new[] { BaseItemKind.BoxSet }; break; - case CollectionType.HomeVideos: - case CollectionType.Photos: + case CollectionType.homevideos: + case CollectionType.photos: includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Photo }; break; default: @@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Images break; } - var recursive = viewType != CollectionType.Playlists; + var recursive = viewType != CollectionType.playlists; return view.GetItemList(new InternalItemsQuery { diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs index 5de53df739..6b2ae23b3c 100644 --- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs @@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Images var view = (UserView)item; var isUsingCollectionStrip = IsUsingCollectionStrip(view); - var recursive = isUsingCollectionStrip && view?.ViewType is not null && view.ViewType != CollectionType.BoxSets && view.ViewType != CollectionType.Playlists; + var recursive = isUsingCollectionStrip && view?.ViewType is not null && view.ViewType != CollectionType.boxsets && view.ViewType != CollectionType.playlists; var result = view.GetItemList(new InternalItemsQuery { @@ -114,9 +114,9 @@ namespace Emby.Server.Implementations.Images { CollectionType[] collectionStripViewTypes = { - CollectionType.Movies, - CollectionType.TvShows, - CollectionType.Playlists + CollectionType.movies, + CollectionType.tvshows, + CollectionType.playlists }; return view?.ViewType is not null && collectionStripViewTypes.Contains(view.ViewType.Value); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f40177fa77..a79ffd9cb1 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1514,7 +1514,7 @@ namespace Emby.Server.Implementations.Library { if (item is UserView view) { - if (view.ViewType == CollectionType.LiveTv) + if (view.ViewType == CollectionType.livetv) { return new[] { view.Id }; } @@ -1543,7 +1543,7 @@ namespace Emby.Server.Implementations.Library } // Handle grouping - if (user is not null && view.ViewType != CollectionType.Unknown && UserView.IsEligibleForGrouping(view.ViewType) + if (user is not null && view.ViewType != CollectionType.unknown && UserView.IsEligibleForGrouping(view.ViewType) && user.GetPreference(PreferenceKind.GroupedFolders).Length > 0) { return GetUserRootFolder() diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index ac423ed091..dbf05c1dbb 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio List files, CollectionType? collectionType) { - if (collectionType == CollectionType.Books) + if (collectionType == CollectionType.books) { return ResolveMultipleAudio(parent, files, true); } @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var collectionType = args.GetCollectionType(); - var isBooksCollectionType = collectionType == CollectionType.Books; + var isBooksCollectionType = collectionType == CollectionType.books; if (args.IsDirectory) { @@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio MediaBrowser.Controller.Entities.Audio.Audio item = null; - var isMusicCollectionType = collectionType == CollectionType.Music; + var isMusicCollectionType = collectionType == CollectionType.music; // Use regular audio type for mixed libraries, owned items and music if (isMixedCollectionType || diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 06e292f4cf..0bfb7fbe6a 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -55,7 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio protected override MusicAlbum Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); - var isMusicMediaFolder = collectionType == CollectionType.Music; + var isMusicMediaFolder = collectionType == CollectionType.music; // If there's a collection type and it's not music, don't allow it. if (!isMusicMediaFolder) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 7d6f97b121..1bdae7f62b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var collectionType = args.GetCollectionType(); - var isMusicMediaFolder = collectionType == CollectionType.Music; + var isMusicMediaFolder = collectionType == CollectionType.music; // If there's a collection type and it's not music, it can't be a music artist if (!isMusicMediaFolder) diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index b76bfe4274..464a548ab9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -23,7 +23,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books var collectionType = args.GetCollectionType(); // Only process items that are in a collection folder containing books - if (collectionType != CollectionType.Books) + if (collectionType != CollectionType.books) { return null; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 50fd8b8779..1a210e3cc8 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -31,11 +31,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies private static readonly CollectionType[] _validCollectionTypes = new[] { - CollectionType.Movies, - CollectionType.HomeVideos, - CollectionType.MusicVideos, - CollectionType.TvShows, - CollectionType.Photos + CollectionType.movies, + CollectionType.homevideos, + CollectionType.musicvideos, + CollectionType.tvshows, + CollectionType.photos }; /// @@ -100,12 +100,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies Video movie = null; var files = args.GetActualFileSystemChildren().ToList(); - if (collectionType == CollectionType.MusicVideos) + if (collectionType == CollectionType.musicvideos) { movie = FindMovie(args, args.Path, args.Parent, files, DirectoryService, collectionType, false); } - if (collectionType == CollectionType.HomeVideos) + if (collectionType == CollectionType.homevideos) { movie = FindMovie public class PluginConfiguration : BasePluginConfiguration { + /// + /// Gets or sets a value to use as the API key for accessing TMDb. This is intentionally excluded from the + /// settings page as the API key should not need to be changed by most users. + /// + public string TmdbApiKey { get; set; } = TmdbUtils.ApiKey; + /// /// Gets or sets a value indicating whether include adult content when searching with TMDb. /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html index cd21516f95..f3c24e7b42 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html @@ -64,9 +64,18 @@ var clientConfig, pluginConfig; var configureImageScaling = function() { - if (clientConfig === null || pluginConfig === null) { + if (clientConfig === undefined || pluginConfig === undefined) { return; } + if (Object.keys(clientConfig).length === 0) { + clientConfig = { + PosterSizes: [pluginConfig.PosterSize], + BackdropSizes: [pluginConfig.BackdropSize], + LogoSizes: [pluginConfig.LogoSize], + ProfileSizes: [pluginConfig.ProfileSize], + StillSizes: [pluginConfig.StillSize] + }; + } var sizeOptionsGenerator = function (size) { return ''; @@ -104,6 +113,15 @@ ApiClient.fetch(request).then(function (config) { clientConfig = config; configureImageScaling(); + }, function (error) { + error.text().then(function (contents) { + Dashboard.alert({ + title: error.statusText, + message: contents + }); + clientConfig = {}; + configureImageScaling(); + }); }); ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 72e59c9ac4..1402104334 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public TmdbClientManager(IMemoryCache memoryCache) { _memoryCache = memoryCache; - _tmDbClient = new TMDbClient(TmdbUtils.ApiKey); + _tmDbClient = new TMDbClient(Plugin.Instance.Configuration.TmdbApiKey); // Not really interested in NotFoundException _tmDbClient.ThrowApiExceptions = false; } From f7479bc7301391d1aed9b2cb1104e501087bb6d6 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sat, 16 Dec 2023 23:28:50 -0500 Subject: [PATCH 039/132] Fix missing posters in collection identify --- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 8 +++++--- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index c2018d820e..c76c65591f 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -75,12 +75,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets var collections = new RemoteSearchResult[collectionSearchResults.Count]; for (var i = 0; i < collectionSearchResults.Count; i++) { + var result = collectionSearchResults[i]; var collection = new RemoteSearchResult { - Name = collectionSearchResults[i].Name, - SearchProviderName = Name + Name = result.Name, + SearchProviderName = Name, + ImageUrl = _tmdbClientManager.GetPosterUrl(result.PosterPath) }; - collection.SetProviderId(MetadataProvider.Tmdb, collectionSearchResults[i].Id.ToString(CultureInfo.InvariantCulture)); + collection.SetProviderId(MetadataProvider.Tmdb, result.Id.ToString(CultureInfo.InvariantCulture)); collections[i] = collection; } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 2f62e117eb..dac7a74ed8 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -299,7 +299,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (!string.IsNullOrWhiteSpace(person.ProfilePath)) { - personInfo.ImageUrl = _tmdbClientManager.GetPosterUrl(person.ProfilePath); + personInfo.ImageUrl = _tmdbClientManager.GetProfileUrl(person.ProfilePath); } if (person.Id > 0) From 49cc4d1dabf23602d775f194b75402d65e0722b8 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sun, 17 Dec 2023 00:12:11 -0500 Subject: [PATCH 040/132] Add full version tags for consistency with other files --- .github/workflows/ci-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 323d920453..0dacbc5c61 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -19,9 +19,9 @@ jobs: runs-on: "${{ matrix.os }}" steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4 + - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 with: dotnet-version: ${{ env.SDK_VERSION }} @@ -34,7 +34,7 @@ jobs: --verbosity minimal - name: Merge code coverage results - uses: danielpalme/ReportGenerator-GitHub-Action@4d510cbed8a05af5aefea46c7fd6e05b95844c89 # 5 + uses: danielpalme/ReportGenerator-GitHub-Action@4d510cbed8a05af5aefea46c7fd6e05b95844c89 # 5.2.0 with: reports: "**/coverage.cobertura.xml" targetdir: "merged/" From e91de654d78c847d482fb9ca315eca31d044d770 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sun, 17 Dec 2023 22:14:11 -0500 Subject: [PATCH 041/132] Stop saving Jellyfin API key in settings xml --- .../Plugins/Tmdb/Configuration/PluginConfiguration.cs | 2 +- MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs index e9cd81a14e..99b759ae29 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// Gets or sets a value to use as the API key for accessing TMDb. This is intentionally excluded from the /// settings page as the API key should not need to be changed by most users. /// - public string TmdbApiKey { get; set; } = TmdbUtils.ApiKey; + public string TmdbApiKey { get; set; } = string.Empty; /// /// Gets or sets a value indicating whether include adult content when searching with TMDb. diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 1402104334..82f2c54f16 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -36,7 +36,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public TmdbClientManager(IMemoryCache memoryCache) { _memoryCache = memoryCache; - _tmDbClient = new TMDbClient(Plugin.Instance.Configuration.TmdbApiKey); + + var apiKey = Plugin.Instance.Configuration.TmdbApiKey; + apiKey = string.IsNullOrEmpty(apiKey) ? TmdbUtils.ApiKey : apiKey; + _tmDbClient = new TMDbClient(apiKey); + // Not really interested in NotFoundException _tmDbClient.ThrowApiExceptions = false; } From 660165cd2ff6288061a90c968381eb014ec3b7fd Mon Sep 17 00:00:00 2001 From: Lars Eichler Date: Mon, 18 Dec 2023 10:46:09 -0500 Subject: [PATCH 042/132] Added translation using Weblate (Abkhazian) --- Emby.Server.Implementations/Localization/Core/ab.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/ab.json diff --git a/Emby.Server.Implementations/Localization/Core/ab.json b/Emby.Server.Implementations/Localization/Core/ab.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ab.json @@ -0,0 +1 @@ +{} From 12df192a31cc0e737d58d4cf517784249bfd9d0b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:14:50 +0000 Subject: [PATCH 043/132] chore(deps): update xunit-dotnet monorepo --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a1efdb37b6..07fd257436 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -83,8 +83,8 @@ - + - + From 7bf831da62f00b7df2e847d6298012b17997285f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 18 Dec 2023 22:02:31 +0100 Subject: [PATCH 044/132] Fix tests --- .../Controllers/ImageControllerTests.cs | 2 +- .../CopyToExtensionsTests.cs | 40 +++++++------------ .../Entities/MediaStreamTests.cs | 2 +- .../Video/CleanDateTimeTests.cs | 3 +- .../NetworkConfigurationTests.cs | 1 - .../MediaInfo/EmbeddedImageProviderTests.cs | 4 +- .../Tmdb/TmdbUtilsTests.cs | 5 +-- .../Library/PathExtensionsTests.cs | 2 +- .../QuickConnect/QuickConnectManagerTests.cs | 4 +- .../SessionManager/SessionManagerTests.cs | 2 +- 10 files changed, 25 insertions(+), 40 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs index 0254a1ec6e..5034ad3c71 100644 --- a/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs +++ b/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs @@ -27,7 +27,7 @@ public static class ImageControllerTests [InlineData(null)] [InlineData("")] [InlineData("text/html")] - public static void TryGetImageExtensionFromContentType_InValid_False(string contentType) + public static void TryGetImageExtensionFromContentType_InValid_False(string? contentType) { Assert.False(ImageController.TryGetImageExtensionFromContentType(contentType, out var ex)); Assert.Null(ex); diff --git a/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs index d46beedd99..95f9a5fcfb 100644 --- a/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs +++ b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs @@ -8,20 +8,18 @@ namespace Jellyfin.Extensions.Tests { public static TheoryData, IList, int, IList> CopyTo_Valid_Correct_TestData() { - var data = new TheoryData, IList, int, IList>(); - - data.Add( - new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 }); - - data.Add( - new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } ); + var data = new TheoryData, IList, int, IList> + { + { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 } }, + { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } } + }; return data; } [Theory] [MemberData(nameof(CopyTo_Valid_Correct_TestData))] - public static void CopyTo_Valid_Correct(IReadOnlyList source, IList destination, int index, IList expected) + public static void CopyTo_Valid_Correct(IReadOnlyList source, IList destination, int index, IList expected) { source.CopyTo(destination, index); Assert.Equal(expected, destination); @@ -29,29 +27,21 @@ namespace Jellyfin.Extensions.Tests public static TheoryData, IList, int> CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData() { - var data = new TheoryData, IList, int>(); - - data.Add( - new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 ); - - data.Add( - new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 ); - - data.Add( - new[] { 0, 1, 2 }, Array.Empty(), 0 ); - - data.Add( - new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 ); - - data.Add( - new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 ); + var data = new TheoryData, IList, int> + { + { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 }, + { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 }, + { new[] { 0, 1, 2 }, Array.Empty(), 0 }, + { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 }, + { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 } + }; return data; } [Theory] [MemberData(nameof(CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData))] - public static void CopyTo_Invalid_ThrowsArgumentOutOfRangeException(IReadOnlyList source, IList destination, int index) + public static void CopyTo_Invalid_ThrowsArgumentOutOfRangeException(IReadOnlyList source, IList destination, int index) { Assert.Throws(() => source.CopyTo(destination, index)); } diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index d39a22e30b..f4c0d9fe8f 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -192,7 +192,7 @@ namespace Jellyfin.Model.Tests.Entities [InlineData(4090, 3070, false, "4K")] [InlineData(7680, 4320, false, "8K")] [InlineData(8190, 6140, false, "8K")] - public void GetResolutionText_Valid(int? width, int? height, bool interlaced, string expected) + public void GetResolutionText_Valid(int? width, int? height, bool interlaced, string? expected) { var mediaStream = new MediaStream() { diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index 62d60e5a4b..5029a87933 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -52,9 +52,8 @@ namespace Jellyfin.Naming.Tests.Video [InlineData("My Movie 2013-12-09", "My Movie 2013-12-09", null)] [InlineData("My Movie 20131209", "My Movie 20131209", null)] [InlineData("My Movie 2013-12-09 2013", "My Movie 2013-12-09", 2013)] - [InlineData(null, null, null)] [InlineData("", "", null)] - public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) + public void CleanDateTimeTest(string input, string? expectedName, int? expectedYear) { input = Path.GetFileName(input); diff --git a/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs b/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs index 30726f1d3c..f337fe20b8 100644 --- a/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs +++ b/tests/Jellyfin.Networking.Tests/Configuration/NetworkConfigurationTests.cs @@ -6,7 +6,6 @@ namespace Jellyfin.Networking.Tests.Configuration; public static class NetworkConfigurationTests { [Theory] - [InlineData("", null)] [InlineData("", "")] [InlineData("/Test", "/Test")] [InlineData("/Test", "Test")] diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs index 2bc686a337..85963e5def 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/EmbeddedImageProviderTests.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [InlineData("clearlogo.png", null, 1, ImageType.Logo, ImageFormat.Png)] // extract extension from name [InlineData("backdrop", "image/bmp", 2, ImageType.Backdrop, ImageFormat.Bmp)] // extract extension from mimetype [InlineData("poster", null, 3, ImageType.Primary, ImageFormat.Jpg)] // default extension to jpg - public async void GetImage_Attachment_ReturnsCorrectSelection(string filename, string mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat) + public async void GetImage_Attachment_ReturnsCorrectSelection(string filename, string? mimetype, int targetIndex, ImageType type, ImageFormat? expectedFormat) { var attachments = new List(); string pathPrefix = "path"; @@ -103,7 +103,7 @@ namespace Jellyfin.Providers.Tests.MediaInfo [InlineData(null, "mjpeg", 1, ImageType.Primary, ImageFormat.Jpg)] [InlineData(null, "png", 1, ImageType.Primary, ImageFormat.Png)] [InlineData(null, "webp", 1, ImageType.Primary, ImageFormat.Webp)] - public async void GetImage_Embedded_ReturnsCorrectSelection(string label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat) + public async void GetImage_Embedded_ReturnsCorrectSelection(string? label, string? codec, int targetIndex, ImageType type, ImageFormat? expectedFormat) { var streams = new List(); for (int i = 1; i <= targetIndex; i++) diff --git a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs index efd2d9553f..0bfa330cb6 100644 --- a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs +++ b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs @@ -25,14 +25,11 @@ namespace Jellyfin.Providers.Tests.Tmdb } [Theory] - [InlineData(null, null, null)] - [InlineData(null, "en-US", null)] - [InlineData("en", null, "en")] [InlineData("en", "en-US", "en-US")] [InlineData("fr-CA", "fr-BE", "fr-CA")] [InlineData("fr-CA", "fr", "fr-CA")] [InlineData("de", "en-US", "de")] - public static void AdjustImageLanguage_Valid_Success(string imageLanguage, string requestLanguage, string expected) + public static void AdjustImageLanguage_Valid_Success(string imageLanguage, string requestLanguage, string? expected) { Assert.Equal(expected, TmdbUtils.AdjustImageLanguage(imageLanguage, requestLanguage)); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index 1c35eb3f50..d1be07aa22 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library [InlineData(@"\home/jeff\myfile.mkv", '\\', @"\home\jeff\myfile.mkv")] [InlineData(@"\home/jeff\myfile.mkv", '/', "/home/jeff/myfile.mkv")] [InlineData("", '/', "")] - public void NormalizePath_SpecifyingSeparator_Normalizes(string path, char separator, string expectedPath) + public void NormalizePath_SpecifyingSeparator_Normalizes(string? path, char separator, string? expectedPath) { Assert.Equal(expectedPath, path.NormalizePath(separator)); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs index c32d89ea5d..30f72f5957 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/QuickConnect/QuickConnectManagerTests.cs @@ -85,10 +85,10 @@ namespace Jellyfin.Server.Implementations.Tests.QuickConnect } [Fact] - public void AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException() + public async Task AuthorizeRequest_QuickConnectUnavailable_ThrowsAuthenticationException() { _config.QuickConnectAvailable = false; - Assert.ThrowsAsync(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty)); + await Assert.ThrowsAsync(() => _quickConnectManager.AuthorizeRequest(Guid.Empty, string.Empty)); } [Fact] diff --git a/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs index ebd3a3891c..e463d838e6 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/SessionManager/SessionManagerTests.cs @@ -21,7 +21,7 @@ public class SessionManagerTests [Theory] [InlineData("", typeof(ArgumentException))] [InlineData(null, typeof(ArgumentNullException))] - public async Task GetAuthorizationToken_Should_ThrowException(string deviceId, Type exceptionType) + public async Task GetAuthorizationToken_Should_ThrowException(string? deviceId, Type exceptionType) { await using var sessionManager = new Emby.Server.Implementations.Session.SessionManager( NullLogger.Instance, From 05bc894ae86bfd4341766bc14b25c282865c4d98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Dec 2023 00:26:16 +0000 Subject: [PATCH 045/132] chore(deps): update actions/download-artifact action to v4.1.0 --- .github/workflows/ci-openapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 5ff9820cbc..c9f17429a2 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -78,12 +78,12 @@ jobs: - openapi-base steps: - name: Download openapi-head - uses: actions/download-artifact@7a1cd3216ca9260cd8022db641d960b1db4d1be4 # v4.0.0 + uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 with: name: openapi-head path: openapi-head - name: Download openapi-base - uses: actions/download-artifact@7a1cd3216ca9260cd8022db641d960b1db4d1be4 # v4.0.0 + uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 with: name: openapi-base path: openapi-base From a42951c0a81edd0c70f1722c7c19415de2d6ccdd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:29:35 +0000 Subject: [PATCH 046/132] chore(deps): update dependency stylecop.analyzers to v1.2.0-beta.556 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 07fd257436..0c234bcf8b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -70,7 +70,7 @@ - + From d92f2ac31cad5b667125a055708a080605ee53f6 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Tue, 19 Dec 2023 23:21:57 +0300 Subject: [PATCH 047/132] test: fix remux tests This partially reverts commit 2d8f7b46f11ee458fe0b31d8e1473aaaea9cf296 --- .../Dlna/StreamBuilderTests.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index 210ce4a47f..56c2c554da 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")] - [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 + [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Firefox @@ -38,7 +38,7 @@ namespace Jellyfin.Model.Tests [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")] - [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 + [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Safari @@ -89,7 +89,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome-NoHLS", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")] [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode", "http")] - [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 + [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // TranscodeMedia @@ -177,7 +177,7 @@ namespace Jellyfin.Model.Tests [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] - [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 + [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450 [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Firefox @@ -187,7 +187,7 @@ namespace Jellyfin.Model.Tests [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] - [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported)] // #6450 + [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.ContainerNotSupported, "Remux")] // #6450 [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 // Safari @@ -419,14 +419,7 @@ namespace Jellyfin.Model.Tests if (targetAudioStream?.IsExternal == false) { // Check expected audio codecs (1) - if (streamInfo.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported)) - { - Assert.Contains(targetAudioStream.Codec, streamInfo.AudioCodecs); - } - else - { - Assert.DoesNotContain(targetAudioStream.Codec, streamInfo.AudioCodecs); - } + Assert.DoesNotContain(targetAudioStream.Codec, streamInfo.AudioCodecs); } } else if (transcodeMode.Equals("Remux", StringComparison.Ordinal)) From 8c29fa422a65b9c9abe239cf4536804e5c08aa2c Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Wed, 20 Dec 2023 22:36:25 +0300 Subject: [PATCH 048/132] test: fix tizen profile --- .../Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs | 8 ++++---- .../Test Data/DeviceProfile-Tizen3-stereo.json | 13 +++++++++++++ .../Test Data/DeviceProfile-Tizen4-4K-5.1.json | 13 +++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index 56c2c554da..c567f280a0 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -291,11 +291,11 @@ namespace Jellyfin.Model.Tests [InlineData("AndroidTVExoPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] [InlineData("AndroidTVExoPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // Tizen 3 Stereo - [InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] - [InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] + [InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] + [InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // Tizen 4 4K 5.1 - [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] - [InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] + [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] + [InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = default, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetMediaOptions(deviceName, mediaSource); diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json index 53637b7931..2e3e6e6de4 100644 --- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json +++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen3-stereo.json @@ -413,6 +413,19 @@ } ], "CodecProfiles": [ + { + "Type": "VideoAudio", + "Conditions": [ + { + "Condition": "Equals", + "Property": "IsSecondaryAudio", + "Value": "false", + "IsRequired": false, + "$type": "ProfileCondition" + } + ], + "$type": "CodecProfile" + }, { "Type": "Video", "Conditions": [ diff --git a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json index d3ef22c256..1562304718 100644 --- a/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json +++ b/tests/Jellyfin.Model.Tests/Test Data/DeviceProfile-Tizen4-4K-5.1.json @@ -413,6 +413,19 @@ } ], "CodecProfiles": [ + { + "Type": "VideoAudio", + "Conditions": [ + { + "Condition": "Equals", + "Property": "IsSecondaryAudio", + "Value": "false", + "IsRequired": false, + "$type": "ProfileCondition" + } + ], + "$type": "CodecProfile" + }, { "Type": "Video", "Conditions": [ From ca280279da9a21775098ec73aa4c85933a443597 Mon Sep 17 00:00:00 2001 From: Dmitry Lyzo Date: Wed, 20 Dec 2023 22:38:01 +0300 Subject: [PATCH 049/132] test: add more audio tests --- .../Dlna/StreamBuilderTests.cs | 5 + ...ceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json | 102 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index c567f280a0..2f84fa544e 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -274,13 +274,16 @@ namespace Jellyfin.Model.Tests [Theory] // Chrome [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450 + [InlineData("Chrome", "mp4-h264-ac3-aac-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] [InlineData("Chrome", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450 [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")] // Firefox [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450 + [InlineData("Firefox", "mp4-h264-ac3-aac-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.SecondaryAudioNotSupported, "Transcode")] // Yatse [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450 + [InlineData("Yatse", "mp4-h264-ac3-aac-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // #6450 // RokuSSPlus [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // #6450 @@ -292,9 +295,11 @@ namespace Jellyfin.Model.Tests [InlineData("AndroidTVExoPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] // Tizen 3 Stereo [InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] + [InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] [InlineData("Tizen3-stereo", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] // Tizen 4 4K 5.1 [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] + [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] [InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.SecondaryAudioNotSupported, "Remux")] public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = default, string transcodeMode = "DirectStream", string transcodeProtocol = "") { diff --git a/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json new file mode 100644 index 0000000000..9d819c4ada --- /dev/null +++ b/tests/Jellyfin.Model.Tests/Test Data/MediaSourceInfo-mp4-h264-ac3-aac-aac-srt-2600k.json @@ -0,0 +1,102 @@ +{ + "Id": "a766d122b58e45d9492d17af77748bf5", + "Path": "/Media/MyVideo-720p.mp4", + "Container": "mov,mp4,m4a,3gp,3g2,mj2", + "Size": 835317696, + "Name": "MyVideo-720p", + "ETag": "579a34c6d5dfb21d81539a51220b6a23", + "RunTimeTicks": 25801230336, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "h264", + "CodecTag": "avc1", + "Language": "eng", + "TimeBase": "1/11988", + "VideoRange": "SDR", + "DisplayTitle": "720p H264 SDR", + "NalLengthSize": "0", + "BitRate": 2032876, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": true, + "Height": 720, + "Width": 1280, + "AverageFrameRate": 23.976, + "RealFrameRate": 23.976, + "Profile": "High", + "Type": 1, + "AspectRatio": "16:9", + "PixelFormat": "yuv420p", + "Level": 41 + }, + { + "Codec": "ac3", + "CodecTag": "ac-3", + "Language": "eng", + "TimeBase": "1/48000", + "DisplayTitle": "En - Dolby Digital - 5.1 - Default", + "ChannelLayout": "5.1", + "BitRate": 384000, + "Channels": 6, + "SampleRate": 48000, + "IsDefault": true, + "Index": 1, + "Score": 202 + }, + { + "Codec": "aac", + "CodecTag": "mp4a", + "Language": "eng", + "TimeBase": "1/48000", + "DisplayTitle": "En - AAC - Stereo - Default", + "ChannelLayout": "stereo", + "BitRate": 164741, + "Channels": 2, + "SampleRate": 48000, + "IsDefault": true, + "Profile": "LC", + "Index": 2, + "Score": 203 + }, + { + "Codec": "aac", + "CodecTag": "mp4a", + "Language": "rus", + "TimeBase": "1/48000", + "DisplayTitle": "Ru - AAC - Stereo - Default", + "ChannelLayout": "stereo", + "BitRate": 164741, + "Channels": 2, + "SampleRate": 48000, + "IsDefault": false, + "Profile": "LC", + "Index": 3, + "Score": 203 + }, + { + "Codec": "srt", + "Language": "eng", + "TimeBase": "1/1000000", + "localizedUndefined": "Undefined", + "localizedDefault": "Default", + "localizedForced": "Forced", + "DisplayTitle": "En - Default", + "BitRate": 92, + "IsDefault": true, + "Type": 2, + "Index": 4, + "Score": 6421, + "IsExternal": true, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Path": "/Media/MyVideo-WEBDL-2160p.default.eng.srt" + } + ], + "Bitrate": 2590008, + "DefaultAudioStreamIndex": 1, + "DefaultSubtitleStreamIndex": 4 +} From abd74fd5a481d67af1414960be7b7b19c9ee7e82 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 31 Oct 2023 11:12:09 -0400 Subject: [PATCH 050/132] Move TranscodingJobDto and TranscodingThrottler to Controller --- Jellyfin.Api/Controllers/DynamicHlsController.cs | 1 - Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs | 1 - Jellyfin.Api/Helpers/ProgressiveFileStream.cs | 2 +- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 1 - Jellyfin.Api/Models/StreamingDtos/StreamState.cs | 1 - .../MediaEncoding}/TranscodingJobDto.cs | 3 +-- .../MediaEncoding}/TranscodingThrottler.cs | 3 +-- 7 files changed, 3 insertions(+), 9 deletions(-) rename {Jellyfin.Api/Models/PlaybackDtos => MediaBrowser.Controller/MediaEncoding}/TranscodingJobDto.cs (98%) rename {Jellyfin.Api/Models/PlaybackDtos => MediaBrowser.Controller/MediaEncoding}/TranscodingThrottler.cs (98%) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 9e9c610cc5..6b2c423632 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index 0f0a70c698..ce2e476b7d 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Controller.MediaEncoding; using Microsoft.AspNetCore.Http; diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs index d7b1c9f8bb..379ec6e27b 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs +++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Models.PlaybackDtos; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; namespace Jellyfin.Api.Helpers; diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 77d3edbd65..14436909c9 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -9,7 +9,6 @@ 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; using MediaBrowser.Common; diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs index cc1f9163e5..4b69392ef4 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs +++ b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs @@ -1,6 +1,5 @@ using System; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.PlaybackDtos; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; diff --git a/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs similarity index 98% rename from Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs rename to MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs index 480ddab098..6f929204ff 100644 --- a/Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs @@ -2,11 +2,10 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Models.PlaybackDtos; +namespace MediaBrowser.Controller.MediaEncoding; /// /// Class TranscodingJob. diff --git a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs similarity index 98% rename from Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs rename to MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs index b577c4ea6a..aa08af54f0 100644 --- a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs @@ -2,12 +2,11 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Models.PlaybackDtos; +namespace MediaBrowser.Controller.MediaEncoding; /// /// Transcoding throttler. From c2081955c8b2a81eb214f321697d3462709164e0 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 31 Oct 2023 11:31:09 -0400 Subject: [PATCH 051/132] Rename and clean up TranscodingJob --- .../Controllers/DynamicHlsController.cs | 8 +- .../Helpers/FileStreamResponseHelpers.cs | 2 +- Jellyfin.Api/Helpers/ProgressiveFileStream.cs | 4 +- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 71 +++------- .../Models/StreamingDtos/StreamState.cs | 2 +- ...TranscodingJobDto.cs => TranscodingJob.cs} | 130 +++++++++--------- .../MediaEncoding/TranscodingThrottler.cs | 6 +- 7 files changed, 97 insertions(+), 126 deletions(-) rename MediaBrowser.Controller/MediaEncoding/{TranscodingJobDto.cs => TranscodingJob.cs} (65%) diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 6b2c423632..260ed4787b 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -287,7 +287,7 @@ public class DynamicHlsController : BaseJellyfinApiController cancellationToken) .ConfigureAwait(false); - TranscodingJobDto? job = null; + TranscodingJob? job = null; var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); if (!System.IO.File.Exists(playlistPath)) @@ -1431,7 +1431,7 @@ public class DynamicHlsController : BaseJellyfinApiController var segmentExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer); - TranscodingJobDto? job; + TranscodingJob? job; if (System.IO.File.Exists(segmentPath)) { @@ -1921,7 +1921,7 @@ public class DynamicHlsController : BaseJellyfinApiController string segmentPath, string segmentExtension, int segmentIndex, - TranscodingJobDto? transcodingJob, + TranscodingJob? transcodingJob, CancellationToken cancellationToken) { var segmentExists = System.IO.File.Exists(segmentPath); @@ -1990,7 +1990,7 @@ public class DynamicHlsController : BaseJellyfinApiController return GetSegmentResult(state, segmentPath, transcodingJob); } - private ActionResult GetSegmentResult(StreamState state, string segmentPath, TranscodingJobDto? transcodingJob) + private ActionResult GetSegmentResult(StreamState state, string segmentPath, TranscodingJob? transcodingJob) { var segmentEndingPositionTicks = state.Request.CurrentRuntimeTicks + state.Request.ActualSegmentLengthTicks; diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index ce2e476b7d..fafa2c0551 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -96,7 +96,7 @@ public static class FileStreamResponseHelpers await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { - TranscodingJobDto? job; + TranscodingJob? job; if (!File.Exists(outputPath)) { job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs index 379ec6e27b..18088483d3 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs +++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs @@ -14,7 +14,7 @@ namespace Jellyfin.Api.Helpers; public class ProgressiveFileStream : Stream { private readonly Stream _stream; - private readonly TranscodingJobDto? _job; + private readonly TranscodingJob? _job; private readonly TranscodingJobHelper? _transcodingJobHelper; private readonly int _timeoutMs; private bool _disposed; @@ -26,7 +26,7 @@ public class ProgressiveFileStream : Stream /// The transcoding job information. /// The transcoding job helper. /// The timeout duration in milliseconds. - public ProgressiveFileStream(string filePath, TranscodingJobDto? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000) + public ProgressiveFileStream(string filePath, TranscodingJob? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000) { _job = job; _transcodingJobHelper = transcodingJobHelper; diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 14436909c9..9a6ec17fdb 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -36,7 +36,7 @@ public class TranscodingJobHelper : IDisposable /// /// The active transcoding jobs. /// - private static readonly List _activeTranscodingJobs = new List(); + private static readonly List _activeTranscodingJobs = new List(); /// /// The transcoding locks. @@ -105,7 +105,7 @@ public class TranscodingJobHelper : IDisposable /// /// Playback session id. /// The transcoding job. - public TranscodingJobDto? GetTranscodingJob(string playSessionId) + public TranscodingJob? GetTranscodingJob(string playSessionId) { lock (_activeTranscodingJobs) { @@ -119,7 +119,7 @@ public class TranscodingJobHelper : IDisposable /// Path to the transcoding file. /// The . /// The transcoding job. - public TranscodingJobDto? GetTranscodingJob(string path, TranscodingJobType type) + public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) { @@ -139,7 +139,7 @@ public class TranscodingJobHelper : IDisposable _logger.LogDebug("PingTranscodingJob PlaySessionId={0} isUsedPaused: {1}", playSessionId, isUserPaused); - List jobs; + List jobs; lock (_activeTranscodingJobs) { @@ -160,7 +160,7 @@ public class TranscodingJobHelper : IDisposable } } - private void PingTimer(TranscodingJobDto job, bool isProgressCheckIn) + private void PingTimer(TranscodingJob job, bool isProgressCheckIn) { if (job.HasExited) { @@ -195,7 +195,7 @@ public class TranscodingJobHelper : IDisposable /// The state. private async void OnTranscodeKillTimerStopped(object? state) { - var job = state as TranscodingJobDto ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJobDto)}", nameof(state)); + var job = state as TranscodingJob ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJob)}", nameof(state)); if (!job.HasExited && job.Type != TranscodingJobType.Progressive) { var timeSinceLastPing = (DateTime.UtcNow - job.LastPingDate).TotalMilliseconds; @@ -234,9 +234,9 @@ public class TranscodingJobHelper : IDisposable /// The kill job. /// The delete files. /// Task. - private Task KillTranscodingJobs(Func killJob, Func deleteFiles) + private Task KillTranscodingJobs(Func killJob, Func deleteFiles) { - var jobs = new List(); + var jobs = new List(); lock (_activeTranscodingJobs) { @@ -267,7 +267,7 @@ public class TranscodingJobHelper : IDisposable /// The job. /// if set to true [close live stream]. /// The delete. - private async Task KillTranscodingJob(TranscodingJobDto job, bool closeLiveStream, Func delete) + private async Task KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func delete) { job.DisposeKillTimer(); @@ -281,6 +281,7 @@ public class TranscodingJobHelper : IDisposable { #pragma warning disable CA1849 // Can't await in lock block job.CancellationTokenSource.Cancel(); +#pragma warning restore CA1849 } } @@ -289,35 +290,7 @@ public class TranscodingJobHelper : IDisposable _transcodingLocks.Remove(job.Path!); } - lock (job.ProcessLock!) - { - job.TranscodingThrottler?.Stop().GetAwaiter().GetResult(); - - var process = job.Process; - - var hasExited = job.HasExited; - - if (!hasExited) - { - try - { - _logger.LogInformation("Stopping ffmpeg process with q command for {Path}", job.Path); - - process!.StandardInput.WriteLine("q"); - - // Need to wait because killing is asynchronous. - if (!process.WaitForExit(5000)) - { - _logger.LogInformation("Killing FFmpeg process for {Path}", job.Path); - process.Kill(); - } - } - catch (InvalidOperationException) - { - } - } -#pragma warning restore CA1849 - } + job.Stop(); if (delete(job.Path!)) { @@ -430,7 +403,7 @@ public class TranscodingJobHelper : IDisposable /// /// Report the transcoding progress to the session manager. /// - /// The of which the progress will be reported. + /// The of which the progress will be reported. /// The of the current transcoding job. /// The current transcoding position. /// The framerate of the transcoding job. @@ -438,7 +411,7 @@ public class TranscodingJobHelper : IDisposable /// The number of bytes transcoded. /// The bitrate of the transcoding job. public void ReportTranscodingProgress( - TranscodingJobDto job, + TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, @@ -500,7 +473,7 @@ public class TranscodingJobHelper : IDisposable /// The cancellation token source. /// The working directory. /// Task. - public async Task StartFfMpeg( + public async Task StartFfMpeg( StreamState state, string outputPath, string commandLineArguments, @@ -655,7 +628,7 @@ public class TranscodingJobHelper : IDisposable return transcodingJob; } - private void StartThrottler(StreamState state, TranscodingJobDto transcodingJob) + private void StartThrottler(StreamState state, TranscodingJob transcodingJob) { if (EnableThrottling(state)) { @@ -688,7 +661,7 @@ public class TranscodingJobHelper : IDisposable /// The state. /// The cancellation token source. /// TranscodingJob. - public TranscodingJobDto OnTranscodeBeginning( + public TranscodingJob OnTranscodeBeginning( string path, string? playSessionId, string? liveStreamId, @@ -701,7 +674,7 @@ public class TranscodingJobHelper : IDisposable { lock (_activeTranscodingJobs) { - var job = new TranscodingJobDto(_loggerFactory.CreateLogger()) + var job = new TranscodingJob(_loggerFactory.CreateLogger()) { Type = type, Path = path, @@ -727,7 +700,7 @@ public class TranscodingJobHelper : IDisposable /// Called when [transcode end]. /// /// The transcode job. - public void OnTranscodeEndRequest(TranscodingJobDto job) + public void OnTranscodeEndRequest(TranscodingJob job) { job.ActiveRequestCount--; _logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={ActiveRequestCount}", job.ActiveRequestCount); @@ -775,7 +748,7 @@ public class TranscodingJobHelper : IDisposable /// The process. /// The job. /// The state. - private void OnFfMpegProcessExited(Process process, TranscodingJobDto job, StreamState state) + private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state) { job.HasExited = true; job.ExitCode = process.ExitCode; @@ -826,8 +799,8 @@ public class TranscodingJobHelper : IDisposable /// /// The path. /// The type. - /// The . - public TranscodingJobDto? OnTranscodeBeginRequest(string path, TranscodingJobType type) + /// The . + public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) { @@ -844,7 +817,7 @@ public class TranscodingJobHelper : IDisposable } } - private void OnTranscodeBeginRequest(TranscodingJobDto job) + private void OnTranscodeBeginRequest(TranscodingJob job) { job.ActiveRequestCount++; diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs index 4b69392ef4..439f8052c1 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs +++ b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs @@ -140,7 +140,7 @@ public class StreamState : EncodingJobInfo, IDisposable /// /// Gets or sets the transcoding job. /// - public TranscodingJobDto? TranscodingJob { get; set; } + public TranscodingJob? TranscodingJob { get; set; } /// public void Dispose() diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs similarity index 65% rename from MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs rename to MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs index 6f929204ff..1e6d5933c8 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingJobDto.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJob.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Threading; using MediaBrowser.Model.Dto; using Microsoft.Extensions.Logging; @@ -10,39 +9,31 @@ namespace MediaBrowser.Controller.MediaEncoding; /// /// Class TranscodingJob. /// -public class TranscodingJobDto : IDisposable +public sealed class TranscodingJob : IDisposable { - /// - /// The process lock. - /// - [SuppressMessage("Microsoft.Performance", "CA1051:NoVisibleInstanceFields", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")] - [SuppressMessage("Microsoft.Performance", "SA1401:PrivateField", MessageId = "ProcessLock", Justification = "Imported from ServiceStack")] - public readonly object ProcessLock = new object(); + private readonly ILogger _logger; + private readonly object _processLock = new(); + private readonly object _timerLock = new(); + + private Timer? _killTimer; /// - /// Timer lock. - /// - private readonly object _timerLock = new object(); - - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Instance of the interface. - public TranscodingJobDto(ILogger logger) + public TranscodingJob(ILogger logger) { - Logger = logger; + _logger = logger; } /// /// Gets or sets the play session identifier. /// - /// The play session identifier. public string? PlaySessionId { get; set; } /// /// Gets or sets the live stream identifier. /// - /// The live stream identifier. public string? LiveStreamId { get; set; } /// @@ -53,7 +44,6 @@ public class TranscodingJobDto : IDisposable /// /// Gets or sets the path. /// - /// The path. public MediaSourceInfo? MediaSource { get; set; } /// @@ -64,32 +54,18 @@ public class TranscodingJobDto : IDisposable /// /// Gets or sets the type. /// - /// The type. public TranscodingJobType Type { get; set; } /// /// Gets or sets the process. /// - /// The process. public Process? Process { get; set; } - /// - /// Gets logger. - /// - public ILogger Logger { get; private set; } - /// /// Gets or sets the active request count. /// - /// The active request count. public int ActiveRequestCount { get; set; } - /// - /// Gets or sets the kill timer. - /// - /// The kill timer. - private Timer? KillTimer { get; set; } - /// /// Gets or sets device id. /// @@ -177,7 +153,7 @@ public class TranscodingJobDto : IDisposable { lock (_timerLock) { - KillTimer?.Change(Timeout.Infinite, Timeout.Infinite); + _killTimer?.Change(Timeout.Infinite, Timeout.Infinite); } } @@ -188,10 +164,10 @@ public class TranscodingJobDto : IDisposable { lock (_timerLock) { - if (KillTimer is not null) + if (_killTimer is not null) { - KillTimer.Dispose(); - KillTimer = null; + _killTimer.Dispose(); + _killTimer = null; } } } @@ -219,15 +195,15 @@ public class TranscodingJobDto : IDisposable lock (_timerLock) { - if (KillTimer is null) + if (_killTimer is null) { - Logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); - KillTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite); + _logger.LogDebug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + _killTimer = new Timer(new TimerCallback(callback), this, intervalMs, Timeout.Infinite); } else { - Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); - KillTimer.Change(intervalMs, Timeout.Infinite); + _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + _killTimer.Change(intervalMs, Timeout.Infinite); } } } @@ -244,39 +220,61 @@ public class TranscodingJobDto : IDisposable lock (_timerLock) { - if (KillTimer is not null) + if (_killTimer is not null) { var intervalMs = PingTimeout; - Logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); - KillTimer.Change(intervalMs, Timeout.Infinite); + _logger.LogDebug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + _killTimer.Change(intervalMs, Timeout.Infinite); } } } + /// + /// Stops the transcoding job. + /// + public void Stop() + { + lock (_processLock) + { +#pragma warning disable CA1849 // Can't await in lock block + TranscodingThrottler?.Stop().GetAwaiter().GetResult(); + + var process = Process; + + if (!HasExited) + { + try + { + _logger.LogInformation("Stopping ffmpeg process with q command for {Path}", Path); + + process!.StandardInput.WriteLine("q"); + + // Need to wait because killing is asynchronous. + if (!process.WaitForExit(5000)) + { + _logger.LogInformation("Killing FFmpeg process for {Path}", Path); + process.Kill(); + } + } + catch (InvalidOperationException) + { + } + } +#pragma warning restore CA1849 + } + } + /// public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose all resources. - /// - /// Whether to dispose all resources. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - Process?.Dispose(); - Process = null; - KillTimer?.Dispose(); - KillTimer = null; - CancellationTokenSource?.Dispose(); - CancellationTokenSource = null; - TranscodingThrottler?.Dispose(); - TranscodingThrottler = null; - } + Process?.Dispose(); + Process = null; + _killTimer?.Dispose(); + _killTimer = null; + CancellationTokenSource?.Dispose(); + CancellationTokenSource = null; + TranscodingThrottler?.Dispose(); + TranscodingThrottler = null; } } diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs index aa08af54f0..813f13eaef 100644 --- a/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs +++ b/MediaBrowser.Controller/MediaEncoding/TranscodingThrottler.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.MediaEncoding; /// public class TranscodingThrottler : IDisposable { - private readonly TranscodingJobDto _job; + private readonly TranscodingJob _job; private readonly ILogger _logger; private readonly IConfigurationManager _config; private readonly IFileSystem _fileSystem; @@ -29,7 +29,7 @@ public class TranscodingThrottler : IDisposable /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - public TranscodingThrottler(TranscodingJobDto job, ILogger logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder) + public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder) { _job = job; _logger = logger; @@ -145,7 +145,7 @@ public class TranscodingThrottler : IDisposable } } - private bool IsThrottleAllowed(TranscodingJobDto job, int thresholdSeconds) + private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds) { var bytesDownloaded = job.BytesDownloaded; var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; From 9215a4d40ae24e5996a5e16dfa296b09a7befc40 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 31 Oct 2023 13:26:37 -0400 Subject: [PATCH 052/132] Add ITranscodeManager service --- .../ApplicationHost.cs | 3 +- Jellyfin.Api/Controllers/AudioController.cs | 1 + .../Controllers/DynamicHlsController.cs | 46 +-- .../Controllers/HlsSegmentController.cs | 14 +- Jellyfin.Api/Controllers/LiveTvController.cs | 11 +- .../Controllers/PlaystateController.cs | 17 +- .../Controllers/UniversalAudioController.cs | 1 + Jellyfin.Api/Controllers/VideosController.cs | 16 +- Jellyfin.Api/Helpers/AudioHelper.cs | 16 +- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 12 +- .../Helpers/FileStreamResponseHelpers.cs | 21 +- Jellyfin.Api/Helpers/HlsHelpers.cs | 1 + Jellyfin.Api/Helpers/ProgressiveFileStream.cs | 12 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 10 +- .../StreamingDtos/HlsAudioRequestDto.cs | 4 +- .../StreamingDtos/HlsVideoRequestDto.cs | 4 +- .../MediaEncoding/ITranscodeManager.cs | 104 ++++++ .../Streaming}/StreamState.cs | 13 +- .../Streaming}/StreamingRequestDto.cs | 2 +- .../Streaming}/VideoRequestDto.cs | 2 +- .../Transcoding/TranscodeManager.cs | 326 +++++------------- 21 files changed, 306 insertions(+), 330 deletions(-) create mode 100644 MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs rename {Jellyfin.Api/Models/StreamingDtos => MediaBrowser.Controller/Streaming}/StreamState.cs (90%) rename {Jellyfin.Api/Models/StreamingDtos => MediaBrowser.Controller/Streaming}/StreamingRequestDto.cs (96%) rename {Jellyfin.Api/Models/StreamingDtos => MediaBrowser.Controller/Streaming}/VideoRequestDto.cs (94%) rename Jellyfin.Api/Helpers/TranscodingJobHelper.cs => MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs (71%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index dce56e0a4f..f385f6a512 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -76,6 +76,7 @@ using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.MediaEncoding.Subtitles; +using MediaBrowser.MediaEncoding.Transcoding; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; @@ -583,7 +584,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 5bc5330861..cd09d2bfab 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -6,6 +6,7 @@ using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 260ed4787b..dda1e9d561 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; @@ -18,6 +19,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; @@ -50,7 +52,7 @@ public class DynamicHlsController : BaseJellyfinApiController private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; private readonly IFileSystem _fileSystem; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; private readonly ILogger _logger; private readonly EncodingHelper _encodingHelper; private readonly IDynamicHlsPlaylistGenerator _dynamicHlsPlaylistGenerator; @@ -66,7 +68,7 @@ public class DynamicHlsController : BaseJellyfinApiController /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the class. + /// Instance of the interface. /// Instance of the interface. /// Instance of . /// Instance of . @@ -78,7 +80,7 @@ public class DynamicHlsController : BaseJellyfinApiController IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, ILogger logger, DynamicHlsHelper dynamicHlsHelper, EncodingHelper encodingHelper, @@ -90,7 +92,7 @@ public class DynamicHlsController : BaseJellyfinApiController _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; _fileSystem = fileSystem; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; _logger = logger; _dynamicHlsHelper = dynamicHlsHelper; _encodingHelper = encodingHelper; @@ -282,7 +284,7 @@ public class DynamicHlsController : BaseJellyfinApiController _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, TranscodingJobType, cancellationToken) .ConfigureAwait(false); @@ -292,7 +294,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (!System.IO.File.Exists(playlistPath)) { - var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath); + var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath); await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { @@ -301,11 +303,11 @@ public class DynamicHlsController : BaseJellyfinApiController // If the playlist doesn't already exist, startup ffmpeg try { - job = await _transcodingJobHelper.StartFfMpeg( + job = await _transcodeManager.StartFfMpeg( state, playlistPath, GetCommandLineArguments(playlistPath, state, true, 0), - Request, + Request.HttpContext.User.GetUserId(), TranscodingJobType, cancellationTokenSource) .ConfigureAwait(false); @@ -330,11 +332,11 @@ public class DynamicHlsController : BaseJellyfinApiController } } - job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); if (job is not null) { - _transcodingJobHelper.OnTranscodeEndRequest(job); + _transcodeManager.OnTranscodeEndRequest(job); } var playlistText = HlsHelpers.GetLivePlaylistText(playlistPath, state); @@ -1382,7 +1384,7 @@ public class DynamicHlsController : BaseJellyfinApiController _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, TranscodingJobType, cancellationTokenSource.Token) .ConfigureAwait(false); @@ -1420,7 +1422,7 @@ public class DynamicHlsController : BaseJellyfinApiController _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, TranscodingJobType, cancellationToken) .ConfigureAwait(false); @@ -1435,12 +1437,12 @@ public class DynamicHlsController : BaseJellyfinApiController if (System.IO.File.Exists(segmentPath)) { - job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); _logger.LogDebug("returning {0} [it exists, try 1]", segmentPath); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } - var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath); + var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath); await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false); var released = false; var startTranscoding = false; @@ -1449,7 +1451,7 @@ public class DynamicHlsController : BaseJellyfinApiController { if (System.IO.File.Exists(segmentPath)) { - job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); transcodingLock.Release(); released = true; _logger.LogDebug("returning {0} [it exists, try 2]", segmentPath); @@ -1487,7 +1489,7 @@ public class DynamicHlsController : BaseJellyfinApiController // If the playlist doesn't already exist, startup ffmpeg try { - await _transcodingJobHelper.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false) + await _transcodeManager.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false) .ConfigureAwait(false); if (currentTranscodingIndex.HasValue) @@ -1498,11 +1500,11 @@ public class DynamicHlsController : BaseJellyfinApiController streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks; state.WaitForPath = segmentPath; - job = await _transcodingJobHelper.StartFfMpeg( + job = await _transcodeManager.StartFfMpeg( state, playlistPath, GetCommandLineArguments(playlistPath, state, false, segmentId), - Request, + Request.HttpContext.User.GetUserId(), TranscodingJobType, cancellationTokenSource).ConfigureAwait(false); } @@ -1516,7 +1518,7 @@ public class DynamicHlsController : BaseJellyfinApiController } else { - job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); if (job?.TranscodingThrottler is not null) { await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false); @@ -1533,7 +1535,7 @@ public class DynamicHlsController : BaseJellyfinApiController } _logger.LogDebug("returning {0} [general case]", segmentPath); - job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); + job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false); } @@ -2000,7 +2002,7 @@ public class DynamicHlsController : BaseJellyfinApiController if (transcodingJob is not null) { transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks); - _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob); + _transcodeManager.OnTranscodeEndRequest(transcodingJob); } return Task.CompletedTask; @@ -2011,7 +2013,7 @@ public class DynamicHlsController : BaseJellyfinApiController private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension) { - var job = _transcodingJobHelper.GetTranscodingJob(playlist, TranscodingJobType); + var job = _transcodeManager.GetTranscodingJob(playlist, TranscodingJobType); if (job is null || job.HasExited) { diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index 392d9955fb..1927a332b2 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -24,22 +24,22 @@ public class HlsSegmentController : BaseJellyfinApiController { private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. - /// Initialized instance of the . + /// Instance of the interface. public HlsSegmentController( IFileSystem fileSystem, IServerConfigurationManager serverConfigurationManager, - TranscodingJobHelper transcodingJobHelper) + ITranscodeManager transcodeManager) { _fileSystem = fileSystem; _serverConfigurationManager = serverConfigurationManager; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; } /// @@ -112,7 +112,7 @@ public class HlsSegmentController : BaseJellyfinApiController [FromQuery, Required] string deviceId, [FromQuery, Required] string playSessionId) { - _transcodingJobHelper.KillTranscodingJobs(deviceId, playSessionId, path => true); + _transcodeManager.KillTranscodingJobs(deviceId, playSessionId, _ => true); return NoContent(); } @@ -174,13 +174,13 @@ public class HlsSegmentController : BaseJellyfinApiController private ActionResult GetFileResult(string path, string playlistPath) { - var transcodingJob = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); + var transcodingJob = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); Response.OnCompleted(() => { if (transcodingJob is not null) { - _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob); + _transcodeManager.OnTranscodeEndRequest(transcodingJob); } return Task.CompletedTask; diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 425086895b..a40f273ae4 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -24,6 +24,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -47,7 +48,7 @@ public class LiveTvController : BaseJellyfinApiController private readonly IDtoService _dtoService; private readonly IMediaSourceManager _mediaSourceManager; private readonly IConfigurationManager _configurationManager; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; /// /// Initializes a new instance of the class. @@ -59,7 +60,7 @@ public class LiveTvController : BaseJellyfinApiController /// 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, @@ -68,7 +69,7 @@ public class LiveTvController : BaseJellyfinApiController IDtoService dtoService, IMediaSourceManager mediaSourceManager, IConfigurationManager configurationManager, - TranscodingJobHelper transcodingJobHelper) + ITranscodeManager transcodeManager) { _liveTvManager = liveTvManager; _userManager = userManager; @@ -77,7 +78,7 @@ public class LiveTvController : BaseJellyfinApiController _dtoService = dtoService; _mediaSourceManager = mediaSourceManager; _configurationManager = configurationManager; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; } /// @@ -1171,7 +1172,7 @@ public class LiveTvController : BaseJellyfinApiController return NotFound(); } - var stream = new ProgressiveFileStream(path, null, _transcodingJobHelper); + var stream = new ProgressiveFileStream(path, null, _transcodeManager); return new FileStreamResult(stream, MimeTypes.GetMimeType(path)); } diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 8ad553bcb8..bde2f4d1ac 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -8,6 +8,7 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; @@ -30,7 +31,7 @@ public class PlaystateController : BaseJellyfinApiController private readonly ILibraryManager _libraryManager; private readonly ISessionManager _sessionManager; private readonly ILogger _logger; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; /// /// Initializes a new instance of the class. @@ -40,14 +41,14 @@ public class PlaystateController : BaseJellyfinApiController /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Th singleton. + /// Instance of the interface. public PlaystateController( IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, ILoggerFactory loggerFactory, - TranscodingJobHelper transcodingJobHelper) + ITranscodeManager transcodeManager) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -55,7 +56,7 @@ public class PlaystateController : BaseJellyfinApiController _sessionManager = sessionManager; _logger = loggerFactory.CreateLogger(); - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; } /// @@ -188,7 +189,7 @@ public class PlaystateController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult PingPlaybackSession([FromQuery, Required] string playSessionId) { - _transcodingJobHelper.PingTranscodingJob(playSessionId, null); + _transcodeManager.PingTranscodingJob(playSessionId, null); return NoContent(); } @@ -205,7 +206,7 @@ public class PlaystateController : BaseJellyfinApiController _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); @@ -354,7 +355,7 @@ public class PlaystateController : BaseJellyfinApiController _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); @@ -388,7 +389,7 @@ public class PlaystateController : BaseJellyfinApiController { if (method == PlayMethod.Transcode) { - var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodingJobHelper.GetTranscodingJob(playSessionId); + var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodeManager.GetTranscodingJob(playSessionId); if (job is null) { return PlayMethod.DirectPlay; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 7177a04403..0a416aedbe 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -11,6 +11,7 @@ using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 5d9868eb9f..c231c147fc 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -11,7 +11,6 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; -using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; @@ -20,6 +19,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -43,7 +43,7 @@ public class VideosController : BaseJellyfinApiController private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; private readonly IHttpClientFactory _httpClientFactory; private readonly EncodingHelper _encodingHelper; @@ -58,7 +58,7 @@ public class VideosController : BaseJellyfinApiController /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the class. + /// Instance of the interface. /// Instance of the interface. /// Instance of . public VideosController( @@ -68,7 +68,7 @@ public class VideosController : BaseJellyfinApiController IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, IHttpClientFactory httpClientFactory, EncodingHelper encodingHelper) { @@ -78,7 +78,7 @@ public class VideosController : BaseJellyfinApiController _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; _httpClientFactory = httpClientFactory; _encodingHelper = encodingHelper; } @@ -427,7 +427,7 @@ public class VideosController : BaseJellyfinApiController _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, _transcodingJobType, cancellationTokenSource.Token) .ConfigureAwait(false); @@ -466,7 +466,7 @@ public class VideosController : BaseJellyfinApiController if (state.MediaSource.IsInfiniteStream) { - var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper); + var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager); return File(liveStream, contentType); } @@ -482,7 +482,7 @@ public class VideosController : BaseJellyfinApiController state, isHeadRequest, HttpContext, - _transcodingJobHelper, + _transcodeManager, ffmpegCommandLineArguments, _transcodingJobType, cancellationTokenSource).ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 926ce99dd8..c80a9d582d 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -2,13 +2,13 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Http; @@ -26,7 +26,7 @@ public class AudioHelper private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpContextAccessor _httpContextAccessor; private readonly EncodingHelper _encodingHelper; @@ -39,7 +39,7 @@ public class AudioHelper /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of . + /// Instance of interface. /// Instance of the interface. /// Instance of the interface. /// Instance of . @@ -49,7 +49,7 @@ public class AudioHelper IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, EncodingHelper encodingHelper) @@ -59,7 +59,7 @@ public class AudioHelper _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; _httpClientFactory = httpClientFactory; _httpContextAccessor = httpContextAccessor; _encodingHelper = encodingHelper; @@ -94,7 +94,7 @@ public class AudioHelper _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, transcodingJobType, cancellationTokenSource.Token) .ConfigureAwait(false); @@ -133,7 +133,7 @@ public class AudioHelper if (state.MediaSource.IsInfiniteStream) { - var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper); + var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager); return new FileStreamResult(stream, contentType); } @@ -149,7 +149,7 @@ public class AudioHelper state, isHeadRequest, _httpContextAccessor.HttpContext, - _transcodingJobHelper, + _transcodeManager, ffmpegCommandLineArguments, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 05f7d44bf0..fa81fc284d 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -8,7 +8,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Extensions; -using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -18,6 +17,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Controller.Trickplay; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; @@ -39,7 +39,7 @@ public class DynamicHlsHelper private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; private readonly INetworkManager _networkManager; private readonly ILogger _logger; private readonly IHttpContextAccessor _httpContextAccessor; @@ -54,7 +54,7 @@ public class DynamicHlsHelper /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of . + /// Instance of . /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -66,7 +66,7 @@ public class DynamicHlsHelper IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, INetworkManager networkManager, ILogger logger, IHttpContextAccessor httpContextAccessor, @@ -78,7 +78,7 @@ public class DynamicHlsHelper _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; _networkManager = networkManager; _logger = logger; _httpContextAccessor = httpContextAccessor; @@ -130,7 +130,7 @@ public class DynamicHlsHelper _serverConfigurationManager, _mediaEncoder, _encodingHelper, - _transcodingJobHelper, + _transcodeManager, transcodingJobType, cancellationTokenSource.Token) .ConfigureAwait(false); diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index fafa2c0551..5385979d4a 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -4,8 +4,9 @@ using System.Net.Http; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Models.StreamingDtos; +using Jellyfin.Api.Extensions; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; @@ -64,7 +65,7 @@ public static class FileStreamResponseHelpers /// The current . /// Whether the current request is a HTTP HEAD request so only the headers get returned. /// The current http context. - /// The singleton. + /// The singleton. /// The command line arguments to start ffmpeg. /// The . /// The . @@ -73,7 +74,7 @@ public static class FileStreamResponseHelpers StreamState state, bool isHeadRequest, HttpContext httpContext, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, string ffmpegCommandLineArguments, TranscodingJobType transcodingJobType, CancellationTokenSource cancellationTokenSource) @@ -92,22 +93,28 @@ public static class FileStreamResponseHelpers return new OkResult(); } - var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath); + var transcodingLock = transcodeManager.GetTranscodingLock(outputPath); await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJob? job; if (!File.Exists(outputPath)) { - job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false); + job = await transcodeManager.StartFfMpeg( + state, + outputPath, + ffmpegCommandLineArguments, + httpContext.User.GetUserId(), + transcodingJobType, + cancellationTokenSource).ConfigureAwait(false); } else { - job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); + job = transcodeManager.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); state.Dispose(); } - var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper); + var stream = new ProgressiveFileStream(outputPath, job, transcodeManager); return new FileStreamResult(stream, contentType); } finally diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs index e2d3bfb193..c8a36c562e 100644 --- a/Jellyfin.Api/Helpers/HlsHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsHelpers.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs index 18088483d3..98ea844a96 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs +++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs @@ -15,7 +15,7 @@ public class ProgressiveFileStream : Stream { private readonly Stream _stream; private readonly TranscodingJob? _job; - private readonly TranscodingJobHelper? _transcodingJobHelper; + private readonly ITranscodeManager? _transcodeManager; private readonly int _timeoutMs; private bool _disposed; @@ -24,12 +24,12 @@ public class ProgressiveFileStream : Stream /// /// The path to the transcoded file. /// The transcoding job information. - /// The transcoding job helper. + /// The transcode manager. /// The timeout duration in milliseconds. - public ProgressiveFileStream(string filePath, TranscodingJob? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000) + public ProgressiveFileStream(string filePath, TranscodingJob? job, ITranscodeManager transcodeManager, int timeoutMs = 30000) { _job = job; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; _timeoutMs = timeoutMs; _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan); @@ -43,7 +43,7 @@ public class ProgressiveFileStream : Stream public ProgressiveFileStream(Stream stream, int timeoutMs = 30000) { _job = null; - _transcodingJobHelper = null; + _transcodeManager = null; _timeoutMs = timeoutMs; _stream = stream; } @@ -153,7 +153,7 @@ public class ProgressiveFileStream : Stream if (_job is not null) { - _transcodingJobHelper?.OnTranscodeEndRequest(_job); + _transcodeManager?.OnTranscodeEndRequest(_job); } } } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 71c62b2356..78943f7b58 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Extensions; -using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; @@ -14,6 +13,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -38,7 +38,7 @@ public static class StreamingHelpers /// Instance of the interface. /// Instance of the interface. /// Instance of . - /// Initialized . + /// Instance of the interface. /// The . /// The . /// A containing the current . @@ -51,7 +51,7 @@ public static class StreamingHelpers IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, EncodingHelper encodingHelper, - TranscodingJobHelper transcodingJobHelper, + ITranscodeManager transcodeManager, TranscodingJobType transcodingJobType, CancellationToken cancellationToken) { @@ -74,7 +74,7 @@ public static class StreamingHelpers streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url); } - var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper) + var state = new StreamState(mediaSourceManager, transcodingJobType, transcodeManager) { Request = streamingRequest, RequestedUrl = url, @@ -115,7 +115,7 @@ public static class StreamingHelpers if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId)) { var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId) - ? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId) + ? transcodeManager.GetTranscodingJob(streamingRequest.PlaySessionId) : null; if (currentJob is not null) diff --git a/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs index 4f1abb1ffb..bd176bb6a5 100644 --- a/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs +++ b/Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs @@ -1,4 +1,6 @@ -namespace Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Controller.Streaming; + +namespace Jellyfin.Api.Models.StreamingDtos; /// /// The hls video request dto. diff --git a/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs b/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs index 1cd3d01323..53b6d7575b 100644 --- a/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs +++ b/Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs @@ -1,4 +1,6 @@ -namespace Jellyfin.Api.Models.StreamingDtos; +using MediaBrowser.Controller.Streaming; + +namespace Jellyfin.Api.Models.StreamingDtos; /// /// The hls video request dto. diff --git a/MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs b/MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs new file mode 100644 index 0000000000..c19a12ae7a --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs @@ -0,0 +1,104 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Streaming; + +namespace MediaBrowser.Controller.MediaEncoding; + +/// +/// A service for managing media transcoding. +/// +public interface ITranscodeManager +{ + /// + /// Get transcoding job. + /// + /// Playback session id. + /// The transcoding job. + public TranscodingJob? GetTranscodingJob(string playSessionId); + + /// + /// Get transcoding job. + /// + /// Path to the transcoding file. + /// The . + /// The transcoding job. + public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type); + + /// + /// Ping transcoding job. + /// + /// Play session id. + /// Is user paused. + /// Play session id is null. + public void PingTranscodingJob(string playSessionId, bool? isUserPaused); + + /// + /// Kills the single transcoding job. + /// + /// The device id. + /// The play session identifier. + /// The delete files. + /// Task. + public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func deleteFiles); + + /// + /// Report the transcoding progress to the session manager. + /// + /// The of which the progress will be reported. + /// The of the current transcoding job. + /// The current transcoding position. + /// The framerate of the transcoding job. + /// The completion percentage of the transcode. + /// The number of bytes transcoded. + /// The bitrate of the transcoding job. + public void ReportTranscodingProgress( + TranscodingJob job, + StreamState state, + TimeSpan? transcodingPosition, + float? framerate, + double? percentComplete, + long? bytesTranscoded, + int? bitRate); + + /// + /// Starts FFMpeg. + /// + /// The state. + /// The output path. + /// The command line arguments for FFmpeg. + /// The user id. + /// The . + /// The cancellation token source. + /// The working directory. + /// Task. + public Task StartFfMpeg( + StreamState state, + string outputPath, + string commandLineArguments, + Guid userId, + TranscodingJobType transcodingJobType, + CancellationTokenSource cancellationTokenSource, + string? workingDirectory = null); + + /// + /// Called when [transcode begin request]. + /// + /// The path. + /// The type. + /// The . + public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type); + + /// + /// Called when [transcode end]. + /// + /// The transcode job. + public void OnTranscodeEndRequest(TranscodingJob job); + + /// + /// Gets the transcoding lock. + /// + /// The output path of the transcoded file. + /// A . + public SemaphoreSlim GetTranscodingLock(string outputPath); +} diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/MediaBrowser.Controller/Streaming/StreamState.cs similarity index 90% rename from Jellyfin.Api/Models/StreamingDtos/StreamState.cs rename to MediaBrowser.Controller/Streaming/StreamState.cs index 439f8052c1..b5dbe29ec7 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs +++ b/MediaBrowser.Controller/Streaming/StreamState.cs @@ -1,10 +1,9 @@ using System; -using Jellyfin.Api.Helpers; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; -namespace Jellyfin.Api.Models.StreamingDtos; +namespace MediaBrowser.Controller.Streaming; /// /// The stream state dto. @@ -12,7 +11,7 @@ namespace Jellyfin.Api.Models.StreamingDtos; public class StreamState : EncodingJobInfo, IDisposable { private readonly IMediaSourceManager _mediaSourceManager; - private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ITranscodeManager _transcodeManager; private bool _disposed; /// @@ -20,12 +19,12 @@ public class StreamState : EncodingJobInfo, IDisposable /// /// Instance of the interface. /// The . - /// The singleton. - public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper) + /// The singleton. + public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, ITranscodeManager transcodeManager) : base(transcodingType) { _mediaSourceManager = mediaSourceManager; - _transcodingJobHelper = transcodingJobHelper; + _transcodeManager = transcodeManager; } /// @@ -152,7 +151,7 @@ public class StreamState : EncodingJobInfo, IDisposable /// public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { - _transcodingJobHelper.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); + _transcodeManager.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); } /// diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs b/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs similarity index 96% rename from Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs rename to MediaBrowser.Controller/Streaming/StreamingRequestDto.cs index a357498d4c..e47ef65f06 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs +++ b/MediaBrowser.Controller/Streaming/StreamingRequestDto.cs @@ -1,6 +1,6 @@ using MediaBrowser.Controller.MediaEncoding; -namespace Jellyfin.Api.Models.StreamingDtos; +namespace MediaBrowser.Controller.Streaming; /// /// The audio streaming request dto. diff --git a/Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs b/MediaBrowser.Controller/Streaming/VideoRequestDto.cs similarity index 94% rename from Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs rename to MediaBrowser.Controller/Streaming/VideoRequestDto.cs index 8548fec1a1..44dc831fdc 100644 --- a/Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs +++ b/MediaBrowser.Controller/Streaming/VideoRequestDto.cs @@ -1,4 +1,4 @@ -namespace Jellyfin.Api.Models.StreamingDtos; +namespace MediaBrowser.Controller.Streaming; /// /// The video request dto. diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs similarity index 71% rename from Jellyfin.Api/Helpers/TranscodingJobHelper.cs rename to MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index 9a6ec17fdb..483d0a1d82 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -8,8 +8,6 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Extensions; -using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -18,93 +16,77 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Session; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Session; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Helpers; +namespace MediaBrowser.MediaEncoding.Transcoding; -/// -/// Transcoding job helpers. -/// -public class TranscodingJobHelper : IDisposable +/// +public sealed class TranscodeManager : ITranscodeManager, IDisposable { - /// - /// The active transcoding jobs. - /// - private static readonly List _activeTranscodingJobs = new List(); - - /// - /// The transcoding locks. - /// - private static readonly Dictionary _transcodingLocks = new Dictionary(); - - private readonly IAttachmentExtractor _attachmentExtractor; - private readonly IApplicationPaths _appPaths; - private readonly EncodingHelper _encodingHelper; + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; + private readonly IApplicationPaths _appPaths; + private readonly IServerConfigurationManager _serverConfigurationManager; + private readonly IUserManager _userManager; + private readonly ISessionManager _sessionManager; + private readonly EncodingHelper _encodingHelper; private readonly IMediaEncoder _mediaEncoder; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly ISessionManager _sessionManager; - private readonly ILoggerFactory _loggerFactory; - private readonly IUserManager _userManager; + private readonly IAttachmentExtractor _attachmentExtractor; + + private readonly List _activeTranscodingJobs = new(); + private readonly Dictionary _transcodingLocks = new(); /// - /// Initializes a new instance of the class. + /// 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. - /// 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, - ILogger logger, - IMediaSourceManager mediaSourceManager, + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . + /// The . + public TranscodeManager( + ILoggerFactory loggerFactory, IFileSystem fileSystem, - IMediaEncoder mediaEncoder, + IApplicationPaths appPaths, IServerConfigurationManager serverConfigurationManager, + IUserManager userManager, ISessionManager sessionManager, EncodingHelper encodingHelper, - ILoggerFactory loggerFactory, - IUserManager userManager) + IMediaEncoder mediaEncoder, + IMediaSourceManager mediaSourceManager, + IAttachmentExtractor attachmentExtractor) { - _attachmentExtractor = attachmentExtractor; - _appPaths = appPaths; - _logger = logger; - _mediaSourceManager = mediaSourceManager; + _loggerFactory = loggerFactory; _fileSystem = fileSystem; - _mediaEncoder = mediaEncoder; + _appPaths = appPaths; _serverConfigurationManager = serverConfigurationManager; + _userManager = userManager; _sessionManager = sessionManager; _encodingHelper = encodingHelper; - _loggerFactory = loggerFactory; - _userManager = userManager; + _mediaEncoder = mediaEncoder; + _mediaSourceManager = mediaSourceManager; + _attachmentExtractor = attachmentExtractor; + _logger = loggerFactory.CreateLogger(); DeleteEncodedMediaCache(); - - sessionManager.PlaybackProgress += OnPlaybackProgress; - sessionManager.PlaybackStart += OnPlaybackProgress; + _sessionManager.PlaybackProgress += OnPlaybackProgress; + _sessionManager.PlaybackStart += OnPlaybackProgress; } - /// - /// Get transcoding job. - /// - /// Playback session id. - /// The transcoding job. + /// public TranscodingJob? GetTranscodingJob(string playSessionId) { lock (_activeTranscodingJobs) @@ -113,12 +95,7 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Get transcoding job. - /// - /// Path to the transcoding file. - /// The . - /// The transcoding job. + /// public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) @@ -127,12 +104,7 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Ping transcoding job. - /// - /// Play session id. - /// Is user paused. - /// Play session id is null. + /// public void PingTranscodingJob(string playSessionId, bool? isUserPaused) { ArgumentException.ThrowIfNullOrEmpty(playSessionId); @@ -189,10 +161,6 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Called when [transcode kill timer stopped]. - /// - /// The state. private async void OnTranscodeKillTimerStopped(object? state) { var job = state as TranscodingJob ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJob)}", nameof(state)); @@ -212,29 +180,8 @@ public class TranscodingJobHelper : IDisposable await KillTranscodingJob(job, true, path => true).ConfigureAwait(false); } - /// - /// Kills the single transcoding job. - /// - /// The device id. - /// The play session identifier. - /// The delete files. - /// Task. + /// public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func deleteFiles) - { - return KillTranscodingJobs( - j => string.IsNullOrWhiteSpace(playSessionId) - ? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase) - : string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase), - deleteFiles); - } - - /// - /// Kills the transcoding jobs. - /// - /// The kill job. - /// The delete files. - /// Task. - private Task KillTranscodingJobs(Func killJob, Func deleteFiles) { var jobs = new List(); @@ -242,13 +189,12 @@ public class TranscodingJobHelper : IDisposable { // This is really only needed for HLS. // Progressive streams can stop on their own reliably. - jobs.AddRange(_activeTranscodingJobs.Where(killJob)); + jobs.AddRange(_activeTranscodingJobs.Where(j => string.IsNullOrWhiteSpace(playSessionId) + ? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase) + : string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase))); } - if (jobs.Count == 0) - { - return Task.CompletedTask; - } + return Task.WhenAll(GetKillJobs()); IEnumerable GetKillJobs() { @@ -257,16 +203,8 @@ public class TranscodingJobHelper : IDisposable yield return KillTranscodingJob(job, false, deleteFiles); } } - - return Task.WhenAll(GetKillJobs()); } - /// - /// Kills the transcoding job. - /// - /// The job. - /// if set to true [close live stream]. - /// The delete. private async Task KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func delete) { job.DisposeKillTimer(); @@ -353,10 +291,6 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Deletes the progressive partial stream files. - /// - /// The output file path. private void DeleteProgressivePartialStreamFiles(string outputFilePath) { if (File.Exists(outputFilePath)) @@ -365,10 +299,6 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Deletes the HLS partial stream files. - /// - /// The output file path. private void DeleteHlsPartialStreamFiles(string outputFilePath) { var directory = Path.GetDirectoryName(outputFilePath) @@ -400,16 +330,7 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Report the transcoding progress to the session manager. - /// - /// The of which the progress will be reported. - /// The of the current transcoding job. - /// The current transcoding position. - /// The framerate of the transcoding job. - /// The completion percentage of the transcode. - /// The number of bytes transcoded. - /// The bitrate of the transcoding job. + /// public void ReportTranscodingProgress( TranscodingJob job, StreamState state, @@ -462,22 +383,12 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Starts FFmpeg. - /// - /// The state. - /// The output path. - /// The command line arguments for FFmpeg. - /// The . - /// The . - /// The cancellation token source. - /// The working directory. - /// Task. + /// public async Task StartFfMpeg( StreamState state, string outputPath, string commandLineArguments, - HttpRequest request, + Guid userId, TranscodingJobType transcodingJobType, CancellationTokenSource cancellationTokenSource, string? workingDirectory = null) @@ -489,7 +400,6 @@ public class TranscodingJobHelper : IDisposable if (state.VideoRequest is not null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { - var userId = request.HttpContext.User.GetUserId(); var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); if (user is not null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) { @@ -567,13 +477,26 @@ public class TranscodingJobHelper : IDisposable $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log"); // FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); + Stream logStream = new FileStream( + logFilePath, + FileMode.Create, + FileAccess.Write, + FileShare.Read, + IODefaults.FileStreamBufferSize, + FileOptions.Asynchronous); var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments; - var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); + var commandLineLogMessageBytes = Encoding.UTF8.GetBytes( + JsonSerializer.Serialize(state.MediaSource) + + Environment.NewLine + + Environment.NewLine + + commandLineLogMessage + + Environment.NewLine + + Environment.NewLine); + await logStream.WriteAsync(commandLineLogMessageBytes, cancellationTokenSource.Token).ConfigureAwait(false); - process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state); + process.Exited += (_, _) => OnFfMpegProcessExited(process, transcodingJob, state); try { @@ -582,7 +505,6 @@ public class TranscodingJobHelper : IDisposable catch (Exception ex) { _logger.LogError(ex, "Error starting FFmpeg"); - this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state); throw; @@ -637,31 +559,14 @@ public class TranscodingJobHelper : IDisposable } } - private bool EnableThrottling(StreamState state) - { - var encodingOptions = _serverConfigurationManager.GetEncodingOptions(); + private static bool EnableThrottling(StreamState state) + => state.InputProtocol == MediaProtocol.File + && state.RunTimeTicks.HasValue + && state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks + && state.IsInputVideo + && state.VideoType == VideoType.VideoFile; - return state.InputProtocol == MediaProtocol.File && - state.RunTimeTicks.HasValue && - state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && - state.IsInputVideo && - state.VideoType == VideoType.VideoFile; - } - - /// - /// Called when [transcode beginning]. - /// - /// The path. - /// The play session identifier. - /// The live stream identifier. - /// The transcoding job identifier. - /// The type. - /// The process. - /// The device id. - /// The state. - /// The cancellation token source. - /// TranscodingJob. - public TranscodingJob OnTranscodeBeginning( + private TranscodingJob OnTranscodeBeginning( string path, string? playSessionId, string? liveStreamId, @@ -696,10 +601,7 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Called when [transcode end]. - /// - /// The transcode job. + /// public void OnTranscodeEndRequest(TranscodingJob job) { job.ActiveRequestCount--; @@ -710,16 +612,7 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// - /// The progressive - /// - /// Called when [transcode failed to start]. - /// - /// The path. - /// The type. - /// The state. - public void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state) + private void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state) { lock (_activeTranscodingJobs) { @@ -742,12 +635,6 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Processes the exited. - /// - /// The process. - /// The job. - /// The state. private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state) { job.HasExited = true; @@ -794,44 +681,30 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Called when [transcode begin request]. - /// - /// The path. - /// The type. - /// The . + /// public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type) { lock (_activeTranscodingJobs) { - var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase)); + var job = _activeTranscodingJobs + .FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase)); if (job is null) { return null; } - OnTranscodeBeginRequest(job); + job.ActiveRequestCount++; + if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive) + { + job.StopKillTimer(); + } return job; } } - private void OnTranscodeBeginRequest(TranscodingJob job) - { - job.ActiveRequestCount++; - - if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive) - { - job.StopKillTimer(); - } - } - - /// - /// Gets the transcoding lock. - /// - /// The output path of the transcoded file. - /// A . + /// public SemaphoreSlim GetTranscodingLock(string outputPath) { lock (_transcodingLocks) @@ -854,9 +727,6 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Deletes the encoded media cache. - /// private void DeleteEncodedMediaCache() { var path = _serverConfigurationManager.GetTranscodePath(); @@ -871,26 +741,10 @@ public class TranscodingJobHelper : IDisposable } } - /// - /// Dispose transcoding job helper. - /// + /// public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose throttler. - /// - /// Disposing. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _loggerFactory.Dispose(); - _sessionManager.PlaybackProgress -= OnPlaybackProgress; - _sessionManager.PlaybackStart -= OnPlaybackProgress; - } + _sessionManager.PlaybackProgress -= OnPlaybackProgress; + _sessionManager.PlaybackStart -= OnPlaybackProgress; } } From c49539cbe0152d23d82d7710ff2bc32e5d3d187b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 21 Dec 2023 12:20:30 -0500 Subject: [PATCH 053/132] Move ProgressiveFileStream to Controller --- Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs | 1 + Jellyfin.Api/Controllers/LiveTvController.cs | 1 + .../Streaming}/ProgressiveFileStream.cs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) rename {Jellyfin.Api/Helpers => MediaBrowser.Controller/Streaming}/ProgressiveFileStream.cs (99%) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index ddf7b882a7..7df66d3585 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index a40f273ae4..5502836239 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -25,6 +25,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs similarity index 99% rename from Jellyfin.Api/Helpers/ProgressiveFileStream.cs rename to MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs index 98ea844a96..f44dc92d71 100644 --- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs +++ b/MediaBrowser.Controller/Streaming/ProgressiveFileStream.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; -namespace Jellyfin.Api.Helpers; +namespace MediaBrowser.Controller.Streaming; /// /// A progressive file stream for transferring transcoded files as they are written to. From 0ca263a142a9635eef3075c5288acbf0c5fa75bd Mon Sep 17 00:00:00 2001 From: Peyman M Date: Thu, 21 Dec 2023 01:24:34 +0000 Subject: [PATCH 054/132] Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fa/ --- Emby.Server.Implementations/Localization/Core/fa.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index 8e4bba25b6..8364ce2360 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -124,5 +124,7 @@ "TaskKeyframeExtractorDescription": "فریم های کلیدی را از فایل های ویدئویی استخراج می کند تا لیست های پخش HLS دقیق تری ایجاد کند. این کار ممکن است برای مدت طولانی اجرا شود.", "TaskKeyframeExtractor": "استخراج کننده فریم کلیدی", "External": "خارجی", - "HearingImpaired": "مشکل شنوایی" + "HearingImpaired": "مشکل شنوایی", + "TaskRefreshTrickplayImages": "تولید تصاویر Trickplay", + "TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه." } From 9fdbb71a50486d1bdaaf9170db3fc9bb577db1ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 01:50:13 +0000 Subject: [PATCH 055/132] chore(deps): update github/codeql-action action to v3.22.12 --- .github/workflows/ci-codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml index 89f9c59d78..7c07335980 100644 --- a/.github/workflows/ci-codeql-analysis.yml +++ b/.github/workflows/ci-codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '8.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11 + uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11 + uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11 + uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 From 836c2032fc6f023ac3d09adf2ed5dfacd37076c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Dec 2023 01:52:09 +0000 Subject: [PATCH 056/132] chore(deps): update xunit-dotnet monorepo --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0c234bcf8b..90280d98bd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -83,8 +83,8 @@ - + - + From c41ecba2cd864e97df50a4eda848fbd696823bb5 Mon Sep 17 00:00:00 2001 From: Stephen Mwangi Date: Sat, 23 Dec 2023 07:49:02 +0300 Subject: [PATCH 057/132] Fix Somalia's display name --- Emby.Server.Implementations/Localization/countries.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/countries.json b/Emby.Server.Implementations/Localization/countries.json index 22ffc5e097..0a11b3e458 100644 --- a/Emby.Server.Implementations/Localization/countries.json +++ b/Emby.Server.Implementations/Localization/countries.json @@ -696,7 +696,7 @@ "TwoLetterISORegionName": "SI" }, { - "DisplayName": "Soomaaliya", + "DisplayName": "Somalia", "Name": "SO", "ThreeLetterISORegionName": "SOM", "TwoLetterISORegionName": "SO" From a3a071154533206957e358adafb40f765fa946b1 Mon Sep 17 00:00:00 2001 From: Sergey Date: Sat, 23 Dec 2023 16:06:09 -0500 Subject: [PATCH 058/132] Added translation using Weblate (Armenian) --- Emby.Server.Implementations/Localization/Core/hy.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/hy.json diff --git a/Emby.Server.Implementations/Localization/Core/hy.json b/Emby.Server.Implementations/Localization/Core/hy.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/hy.json @@ -0,0 +1 @@ +{} From 0c9b1f1ebfe6e14c0b9dc48d23f57c97bb144261 Mon Sep 17 00:00:00 2001 From: Sergey Date: Sat, 23 Dec 2023 21:19:09 +0000 Subject: [PATCH 059/132] Translated using Weblate (Armenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hy/ --- .../Localization/Core/hy.json | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/hy.json b/Emby.Server.Implementations/Localization/Core/hy.json index 0967ef424b..c348836fe6 100644 --- a/Emby.Server.Implementations/Localization/Core/hy.json +++ b/Emby.Server.Implementations/Localization/Core/hy.json @@ -1 +1,35 @@ -{} +{ + "TasksLibraryCategory": "Գրադարան", + "TasksApplicationCategory": "Հավելված", + "TaskCleanActivityLog": "Մաքրել ակտիվության մատյանը", + "Application": "Հավելված", + "AuthenticationSucceededWithUserName": "{0} հաջողությամբ վավերականացվել են", + "Books": "Գրքեր", + "CameraImageUploadedFrom": "Նոր լուսանկար է վերբեռնվել {0}-ի կողմից", + "Channels": "Ալիքներ", + "DeviceOfflineWithName": "{0}ը անջատվեց", + "External": "Արտաքին", + "FailedLoginAttemptWithUserName": "Ձախողված մուտքի փործ {0}-ի կողմից", + "Folders": "Պանակներ", + "HeaderContinueWatching": "Շարունակել դիտումը", + "Inherit": "Ժառանգել", + "ItemAddedWithName": "{0}ը ավացված է գրադարանի մեջ", + "ItemRemovedWithName": "{0}ը հեռացված է գրադարանից", + "LabelIpAddressValue": "IP հասցե` {0}", + "Movies": "Ֆիլմեր", + "Music": "Երաժշտություն", + "NameSeasonNumber": "Սեզոն {0}", + "Photos": "Լուսանկարներ", + "PluginInstalledWithName": "{0}ն տեղադրված է", + "Songs": "Երգեր", + "System": "Համակարգ", + "TvShows": "Հեռուստասերիալներ", + "User": "Օգտատեր", + "VersionNumber": "Տարբերակ {0}", + "TasksMaintenanceCategory": "Սպասարկում", + "TasksChannelsCategory": "Ինտերնետային ալիքներ", + "TaskRefreshPeople": "Թարմացնել մարդկանց", + "TaskRefreshChannels": "Թարմացնել ալիքները", + "TaskDownloadMissingSubtitles": "Ներբեռնել պակասող ենթագրերը", + "Albums": "Ալբոմներ" +} From 131db9b3806d273fdd20f7adb991a6aef4566f5e Mon Sep 17 00:00:00 2001 From: queeup Date: Wed, 27 Dec 2023 14:44:51 +0000 Subject: [PATCH 060/132] Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- Emby.Server.Implementations/Localization/Core/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 6a04115faf..d7a627d127 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -111,7 +111,7 @@ "TaskCleanLogs": "Günlük Dizinini Temizle", "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve ortam bilgilerini yeniler.", "TaskRefreshLibrary": "Medya Kütüphanesini Tara", - "TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.", + "TaskRefreshChapterImagesDescription": "Bölümlere ayrılmış videolar için küçük resimler oluştur.", "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar", "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.", "TaskCleanActivityLog": "Etkinlik Günlüğünü Temizle", From 260fe6890c1fb2db91bbb97310ef1256984dbdac Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 26 Dec 2023 19:56:55 +0000 Subject: [PATCH 061/132] Translated using Weblate (Armenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hy/ --- Emby.Server.Implementations/Localization/Core/hy.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/hy.json b/Emby.Server.Implementations/Localization/Core/hy.json index c348836fe6..563f842923 100644 --- a/Emby.Server.Implementations/Localization/Core/hy.json +++ b/Emby.Server.Implementations/Localization/Core/hy.json @@ -31,5 +31,9 @@ "TaskRefreshPeople": "Թարմացնել մարդկանց", "TaskRefreshChannels": "Թարմացնել ալիքները", "TaskDownloadMissingSubtitles": "Ներբեռնել պակասող ենթագրերը", - "Albums": "Ալբոմներ" + "Albums": "Ալբոմներ", + "AppDeviceValues": "Հավելված` {0}, Սարք `{1}", + "ChapterNameValue": "Գլուխ {0}", + "Collections": "Հավաքածուներ", + "DeviceOnlineWithName": "{0}-ն միացված է" } From 909d31c8378e664d3575ffab795eb48967781dc2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 31 Dec 2023 15:05:19 +0100 Subject: [PATCH 062/132] Fix broken code coverage --- tests/coverletArgs.runsettings | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/coverletArgs.runsettings b/tests/coverletArgs.runsettings index 3113957e0a..8e97894487 100644 --- a/tests/coverletArgs.runsettings +++ b/tests/coverletArgs.runsettings @@ -4,14 +4,15 @@ - cobertura + cobertura [coverlet.*.tests?]*,[*]Coverlet.Core*,[*]Moq* Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute false - true + false false + - \ No newline at end of file + From 19fa91bfa36e4f1cb164c10fdc7b12d4240e4bc5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 31 Dec 2023 15:24:23 +0100 Subject: [PATCH 063/132] SkipAutoProps is true by default but set it explicitly --- tests/coverletArgs.runsettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/coverletArgs.runsettings b/tests/coverletArgs.runsettings index 8e97894487..7d48373896 100644 --- a/tests/coverletArgs.runsettings +++ b/tests/coverletArgs.runsettings @@ -10,7 +10,7 @@ false false false - + true From 71e028155c062442119af0b5febd5e549f397243 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 16:30:15 +0000 Subject: [PATCH 064/132] chore(deps): update dependency metabrainz.musicbrainz to v6 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 90280d98bd..6b99ac807e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From e9361d47c0c2dc5032340cc04a56afa04b6e583e Mon Sep 17 00:00:00 2001 From: bene toffix Date: Sun, 31 Dec 2023 18:12:30 +0000 Subject: [PATCH 065/132] Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 26290df4d8..c4d8c69479 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -124,5 +124,7 @@ "TaskKeyframeExtractorDescription": "Extreu fotogrames clau dels fitxers de vídeo per crear llistes de reproducció HLS més precises. Aquesta tasca pot durar molt de temps.", "TaskKeyframeExtractor": "Extractor de fotogrames clau", "External": "Extern", - "HearingImpaired": "Discapacitat auditiva" + "HearingImpaired": "Discapacitat auditiva", + "TaskRefreshTrickplayImages": "Generar miniatures de línia de temps", + "TaskRefreshTrickplayImagesDescription": "Crear miniatures de línia de temps per vídeos en les biblioteques habilitades." } From 9fb67424004166423f5857ca01d0f3b524c27b7a Mon Sep 17 00:00:00 2001 From: Dr Ajay Divvela Date: Tue, 2 Jan 2024 03:16:56 +0000 Subject: [PATCH 066/132] Translated using Weblate (Telugu) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/te/ --- .../Localization/Core/te.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/te.json b/Emby.Server.Implementations/Localization/Core/te.json index 24168b6112..7d4422d62f 100644 --- a/Emby.Server.Implementations/Localization/Core/te.json +++ b/Emby.Server.Implementations/Localization/Core/te.json @@ -38,5 +38,18 @@ "HeaderFavoriteSongs": "ఇష్టమైన పాటలు", "HeaderLiveTV": "ప్రత్యక్ష TV", "HeaderNextUp": "తదుపరి", - "HeaderRecordingGroups": "రికార్డింగ్ గుంపులు" + "HeaderRecordingGroups": "రికార్డింగ్ గుంపులు", + "MessageApplicationUpdated": "జెల్లీఫిన్ సర్వర్ అప్‌డేట్ చేయడం పూర్తి అయ్యింది", + "MessageApplicationUpdatedTo": "జెల్లీఫిన్ సర్వర్ {0} వెర్షన్ కి అప్‌డేట్ చెయ్యబడింది", + "MessageServerConfigurationUpdated": "సర్వర్ కన్ఫిగరేషన్ అప్డేట్ చేయబడింది", + "NewVersionIsAvailable": "జెల్లీఫిన్ సర్వర్ యొక్క కొత్త వెర్షన్ డౌన్‌లోడ్ చేసుకోవడానికి అందుబాటులో ఉంది.", + "NotificationOptionApplicationUpdateInstalled": "అప్లికేషన్ అప్‌డేట్ ఇన్‌స్టాల్ చేయబడింది", + "ItemAddedWithName": "{0} లైబ్రరీకి జోడించబడింది", + "ItemRemovedWithName": "లైబ్రరీ నుండి {0} తీసివేయబడింది", + "LabelIpAddressValue": "ఐపీ చిరునామా: {0}", + "LabelRunningTimeValue": "నడుస్తున్న సమయం: {0}", + "Latest": "తాజా", + "NameInstallFailed": "{0} ఇన్‌స్టాలేషన్ విఫలమైంది", + "NameSeasonUnknown": "భాగం తెలియదు", + "NotificationOptionApplicationUpdateAvailable": "అప్లికేషన్ అప్‌డేట్ అందుబాటులో ఉంది" } From 47cdfd796679c0bf03e73c9723256fa13218265c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 23:01:22 +0000 Subject: [PATCH 067/132] chore(deps): update alex-page/github-project-automation-plus action to v0.9.0 --- .github/workflows/project-automation.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml index 3637eb16ad..d62f655b30 100644 --- a/.github/workflows/project-automation.yml +++ b/.github/workflows/project-automation.yml @@ -15,7 +15,7 @@ jobs: if: ${{ github.repository == 'jellyfin/jellyfin' }} steps: - name: Remove from 'Current Release' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') continue-on-error: true with: @@ -24,7 +24,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add to 'Release Next' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' continue-on-error: true with: @@ -33,7 +33,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add to 'Current Release' project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') continue-on-error: true with: @@ -47,7 +47,7 @@ jobs: run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" - name: Move issue to needs triage - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 continue-on-error: true with: @@ -56,7 +56,7 @@ jobs: repo-token: ${{ secrets.JF_BOT_TOKEN }} - name: Add issue to triage project - uses: alex-page/github-project-automation-plus@7ffb872c64bd809d23563a130a0a97d01dfa8f43 # v0.8.3 + uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36 # v0.9.0 if: github.event.issue.pull_request == '' && github.event.action == 'opened' continue-on-error: true with: From 29b559089cc63b7d0e38ff349efaa95eaa54d3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Wed, 3 Jan 2024 16:39:49 +0000 Subject: [PATCH 068/132] Translated using Weblate (Czech) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/ --- Emby.Server.Implementations/Localization/Core/cs.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 5da33febe3..1c7bc75b5c 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -83,8 +83,8 @@ "UserDeletedWithName": "Uživatel {0} byl smazán", "UserDownloadingItemWithValues": "{0} stahuje {1}", "UserLockedOutWithName": "Uživatel {0} byl odemčen", - "UserOfflineFromDevice": "{0} se odpojil od {1}", - "UserOnlineFromDevice": "{0} se připojil z {1}", + "UserOfflineFromDevice": "{0} se odpojil ze zařízení {1}", + "UserOnlineFromDevice": "{0} se připojil ze zařízení {1}", "UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}", "UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány", "UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}", From aa71129cffd8d1faf8f862449ae98c5c7888ba53 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 5 Jan 2024 07:31:45 +0800 Subject: [PATCH 069/132] Use -noauto{scale,rotate} for disabling auto filters (#10810) `-auto{scale,rotate} 0` has been dropped in upstream FFmpeg. Signed-off-by: nyanmisaka --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 6a16d421cb..400e7f40fb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1068,7 +1068,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // hw transpose filters should be added manually. - args.Append(" -autorotate 0"); + args.Append(" -noautorotate"); return args.ToString().Trim(); } @@ -1159,7 +1159,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options)); if (!isSwDecoder && _mediaEncoder.EncoderVersion >= new Version(4, 4)) { - arg.Append(" -autoscale 0"); + arg.Append(" -noautoscale"); } return arg.ToString(); From b39dc1eb016e0801d8cd6778045ba2c8c58dc614 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:46:57 -0700 Subject: [PATCH 070/132] chore(deps): update prometheus-net monorepo to v8.2.1 (#10804) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6b99ac807e..11e7f17c65 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -54,9 +54,9 @@ - + - + From a23ead735abb2816a2a2813b3defb2a10c489b16 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 07:39:13 +0000 Subject: [PATCH 071/132] chore(deps): update dependency xunit to v2.6.5 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 11e7f17c65..707b6ee5ff 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -85,6 +85,6 @@ - + From 6046512a4fc5024a8e7fa2a03e1c3cc57ec1c28c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 12:45:10 +0000 Subject: [PATCH 072/132] chore(deps): update dependency playlistsnet to v1.4.1 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 11e7f17c65..abc7a5d2c1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -53,7 +53,7 @@ - + From a45a8e34a7f1906ee45ea4db9cfc50c3b9d30aa8 Mon Sep 17 00:00:00 2001 From: Vasyl Belynets Date: Fri, 5 Jan 2024 11:00:37 +0000 Subject: [PATCH 073/132] Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- Emby.Server.Implementations/Localization/Core/uk.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index bd5398f083..4c3bf2f10b 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -22,12 +22,12 @@ "Favorites": "Обрані", "DeviceOnlineWithName": "Пристрій {0} підключився", "DeviceOfflineWithName": "Пристрій {0} відключився", - "Collections": "Добірки", - "ChapterNameValue": "Розділ {0}", + "Collections": "Колекції", + "ChapterNameValue": "Сцена {0}", "Channels": "Канали", - "CameraImageUploadedFrom": "Нова фотографія завантажена з {0}", + "CameraImageUploadedFrom": "Нову фотографію завантажено з {0}", "Books": "Книги", - "AuthenticationSucceededWithUserName": "{0} успішно автентифіковано", + "AuthenticationSucceededWithUserName": "{0} успішно авторизовано", "Artists": "Виконавці", "Application": "Додаток", "AppDeviceValues": "Додаток: {0}, Пристрій: {1}", From ef955818bfc33159203bf1fc27a49f336fd11edd Mon Sep 17 00:00:00 2001 From: newton181 Date: Thu, 4 Jan 2024 21:30:48 +0000 Subject: [PATCH 074/132] Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/ --- Emby.Server.Implementations/Localization/Core/es_419.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json index 3d5c046336..c6863ff368 100644 --- a/Emby.Server.Implementations/Localization/Core/es_419.json +++ b/Emby.Server.Implementations/Localization/Core/es_419.json @@ -123,5 +123,7 @@ "External": "Externo", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.", "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", - "HearingImpaired": "Discapacidad auditiva" + "HearingImpaired": "Discapacidad auditiva", + "TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.", + "TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción" } From f6b865f03a035859adf45352dd57d27a756b2dee Mon Sep 17 00:00:00 2001 From: George Salukvadze Date: Thu, 4 Jan 2024 17:43:40 +0000 Subject: [PATCH 075/132] Translated using Weblate (Georgian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ka/ --- Emby.Server.Implementations/Localization/Core/ka.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json index dbbc81eeb2..5368b8eb6e 100644 --- a/Emby.Server.Implementations/Localization/Core/ka.json +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -123,5 +123,7 @@ "TaskUpdatePluginsDescription": "ავტომატურად განახლებადად მონიშნული დამატებების განახლებების გადმოწერა და დაყენება.", "TaskCleanTranscodeDescription": "ერთ დღეზე უფრო ძველი ტრანსკოდირების ფაილების წაშლა.", "TaskDownloadMissingSubtitlesDescription": "მეტამონაცემებზე დაყრდნობით ინტერნეტში ნაკლული სუბტიტრების ძებნა.", - "TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს." + "TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს.", + "TaskRefreshTrickplayImagesDescription": "ქმნის trickplay წინასწარ ხედებს ვიდეოებისთვის ჩართულ ბიბლიოთეკებში.", + "TaskRefreshTrickplayImages": "Trickplay სურათების გენერირება" } From 55916a09eb4c88b12906e78d56efb7a67d3dad7e Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sat, 6 Jan 2024 21:33:48 +0100 Subject: [PATCH 076/132] Remove some unused client capabilities and sync code (#10812) --- Emby.Server.Implementations/Dto/DtoService.cs | 9 --------- .../LiveTv/LiveTvManager.cs | 1 - .../Session/SessionManager.cs | 1 - Jellyfin.Api/Controllers/DevicesController.cs | 5 ++--- Jellyfin.Api/Controllers/SessionController.cs | 3 --- .../Controllers/UserViewsController.cs | 1 - .../SessionDtos/ClientCapabilitiesDto.cs | 18 ------------------ .../Devices/DeviceManager.cs | 6 +----- .../Devices/IDeviceManager.cs | 3 +-- MediaBrowser.Controller/Entities/BaseItem.cs | 2 -- MediaBrowser.Model/Dto/BaseItemDto.cs | 5 ----- MediaBrowser.Model/Querying/ItemFields.cs | 7 ------- .../Session/ClientCapabilities.cs | 6 ------ 13 files changed, 4 insertions(+), 63 deletions(-) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 44b97e8b83..d0d5bb81c1 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -418,15 +418,6 @@ namespace Emby.Server.Implementations.Dto { dto.PlayAccess = item.GetPlayAccess(user); } - - if (options.ContainsField(ItemFields.BasicSyncInfo)) - { - var userCanSync = user is not null && user.HasPermission(PermissionKind.EnableContentDownloading); - if (userCanSync && item.SupportsExternalTransfer) - { - dto.SupportsSync = true; - } - } } private static int GetChildCount(Folder folder, User user) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 0544e2a4b7..ca8e204d7c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1095,7 +1095,6 @@ namespace Emby.Server.Implementations.LiveTv // Load these now which will prefetch metadata var dtoOptions = new DtoOptions(); var fields = dtoOptions.Fields.ToList(); - fields.Remove(ItemFields.BasicSyncInfo); dtoOptions.Fields = fields.ToArray(); progress.Report(100); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e8e63d286d..6f599e4c7c 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1670,7 +1670,6 @@ namespace Emby.Server.Implementations.Session var fields = dtoOptions.Fields.ToList(); - fields.Remove(ItemFields.BasicSyncInfo); fields.Remove(ItemFields.CanDelete); fields.Remove(ItemFields.CanDownload); fields.Remove(ItemFields.ChildCount); diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs index aa200a7221..6d9ec343e0 100644 --- a/Jellyfin.Api/Controllers/DevicesController.cs +++ b/Jellyfin.Api/Controllers/DevicesController.cs @@ -42,16 +42,15 @@ public class DevicesController : BaseJellyfinApiController /// /// Get Devices. /// - /// Gets or sets a value indicating whether [supports synchronize]. /// Gets or sets the user identifier. /// Devices retrieved. /// An containing the list of devices. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetDevices([FromQuery] bool? supportsSync, [FromQuery] Guid? userId) + public async Task>> GetDevices([FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - return await _deviceManager.GetDevicesForUser(userId, supportsSync).ConfigureAwait(false); + return await _deviceManager.GetDevicesForUser(userId).ConfigureAwait(false); } /// diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index fdebb3d450..083515a949 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -385,7 +385,6 @@ public class SessionController : BaseJellyfinApiController /// A list of playable media types, comma delimited. Audio, Video, Book, Photo. /// A list of supported remote control commands, comma delimited. /// Determines whether media can be played remotely.. - /// Determines whether sync is supported. /// Determines whether the device supports a unique identifier. /// Capabilities posted. /// A . @@ -397,7 +396,6 @@ public class SessionController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] playableMediaTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] GeneralCommandType[] supportedCommands, [FromQuery] bool supportsMediaControl = false, - [FromQuery] bool supportsSync = false, [FromQuery] bool supportsPersistentIdentifier = true) { if (string.IsNullOrWhiteSpace(id)) @@ -410,7 +408,6 @@ public class SessionController : BaseJellyfinApiController PlayableMediaTypes = playableMediaTypes, SupportedCommands = supportedCommands, SupportsMediaControl = supportsMediaControl, - SupportsSync = supportsSync, SupportsPersistentIdentifier = supportsPersistentIdentifier }); return NoContent(); diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 0ffa3ab1ad..035d044741 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -90,7 +90,6 @@ public class UserViewsController : BaseJellyfinApiController fields.Add(ItemFields.PrimaryImageAspectRatio); fields.Add(ItemFields.DisplayPreferencesId); - fields.Remove(ItemFields.BasicSyncInfo); dtoOptions.Fields = fields.ToArray(); var user = _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs index b021771a0f..acd3f29e34 100644 --- a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs +++ b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs @@ -30,26 +30,11 @@ public class ClientCapabilitiesDto /// public bool SupportsMediaControl { get; set; } - /// - /// Gets or sets a value indicating whether session supports content uploading. - /// - public bool SupportsContentUploading { get; set; } - - /// - /// Gets or sets the message callback url. - /// - public string? MessageCallbackUrl { get; set; } - /// /// Gets or sets a value indicating whether session supports a persistent identifier. /// public bool SupportsPersistentIdentifier { get; set; } - /// - /// Gets or sets a value indicating whether session supports sync. - /// - public bool SupportsSync { get; set; } - /// /// Gets or sets the device profile. /// @@ -76,10 +61,7 @@ public class ClientCapabilitiesDto PlayableMediaTypes = PlayableMediaTypes, SupportedCommands = SupportedCommands, SupportsMediaControl = SupportsMediaControl, - SupportsContentUploading = SupportsContentUploading, - MessageCallbackUrl = MessageCallbackUrl, SupportsPersistentIdentifier = SupportsPersistentIdentifier, - SupportsSync = SupportsSync, DeviceProfile = DeviceProfile, AppStoreUrl = AppStoreUrl, IconUrl = IconUrl diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index d8eee12467..5e5d52b6b3 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -167,7 +167,7 @@ namespace Jellyfin.Server.Implementations.Devices } /// - public async Task> GetDevicesForUser(Guid? userId, bool? supportsSync) + public async Task> GetDevicesForUser(Guid? userId) { var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) @@ -178,10 +178,6 @@ namespace Jellyfin.Server.Implementations.Devices .ThenBy(d => d.DeviceId) .SelectMany(d => dbContext.DeviceOptions.Where(o => o.DeviceId == d.DeviceId).DefaultIfEmpty(), (d, o) => new { Device = d, Options = o }) .AsAsyncEnumerable(); - if (supportsSync.HasValue) - { - sessions = sessions.Where(i => GetCapabilities(i.Device.DeviceId).SupportsSync == supportsSync.Value); - } if (userId.HasValue) { diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 8362db1a71..eb181dcc4c 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -59,9 +59,8 @@ namespace MediaBrowser.Controller.Devices /// Gets the devices. /// /// The user's id, or null. - /// A value indicating whether the device supports sync, or null. /// IEnumerable<DeviceInfo>. - Task> GetDevicesForUser(Guid? userId, bool? supportsSync); + Task> GetDevicesForUser(Guid? userId); Task DeleteDevice(Device device); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7c04fcbfc8..fdbceac961 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -773,8 +773,6 @@ namespace MediaBrowser.Controller.Entities /// The remote trailers. public IReadOnlyList RemoteTrailers { get; set; } - public virtual bool SupportsExternalTransfer => false; - public virtual double GetDefaultPrimaryImageAspectRatio() { return 0; diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index d257eab920..cfff717db2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -85,11 +85,6 @@ namespace MediaBrowser.Model.Dto public string PreferredMetadataCountryCode { get; set; } - /// - /// Gets or sets a value indicating whether [supports synchronize]. - /// - public bool? SupportsSync { get; set; } - public string Container { get; set; } /// diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 242a1c6e99..49d7c0bcb0 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -175,13 +175,6 @@ namespace MediaBrowser.Model.Querying /// Studios, - BasicSyncInfo, - - /// - /// The synchronize information. - /// - SyncInfo, - /// /// The taglines of the item. /// diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 7fefce9cd5..597845fc17 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -23,14 +23,8 @@ namespace MediaBrowser.Model.Session public bool SupportsMediaControl { get; set; } - public bool SupportsContentUploading { get; set; } - - public string MessageCallbackUrl { get; set; } - public bool SupportsPersistentIdentifier { get; set; } - public bool SupportsSync { get; set; } - public DeviceProfile DeviceProfile { get; set; } public string AppStoreUrl { get; set; } From 82f93afa22c9695f960903f7b90c7a2c8b23af74 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Jan 2024 15:33:58 -0500 Subject: [PATCH 077/132] Fix More Live TV Warnings (#10818) * Fix CA1819 in LiveTvManager * Call ConfigureAwait in ChannelManager --- .../Channels/ChannelManager.cs | 35 +++++++++++++------ .../LiveTv/LiveTvManager.cs | 4 +-- .../LiveTv/ILiveTvManager.cs | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 8279acb058..c505e8fe13 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -812,11 +812,16 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - await using FileStream jsonStream = AsyncFile.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (cachedResult is not null) + var jsonStream = AsyncFile.OpenRead(cachePath); + await using (jsonStream.ConfigureAwait(false)) { - return null; + var cachedResult = await JsonSerializer + .DeserializeAsync(jsonStream, _jsonOptions, cancellationToken) + .ConfigureAwait(false); + if (cachedResult is not null) + { + return null; + } } } } @@ -835,11 +840,16 @@ namespace Emby.Server.Implementations.Channels { if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - await using FileStream jsonStream = AsyncFile.OpenRead(cachePath); - var cachedResult = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); - if (cachedResult is not null) + var jsonStream = AsyncFile.OpenRead(cachePath); + await using (jsonStream.ConfigureAwait(false)) { - return null; + var cachedResult = await JsonSerializer + .DeserializeAsync(jsonStream, _jsonOptions, cancellationToken) + .ConfigureAwait(false); + if (cachedResult is not null) + { + return null; + } } } } @@ -867,7 +877,7 @@ namespace Emby.Server.Implementations.Channels throw new InvalidOperationException("Channel returned a null result from GetChannelItems"); } - await CacheResponse(result, cachePath); + await CacheResponse(result, cachePath).ConfigureAwait(false); return result; } @@ -883,8 +893,11 @@ namespace Emby.Server.Implementations.Channels { Directory.CreateDirectory(Path.GetDirectoryName(path)); - await using FileStream createStream = File.Create(path); - await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false); + var createStream = File.Create(path); + await using (createStream.ConfigureAwait(false)) + { + await JsonSerializer.SerializeAsync(createStream, result, _jsonOptions).ConfigureAwait(false); + } } catch (Exception ex) { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index ca8e204d7c..426165de61 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -105,9 +105,9 @@ namespace Emby.Server.Implementations.LiveTv /// The services. public IReadOnlyList Services => _services; - public ITunerHost[] TunerHosts => _tunerHosts; + public IReadOnlyList TunerHosts => _tunerHosts; - public IListingsProvider[] ListingProviders => _listingProviders; + public IReadOnlyList ListingProviders => _listingProviders; private LiveTvOptions GetConfiguration() { diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 3b6a16dee3..4206159e7b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.LiveTv /// The services. IReadOnlyList Services { get; } - IListingsProvider[] ListingProviders { get; } + IReadOnlyList ListingProviders { get; } /// /// Gets the new timer defaults asynchronous. From 43b32b0d94d4deef49bbca735dc50447acdb9250 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Jan 2024 15:34:09 -0500 Subject: [PATCH 078/132] Auto Discovery Cleanup (#10793) * Call GetSmartApiUrl directly in UdpServer.RespondToV2Message GetSmartApiUrl already returns PublishedServerUrl if set. * Rewrite auto discovery using UdpClient and BackgroundService * Respect network address settings in AutoDiscoveryHost * Always listen on broadcast address in Linux for auto-discovery * Await udp server tasks in AutoDiscoveryHost * Only bind to broadcast addresses for IPv4 * Only bind to broadcast if IPv4 is enabled --- Jellyfin.Server/Startup.cs | 4 +- src/Jellyfin.Networking/AutoDiscoveryHost.cs | 129 ++++++++++++++++ src/Jellyfin.Networking/Udp/UdpServer.cs | 136 ----------------- .../UdpServerEntryPoint.cs | 143 ------------------ 4 files changed, 132 insertions(+), 280 deletions(-) create mode 100644 src/Jellyfin.Networking/AutoDiscoveryHost.cs delete mode 100644 src/Jellyfin.Networking/Udp/UdpServer.cs delete mode 100644 src/Jellyfin.Networking/UdpServerEntryPoint.cs diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index aa7be9109e..1030c6f5fc 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -6,6 +6,7 @@ using System.Net.Mime; using System.Text; using Jellyfin.Api.Middleware; using Jellyfin.MediaEncoding.Hls.Extensions; +using Jellyfin.Networking; using Jellyfin.Networking.HappyEyeballs; using Jellyfin.Server.Extensions; using Jellyfin.Server.HealthChecks; @@ -13,7 +14,6 @@ using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Extensions; using Jellyfin.Server.Infrastructure; using MediaBrowser.Common.Net; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Extensions; using Microsoft.AspNetCore.Builder; @@ -121,6 +121,8 @@ namespace Jellyfin.Server .AddCheck>(nameof(JellyfinDbContext)); services.AddHlsPlaylistGenerator(); + + services.AddHostedService(); } /// diff --git a/src/Jellyfin.Networking/AutoDiscoveryHost.cs b/src/Jellyfin.Networking/AutoDiscoveryHost.cs new file mode 100644 index 0000000000..5624c4ed13 --- /dev/null +++ b/src/Jellyfin.Networking/AutoDiscoveryHost.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Model.ApiClient; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Networking; + +/// +/// responsible for responding to auto-discovery messages. +/// +public sealed class AutoDiscoveryHost : BackgroundService +{ + /// + /// The port to listen on for auto-discovery messages. + /// + private const int PortNumber = 7359; + + private readonly ILogger _logger; + private readonly IServerApplicationHost _appHost; + private readonly IConfigurationManager _configurationManager; + private readonly INetworkManager _networkManager; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + /// The . + public AutoDiscoveryHost( + ILogger logger, + IServerApplicationHost appHost, + IConfigurationManager configurationManager, + INetworkManager networkManager) + { + _logger = logger; + _appHost = appHost; + _configurationManager = configurationManager; + _networkManager = networkManager; + } + + /// + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var networkConfig = _configurationManager.GetNetworkConfiguration(); + if (!networkConfig.AutoDiscovery) + { + return; + } + + var udpServers = new List(); + // Linux needs to bind to the broadcast addresses to receive broadcast traffic + if (OperatingSystem.IsLinux() && networkConfig.EnableIPv4) + { + udpServers.Add(ListenForAutoDiscoveryMessage(IPAddress.Broadcast, stoppingToken)); + } + + udpServers.AddRange(_networkManager.GetInternalBindAddresses() + .Select(intf => ListenForAutoDiscoveryMessage( + OperatingSystem.IsLinux() && intf.AddressFamily == AddressFamily.InterNetwork + ? NetworkUtils.GetBroadcastAddress(intf.Subnet) + : intf.Address, + stoppingToken))); + + await Task.WhenAll(udpServers).ConfigureAwait(false); + } + + private async Task ListenForAutoDiscoveryMessage(IPAddress address, CancellationToken cancellationToken) + { + using var udpClient = new UdpClient(new IPEndPoint(address, PortNumber)); + udpClient.MulticastLoopback = false; + + while (!cancellationToken.IsCancellationRequested) + { + try + { + var result = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + var text = Encoding.UTF8.GetString(result.Buffer); + if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) + { + await RespondToV2Message(udpClient, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); + } + } + catch (SocketException ex) + { + _logger.LogError(ex, "Failed to receive data from socket"); + } + catch (OperationCanceledException) + { + _logger.LogDebug("Broadcast socket operation cancelled"); + } + } + } + + private async Task RespondToV2Message(UdpClient udpClient, IPEndPoint endpoint, CancellationToken cancellationToken) + { + var localUrl = _appHost.GetSmartApiUrl(endpoint.Address); + if (string.IsNullOrEmpty(localUrl)) + { + _logger.LogWarning("Unable to respond to server discovery request because the local ip address could not be determined."); + return; + } + + var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); + + try + { + _logger.LogDebug("Sending AutoDiscovery response"); + await udpClient + .SendAsync(JsonSerializer.SerializeToUtf8Bytes(response).AsMemory(), endpoint, cancellationToken) + .ConfigureAwait(false); + } + catch (SocketException ex) + { + _logger.LogError(ex, "Error sending response message"); + } + } +} diff --git a/src/Jellyfin.Networking/Udp/UdpServer.cs b/src/Jellyfin.Networking/Udp/UdpServer.cs deleted file mode 100644 index b130a5a5ff..0000000000 --- a/src/Jellyfin.Networking/Udp/UdpServer.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller; -using MediaBrowser.Model.ApiClient; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; - -namespace Jellyfin.Networking.Udp; - -/// -/// Provides a Udp Server. -/// -public sealed class UdpServer : IDisposable -{ - /// - /// The _logger. - /// - private readonly ILogger _logger; - private readonly IServerApplicationHost _appHost; - private readonly IConfiguration _config; - - private readonly byte[] _receiveBuffer = new byte[8192]; - - private readonly Socket _udpSocket; - private readonly IPEndPoint _endpoint; - private bool _disposed; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// The application host. - /// The configuration manager. - /// The bind address. - /// The port. - public UdpServer( - ILogger logger, - IServerApplicationHost appHost, - IConfiguration configuration, - IPAddress bindAddress, - int port) - { - _logger = logger; - _appHost = appHost; - _config = configuration; - - _endpoint = new IPEndPoint(bindAddress, port); - - _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) - { - MulticastLoopback = false, - }; - _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - } - - private async Task RespondToV2Message(EndPoint endpoint, CancellationToken cancellationToken) - { - string? localUrl = _config[AddressOverrideKey]; - if (string.IsNullOrEmpty(localUrl)) - { - localUrl = _appHost.GetSmartApiUrl(((IPEndPoint)endpoint).Address); - } - - if (string.IsNullOrEmpty(localUrl)) - { - _logger.LogWarning("Unable to respond to server discovery request because the local ip address could not be determined."); - return; - } - - var response = new ServerDiscoveryInfo(localUrl, _appHost.SystemId, _appHost.FriendlyName); - - try - { - _logger.LogDebug("Sending AutoDiscovery response"); - await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint, cancellationToken).ConfigureAwait(false); - } - catch (SocketException ex) - { - _logger.LogError(ex, "Error sending response message"); - } - } - - /// - /// Starts the specified port. - /// - /// The cancellation token to cancel operation. - public void Start(CancellationToken cancellationToken) - { - _udpSocket.Bind(_endpoint); - - _ = Task.Run(async () => await BeginReceiveAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); - } - - private async Task BeginReceiveAsync(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) - { - try - { - var endpoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0); - var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, endpoint, cancellationToken).ConfigureAwait(false); - var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes); - if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) - { - await RespondToV2Message(result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); - } - } - catch (SocketException ex) - { - _logger.LogError(ex, "Failed to receive data from socket"); - } - catch (OperationCanceledException) - { - _logger.LogDebug("Broadcast socket operation cancelled"); - } - } - } - - /// - public void Dispose() - { - if (_disposed) - { - return; - } - - _udpSocket.Dispose(); - _disposed = true; - } -} diff --git a/src/Jellyfin.Networking/UdpServerEntryPoint.cs b/src/Jellyfin.Networking/UdpServerEntryPoint.cs deleted file mode 100644 index 61180c3c0f..0000000000 --- a/src/Jellyfin.Networking/UdpServerEntryPoint.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Networking.Udp; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Plugins; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using IConfigurationManager = MediaBrowser.Common.Configuration.IConfigurationManager; - -namespace Jellyfin.Networking; - -/// -/// Class responsible for registering all UDP broadcast endpoints and their handlers. -/// -public sealed class UdpServerEntryPoint : IServerEntryPoint -{ - /// - /// The port of the UDP server. - /// - public const int PortNumber = 7359; - - /// - /// The logger. - /// - private readonly ILogger _logger; - private readonly IServerApplicationHost _appHost; - private readonly IConfiguration _config; - private readonly IConfigurationManager _configurationManager; - private readonly INetworkManager _networkManager; - - /// - /// The UDP server. - /// - private readonly List _udpServers; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - private bool _disposed; - - /// - /// 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. - public UdpServerEntryPoint( - ILogger logger, - IServerApplicationHost appHost, - IConfiguration configuration, - IConfigurationManager configurationManager, - INetworkManager networkManager) - { - _logger = logger; - _appHost = appHost; - _config = configuration; - _configurationManager = configurationManager; - _networkManager = networkManager; - _udpServers = new List(); - } - - /// - public Task RunAsync() - { - ObjectDisposedException.ThrowIf(_disposed, this); - - if (!_configurationManager.GetNetworkConfiguration().AutoDiscovery) - { - return Task.CompletedTask; - } - - try - { - // Linux needs to bind to the broadcast addresses to get broadcast traffic - // Windows receives broadcast fine when binding to just the interface, it is unable to bind to broadcast addresses - if (OperatingSystem.IsLinux()) - { - // Add global broadcast listener - var server = new UdpServer(_logger, _appHost, _config, IPAddress.Broadcast, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - - // Add bind address specific broadcast listeners - // IPv6 is currently unsupported - var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork); - foreach (var intf in validInterfaces) - { - var broadcastAddress = NetworkUtils.GetBroadcastAddress(intf.Subnet); - _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", broadcastAddress, PortNumber); - - server = new UdpServer(_logger, _appHost, _config, broadcastAddress, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - } - } - else - { - // Add bind address specific broadcast listeners - // IPv6 is currently unsupported - var validInterfaces = _networkManager.GetInternalBindAddresses().Where(i => i.AddressFamily == AddressFamily.InterNetwork); - foreach (var intf in validInterfaces) - { - var intfAddress = intf.Address; - _logger.LogDebug("Binding UDP server to {Address} on port {PortNumber}", intfAddress, PortNumber); - - var server = new UdpServer(_logger, _appHost, _config, intfAddress, PortNumber); - server.Start(_cancellationTokenSource.Token); - _udpServers.Add(server); - } - } - } - catch (SocketException ex) - { - _logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber); - } - - return Task.CompletedTask; - } - - /// - public void Dispose() - { - if (_disposed) - { - return; - } - - _cancellationTokenSource.Cancel(); - _cancellationTokenSource.Dispose(); - foreach (var server in _udpServers) - { - server.Dispose(); - } - - _udpServers.Clear(); - _disposed = true; - } -} From 5ff7c17daf706250ff6052cfd3de2e946a8e6e00 Mon Sep 17 00:00:00 2001 From: JPVenson Date: Sat, 6 Jan 2024 22:35:15 +0200 Subject: [PATCH 079/132] Feature/enable gh codespaces (#10324) * Added BindAll network Manager * Updated docs * Update ApplicationHost.cs resolved merge conflicts * Fixed merge issues * Updated Dev container to dotnet * Update Emby.Server.Implementations/ApplicationHost.cs Co-authored-by: Cody Robibero * Updates container to include dependencies * Resolved merge conflicts * Updated container config to automate setup more * Updated readme * Enabled auto loading of extensions * fixed recommended list not parsable * Removed obsolete code * Reverted change to virtualize GetAllBindInterfaces * Updated Readme * Update devcontainer.json --------- Co-authored-by: Cody Robibero --- .devcontainer/devcontainer.json | 28 ++++++++++++++++++++++++++++ .vscode/extensions.json | 10 ++++------ README.md | 11 +++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..063901c800 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +{ + "name": "Development Jellyfin Server", + "image":"mcr.microsoft.com/devcontainers/dotnet:8.0-jammy", + // restores nuget packages, installs the dotnet workloads and installs the dev https certificate + "postStartCommand": "dotnet restore; dotnet workload update; dotnet dev-certs https --trust", + // reads the extensions list and installs them + "postAttachCommand": "cat .vscode/extensions.json | jq -r .recommendations[] | xargs -n 1 code --install-extension", + "features": { + "ghcr.io/devcontainers/features/dotnet:2": { + "version": "none", + "dotnetRuntimeVersions": "8.0", + "aspNetCoreRuntimeVersions": "8.0" + }, + "ghcr.io/devcontainers-contrib/features/apt-packages:1": { + "preserve_apt_list": false, + "packages": ["libfontconfig1"] + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "dockerDashComposeVersion": "v2" + }, + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {} + }, + "hostRequirements": { + "memory": "8gb", + "cpus": 4 + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 59d9452fed..d738e9fba4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,13 +1,11 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. "recommendations": [ "ms-dotnettools.csharp", - "editorconfig.editorconfig" + "editorconfig.editorconfig", + "GitHub.vscode-github-actions", + "ms-dotnettools.vscode-dotnet-runtime", + "ms-dotnettools.csdevkit" ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [ ] diff --git a/README.md b/README.md index 15dd0ae679..62ef21334d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,17 @@ cd Jellyfin.Server/bin/Debug/net8.0 # Change into the build output directory 2. Execute the build output. On Linux, Mac, etc. use `./jellyfin` and on Windows use `jellyfin.exe`. +### Running from GH-Codespaces + +As Jellyfin will run on a container on a github hosted server, JF needs to handle some things differently. +**NOTE:** If you want to access the JF instance from outside, like with a WebClient on another PC, remember to set the "ports" in the lower VsCode window to public. + +#### FFmpeg installation. +Because sometimes you need FFMPEG to test certain cases, follow the instructions from the wiki on the dev enviorment: +https://jellyfin.org/docs/general/installation/linux/#ffmpeg-installation + +**NOTE:** When first opening the server instance with any WebUI, you will be send to the login instead of the setup page. Refresh the login page once and you should be redirected to the Setup. + ### Running The Tests This repository also includes unit tests that are used to validate functionality as part of a CI pipeline on Azure. There are several ways to run these tests. From 04dddd3a7b3911ef9a3e83c85aa5419e8db2fa38 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Jan 2024 15:43:48 -0500 Subject: [PATCH 080/132] Remove UserManager Cache (#10781) * Remove redundant user cache * Use DI for IPasswordResetProvider and IAuthenticationProvider --- .../Users/UserManager.cs | 175 +++++++++--------- Jellyfin.Server/CoreAppHost.cs | 4 + 2 files changed, 95 insertions(+), 84 deletions(-) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 990b9a5bd4..cc464a7a3d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,7 +1,7 @@ #pragma warning disable CA1307 +#pragma warning disable CA1309 // Use ordinal string comparison - EF can't translate this using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -46,8 +46,6 @@ namespace Jellyfin.Server.Implementations.Users private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider; private readonly IServerConfigurationManager _serverConfigurationManager; - private readonly IDictionary _users; - /// /// Initializes a new instance of the class. /// @@ -58,6 +56,8 @@ namespace Jellyfin.Server.Implementations.Users /// The image processor. /// The logger. /// The system config manager. + /// The password reset providers. + /// The authentication providers. public UserManager( IDbContextFactory dbProvider, IEventManager eventManager, @@ -65,7 +65,9 @@ namespace Jellyfin.Server.Implementations.Users IApplicationHost appHost, IImageProcessor imageProcessor, ILogger logger, - IServerConfigurationManager serverConfigurationManager) + IServerConfigurationManager serverConfigurationManager, + IEnumerable passwordResetProviders, + IEnumerable authenticationProviders) { _dbProvider = dbProvider; _eventManager = eventManager; @@ -75,35 +77,36 @@ namespace Jellyfin.Server.Implementations.Users _logger = logger; _serverConfigurationManager = serverConfigurationManager; - _passwordResetProviders = appHost.GetExports(); - _authenticationProviders = appHost.GetExports(); + _passwordResetProviders = passwordResetProviders.ToList(); + _authenticationProviders = authenticationProviders.ToList(); _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); - - _users = new ConcurrentDictionary(); - using var dbContext = _dbProvider.CreateDbContext(); - foreach (var user in dbContext.Users - .AsSplitQuery() - .Include(user => user.Permissions) - .Include(user => user.Preferences) - .Include(user => user.AccessSchedules) - .Include(user => user.ProfileImage) - .AsEnumerable()) - { - _users.Add(user.Id, user); - } } /// public event EventHandler>? OnUserUpdated; /// - public IEnumerable Users => _users.Values; + public IEnumerable Users + { + get + { + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext).ToList(); + } + } /// - public IEnumerable UsersIds => _users.Keys; + public IEnumerable UsersIds + { + get + { + using var dbContext = _dbProvider.CreateDbContext(); + return dbContext.Users.Select(u => u.Id).ToList(); + } + } // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness @@ -119,8 +122,8 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Guid can't be empty", nameof(id)); } - _users.TryGetValue(id, out var user); - return user; + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext).FirstOrDefault(u => u.Id.Equals(id)); } /// @@ -131,7 +134,9 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - return _users.Values.FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); + using var dbContext = _dbProvider.CreateDbContext(); + return GetUsersInternal(dbContext) + .FirstOrDefault(u => string.Equals(u.Username, name)); } /// @@ -196,8 +201,6 @@ namespace Jellyfin.Server.Implementations.Users user.AddDefaultPermissions(); user.AddDefaultPreferences(); - _users.Add(user.Id, user); - return user; } @@ -232,40 +235,46 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task DeleteUserAsync(Guid userId) { - if (!_users.TryGetValue(userId, out var user)) - { - throw new ResourceNotFoundException(nameof(userId)); - } - - if (_users.Count == 1) - { - throw new InvalidOperationException(string.Format( - CultureInfo.InvariantCulture, - "The user '{0}' cannot be deleted because there must be at least one user in the system.", - user.Username)); - } - - if (user.HasPermission(PermissionKind.IsAdministrator) - && Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1) - { - throw new ArgumentException( - string.Format( - CultureInfo.InvariantCulture, - "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", - user.Username), - nameof(userId)); - } - var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { + var user = await dbContext.Users + .AsSingleQuery() + .Include(u => u.Permissions) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false); + if (user is null) + { + throw new ResourceNotFoundException(nameof(userId)); + } + + if (await dbContext.Users.CountAsync().ConfigureAwait(false) == 1) + { + throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one user in the system.", + user.Username)); + } + + if (user.HasPermission(PermissionKind.IsAdministrator) + && await dbContext.Users + .CountAsync(u => u.Permissions.Any(p => p.Kind == PermissionKind.IsAdministrator && p.Value)) + .ConfigureAwait(false) == 1) + { + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", + user.Username), + nameof(userId)); + } + dbContext.Users.Remove(user); await dbContext.SaveChangesAsync().ConfigureAwait(false); + + await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); } - - _users.Remove(userId); - - await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); } /// @@ -532,23 +541,23 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task InitializeAsync() { - // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. - if (_users.Any()) - { - return; - } - - var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName)) - { - defaultName = "MyJellyfinUser"; - } - - _logger.LogWarning("No users, creating one with username {UserName}", defaultName); - var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { + // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. + if (await dbContext.Users.AnyAsync().ConfigureAwait(false)) + { + return; + } + + var defaultName = Environment.UserName; + if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName)) + { + defaultName = "MyJellyfinUser"; + } + + _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); @@ -595,12 +604,9 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) + var user = await GetUsersInternal(dbContext) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false) ?? throw new ArgumentException("No user exists with given Id!"); user.SubtitleMode = config.SubtitleMode; @@ -628,7 +634,6 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); dbContext.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } } @@ -639,12 +644,9 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) + var user = await GetUsersInternal(dbContext) + .FirstOrDefaultAsync(u => u.Id.Equals(userId)) + .ConfigureAwait(false) ?? throw new ArgumentException("No user exists with given Id!"); // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" @@ -704,7 +706,6 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } } @@ -725,7 +726,6 @@ namespace Jellyfin.Server.Implementations.Users } user.ProfileImage = null; - _users[user.Id] = user; } internal static void ThrowIfInvalidUsername(string name) @@ -872,8 +872,15 @@ namespace Jellyfin.Server.Implementations.Users private async Task UpdateUserInternalAsync(JellyfinDbContext dbContext, User user) { dbContext.Users.Update(user); - _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } + + private IQueryable GetUsersInternal(JellyfinDbContext dbContext) + => dbContext.Users + .AsSplitQuery() + .Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage); } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index c12c90a689..93277a981e 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -14,6 +14,7 @@ using Jellyfin.Server.Implementations.Security; using Jellyfin.Server.Implementations.Trickplay; using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.BaseItemManager; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; @@ -78,6 +79,9 @@ namespace Jellyfin.Server serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddScoped(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); From 615089228a9c456f480d328aa3eb11017a3cb857 Mon Sep 17 00:00:00 2001 From: Dave Senn Date: Sat, 6 Jan 2024 21:44:10 +0100 Subject: [PATCH 081/132] Add nuget.config file to fix NU1507 by only allowing nuget.org as package source. see: https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping and https://github.com/dotnet/sdk/issues/25379 (#10396) --- nuget.config | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 nuget.config diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000000..54e660f9c9 --- /dev/null +++ b/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 7b5c41c2a521f2e49f8acc24f29c8472938f3987 Mon Sep 17 00:00:00 2001 From: TelepathicWalrus <48514138+TelepathicWalrus@users.noreply.github.com> Date: Sat, 6 Jan 2024 20:59:56 +0000 Subject: [PATCH 082/132] Add option to use replaygain tags for audio (#10566) * Add option to use replaygain tags for audio * Change regex to be specific * Use ffprobe for faster metadata grabs * Change regex to .Match --- .../Configuration/LibraryOptions.cs | 2 + .../MediaInfo/AudioFileProber.cs | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index fbad29143b..1c071067df 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -31,6 +31,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableLUFSScan { get; set; } + public bool UseReplayGainTags { get; set; } + public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index d817042274..f718325df5 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -61,6 +61,9 @@ namespace MediaBrowser.Providers.MediaInfo [GeneratedRegex(@"I:\s+(.*?)\s+LUFS")] private static partial Regex LUFSRegex(); + [GeneratedRegex(@"REPLAYGAIN_TRACK_GAIN:\s+-?([0-9.]+)\s+dB")] + private static partial Regex ReplayGainTagRegex(); + /// /// Probes the specified item for metadata. /// @@ -104,8 +107,50 @@ namespace MediaBrowser.Providers.MediaInfo } var libraryOptions = _libraryManager.GetLibraryOptions(item); + bool foundLUFSValue = false; - if (libraryOptions.EnableLUFSScan) + if (libraryOptions.UseReplayGainTags) + { + using (var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = _mediaEncoder.ProbePath, + Arguments = $"-hide_banner -i \"{path}\"", + RedirectStandardOutput = false, + RedirectStandardError = true + }, + }) + { + try + { + process.Start(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error starting ffmpeg"); + + throw; + } + + using var reader = process.StandardError; + var output = await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + Match split = ReplayGainTagRegex().Match(output); + + if (split.Success) + { + item.LUFS = DefaultLUFSValue - float.Parse(split.Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + foundLUFSValue = true; + } + else + { + item.LUFS = DefaultLUFSValue; + } + } + } + + if (libraryOptions.EnableLUFSScan && !foundLUFSValue) { using (var process = new Process() { @@ -144,7 +189,8 @@ namespace MediaBrowser.Providers.MediaInfo } } } - else + + if (!libraryOptions.EnableLUFSScan && !libraryOptions.UseReplayGainTags) { item.LUFS = DefaultLUFSValue; } From f49de51225b2206609df6a89f3cbb5fd7459ff68 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sun, 7 Jan 2024 01:11:08 +0400 Subject: [PATCH 083/132] Detect audio spatial format (#9996) * Detect audio spatial format * Update MediaBrowser.Model/Entities/MediaStream.cs * Update MediaStream.cs --------- Co-authored-by: Cody Robibero --- CONTRIBUTORS.md | 1 + Jellyfin.Data/Enums/AudioSpatialFormat.cs | 22 +++++ MediaBrowser.Model/Entities/MediaStream.cs | 22 +++++ .../Probing/ProbeResultNormalizerTests.cs | 10 ++- .../Test Data/Probing/video_metadata.json | 88 ++++++++++++++++++- 5 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 Jellyfin.Data/Enums/AudioSpatialFormat.cs diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d208879d17..4e45fd24ad 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -81,6 +81,7 @@ - [Maxr1998](https://github.com/Maxr1998) - [mcarlton00](https://github.com/mcarlton00) - [mitchfizz05](https://github.com/mitchfizz05) + - [mohd-akram](https://github.com/mohd-akram) - [MrTimscampi](https://github.com/MrTimscampi) - [n8225](https://github.com/n8225) - [Nalsai](https://github.com/Nalsai) diff --git a/Jellyfin.Data/Enums/AudioSpatialFormat.cs b/Jellyfin.Data/Enums/AudioSpatialFormat.cs new file mode 100644 index 0000000000..5e3a123320 --- /dev/null +++ b/Jellyfin.Data/Enums/AudioSpatialFormat.cs @@ -0,0 +1,22 @@ +namespace Jellyfin.Data.Enums; + +/// +/// An enum representing formats of spatial audio. +/// +public enum AudioSpatialFormat +{ + /// + /// None audio spatial format. + /// + None, + + /// + /// Dolby Atmos audio spatial format. + /// + DolbyAtmos, + + /// + /// DTS:X audio spatial format. + /// + DTSX, +} diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 34642b83aa..ae4a008bb3 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Globalization; using System.Linq; using System.Text; @@ -214,6 +215,27 @@ namespace MediaBrowser.Model.Entities } } + /// + /// Gets the audio spatial format. + /// + /// The audio spatial format. + [DefaultValue(AudioSpatialFormat.None)] + public AudioSpatialFormat AudioSpatialFormat + { + get + { + if (Type != MediaStreamType.Audio || string.IsNullOrEmpty(Profile)) + { + return AudioSpatialFormat.None; + } + + return + Profile.Contains("Dolby Atmos", StringComparison.OrdinalIgnoreCase) ? AudioSpatialFormat.DolbyAtmos : + Profile.Contains("DTS:X", StringComparison.OrdinalIgnoreCase) ? AudioSpatialFormat.DTSX : + AudioSpatialFormat.None; + } + } + public string LocalizedUndefined { get; set; } public string LocalizedDefault { get; set; } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 198dc63efe..344ac8971c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -46,7 +46,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing var internalMediaInfoResult = JsonSerializer.Deserialize(bytes, _jsonOptions); MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File); - Assert.Single(res.MediaStreams); + Assert.Equal(3, res.MediaStreams.Count); Assert.NotNull(res.VideoStream); Assert.Equal("4:3", res.VideoStream.AspectRatio); @@ -83,6 +83,14 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Equal(1, res.VideoStream.BlPresentFlag); Assert.Equal(0, res.VideoStream.DvBlSignalCompatibilityId); + var audio1 = res.MediaStreams[1]; + Assert.Equal("eac3", audio1.Codec); + Assert.Equal(AudioSpatialFormat.DolbyAtmos, audio1.AudioSpatialFormat); + + var audio2 = res.MediaStreams[2]; + Assert.Equal("dts", audio2.Codec); + Assert.Equal(AudioSpatialFormat.DTSX, audio2.AudioSpatialFormat); + Assert.Empty(res.Chapters); Assert.Equal("Just color bars", res.Overview); } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json index 519d81179c..a49c686900 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_metadata.json @@ -61,6 +61,92 @@ "dv_bl_signal_compatibility_id": 0 } ] + }, + { + "index": 1, + "codec_name": "eac3", + "codec_long_name": "ATSC A/52B (AC-3, E-AC-3)", + "profile": "Dolby Digital Plus + Dolby Atmos", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "fltp", + "sample_rate": "48000", + "channels": 6, + "channel_layout": "5.1(side)", + "bits_per_sample": 0, + "initial_padding": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bit_rate": "640000", + "disposition": { + "default": 1, + "dub": 0, + "original": 1, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0, + "captions": 0, + "descriptions": 0, + "metadata": 0, + "dependent": 0, + "still_image": 0 + }, + "tags": { + "language": "eng" + } + }, + { + "index": 2, + "codec_name": "dts", + "codec_long_name": "DCA (DTS Coherent Acoustics)", + "profile": "DTS-HD MA + DTS:X", + "codec_type": "audio", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "sample_fmt": "s32p", + "sample_rate": "48000", + "channels": 8, + "channel_layout": "7.1", + "bits_per_sample": 0, + "initial_padding": 0, + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bits_per_raw_sample": "24", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0, + "captions": 0, + "descriptions": 0, + "metadata": 0, + "dependent": 0, + "still_image": 0 + }, + "tags": { + "language": "eng" + } } ], "chapters": [ @@ -68,7 +154,7 @@ ], "format": { "filename": "some_metadata.mkv", - "nb_streams": 1, + "nb_streams": 3, "nb_programs": 0, "format_name": "matroska,webm", "format_long_name": "Matroska / WebM", From adb0837dc219df6f9474d819c68a73f8c3decf48 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 6 Jan 2024 14:43:19 -0700 Subject: [PATCH 084/132] Respond with Service Unavailable if not in correct network --- Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs | 2 ++ Jellyfin.Api/Middleware/LanFilteringMiddleware.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs index 27bcd5570c..0d107d11b3 100644 --- a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs @@ -41,6 +41,8 @@ public class IPBasedAccessValidationMiddleware if (!networkManager.HasRemoteAccess(remoteIP)) { + // No access from network, respond with 503 instead of 200. + httpContext.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable; return; } diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index d8c95ddffe..34ed4818f9 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -40,6 +41,8 @@ public class LanFilteringMiddleware var host = httpContext.GetNormalizedRemoteIP(); if (!networkManager.IsInLocalNetwork(host)) { + // No access from network, respond with 503 instead of 200. + httpContext.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable; return; } From f2b01f66e88d22f11c6530c085eda002ceb175dd Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 6 Jan 2024 15:01:50 -0700 Subject: [PATCH 085/132] Specify DateTimeKind when pulling a DateTime out of the database --- Emby.Server.Implementations/Data/SqliteExtensions.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 01b5fdaeea..25ef57d271 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -104,6 +104,13 @@ namespace Emby.Server.Implementations.Data if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out var dateTimeResult)) { + // If the resulting DateTimeKind is Unspecified it is actually Utc. + // This is required downstream for the Json serializer. + if (dateTimeResult.Kind == DateTimeKind.Unspecified) + { + dateTimeResult = DateTime.SpecifyKind(dateTimeResult, DateTimeKind.Utc); + } + result = dateTimeResult; return true; } From fa0413d6e4ec565fd039a99ead3b73391046f222 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 6 Jan 2024 15:24:17 -0700 Subject: [PATCH 086/132] Use StatusCodes which is already an int --- Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs | 2 +- Jellyfin.Api/Middleware/LanFilteringMiddleware.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs index 0d107d11b3..842a69dd98 100644 --- a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs @@ -42,7 +42,7 @@ public class IPBasedAccessValidationMiddleware if (!networkManager.HasRemoteAccess(remoteIP)) { // No access from network, respond with 503 instead of 200. - httpContext.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable; + httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; return; } diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index 34ed4818f9..35b0a1dd06 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs @@ -42,7 +42,7 @@ public class LanFilteringMiddleware if (!networkManager.IsInLocalNetwork(host)) { // No access from network, respond with 503 instead of 200. - httpContext.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable; + httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; return; } From fa03ed4f56b6738ffe37a972997c7d84b7483fc1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:11:08 -0700 Subject: [PATCH 087/132] chore(deps): update github/codeql-action action to v3.23.0 (#10832) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci-codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml index 7c07335980..4b2673e82d 100644 --- a/.github/workflows/ci-codeql-analysis.yml +++ b/.github/workflows/ci-codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '8.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12 + uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 From 08d773d119331abff5eeb1cf6669f8bd6f8c8126 Mon Sep 17 00:00:00 2001 From: Kent Date: Sun, 7 Jan 2024 16:49:12 +0000 Subject: [PATCH 088/132] Translated using Weblate (Norwegian Nynorsk) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nn/ --- Emby.Server.Implementations/Localization/Core/nn.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json index 32d4f3a8b6..d0c914de37 100644 --- a/Emby.Server.Implementations/Localization/Core/nn.json +++ b/Emby.Server.Implementations/Localization/Core/nn.json @@ -117,5 +117,6 @@ "TaskCleanActivityLog": "Slett aktivitetslogg", "Undefined": "Udefinert", "Forced": "Tvungen", - "Default": "Standard" + "Default": "Standard", + "External": "Ekstern" } From a152b71b25be6cf22a767634e3b475a4200634ad Mon Sep 17 00:00:00 2001 From: Andrejs Date: Sat, 6 Jan 2024 18:29:29 +0000 Subject: [PATCH 089/132] Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 82a071309e..6e58ef8345 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -123,5 +123,7 @@ "External": "Ārējais", "HearingImpaired": "Ar dzirdes traucējumiem", "TaskKeyframeExtractor": "Atslēgkadru ekstraktors", - "TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs." + "TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs.", + "TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus", + "TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās." } From 2f1b7d0988520d9341692f235b8adf1e4e10ebd7 Mon Sep 17 00:00:00 2001 From: Vasyl Belynets Date: Sun, 7 Jan 2024 21:05:03 +0000 Subject: [PATCH 090/132] Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- Emby.Server.Implementations/Localization/Core/uk.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index 4c3bf2f10b..6f0dcfbe30 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -18,7 +18,7 @@ "HeaderContinueWatching": "Продовжити перегляд", "HeaderAlbumArtists": "Виконавці альбому", "Genres": "Жанри", - "Folders": "Каталоги", + "Folders": "Теки", "Favorites": "Обрані", "DeviceOnlineWithName": "Пристрій {0} підключився", "DeviceOfflineWithName": "Пристрій {0} відключився", @@ -83,7 +83,7 @@ "SubtitleDownloadFailureFromForItem": "Не вдалося завантажити субтитри з {0} для {1}", "StartupEmbyServerIsLoading": "Jellyfin Server завантажується. Будь ласка, спробуйте трішки пізніше.", "Songs": "Пісні", - "Shows": "Шоу", + "Shows": "Телепередачі", "ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити", "ScheduledTaskStartedWithName": "{0} розпочато", "ScheduledTaskFailedWithName": "{0} незавершено, збій", From 051fa04a803068b5fe8c86e1f991e85a9fbc4d04 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 Dec 2023 14:34:44 -0500 Subject: [PATCH 091/132] Move GetRecordingStreamMediaSources to IMediaSourceManager --- .../Library/MediaSourceManager.cs | 35 ++++++++++++++++++- .../LiveTv/EmbyTV/EmbyTV.cs | 32 ----------------- .../LiveTv/LiveTvMediaSourceProvider.cs | 2 +- .../Library/IMediaSourceManager.cs | 9 +++++ 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 96fad9bca8..68eccf311d 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -11,14 +11,15 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using EasyCaching.Core.Configurations; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; @@ -37,6 +38,7 @@ namespace Emby.Server.Implementations.Library // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message. private const char LiveStreamIdDelimeter = '_'; + private readonly IServerApplicationHost _appHost; private readonly IItemRepository _itemRepo; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; @@ -55,6 +57,7 @@ namespace Emby.Server.Implementations.Library private IMediaSourceProvider[] _providers; public MediaSourceManager( + IServerApplicationHost appHost, IItemRepository itemRepo, IApplicationPaths applicationPaths, ILocalizationManager localizationManager, @@ -66,6 +69,7 @@ namespace Emby.Server.Implementations.Library IMediaEncoder mediaEncoder, IDirectoryService directoryService) { + _appHost = appHost; _itemRepo = itemRepo; _userManager = userManager; _libraryManager = libraryManager; @@ -799,6 +803,35 @@ namespace Emby.Server.Implementations.Library return result.Item1; } + public async Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) + { + var stream = new MediaSourceInfo + { + EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", + EncoderProtocol = MediaProtocol.Http, + Path = info.Path, + Protocol = MediaProtocol.File, + Id = info.Id, + SupportsDirectPlay = false, + SupportsDirectStream = true, + SupportsTranscoding = true, + IsInfiniteStream = true, + RequiresOpening = false, + RequiresClosing = false, + BufferMs = 0, + IgnoreDts = true, + IgnoreIndex = true + }; + + await new LiveStreamHelper(_mediaEncoder, _logger, _appPaths) + .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); + + return new List + { + stream + }; + } + public async Task CloseLiveStream(string id) { ArgumentException.ThrowIfNullOrEmpty(id); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index abe3ff349f..e2e0dfb2bb 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -47,7 +47,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private const int TunerDiscoveryDurationMs = 3000; - private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _config; @@ -76,7 +75,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private bool _disposed; public EmbyTV( - IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, ILogger logger, @@ -91,7 +89,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Current = this; - _appHost = appHost; _logger = logger; _httpClientFactory = httpClientFactory; _config = config; @@ -1021,35 +1018,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } - public async Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken) - { - var stream = new MediaSourceInfo - { - EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream", - EncoderProtocol = MediaProtocol.Http, - Path = info.Path, - Protocol = MediaProtocol.File, - Id = info.Id, - SupportsDirectPlay = false, - SupportsDirectStream = true, - SupportsTranscoding = true, - IsInfiniteStream = true, - RequiresOpening = false, - RequiresClosing = false, - BufferMs = 0, - IgnoreDts = true, - IgnoreIndex = true - }; - - await new LiveStreamHelper(_mediaEncoder, _logger, _config.CommonApplicationPaths) - .AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false); - - return new List - { - stream - }; - } - public Task CloseLiveStream(string id, CancellationToken cancellationToken) { return Task.CompletedTask; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 6a92fc5997..a5a1a4a4c5 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.LiveTv { if (activeRecordingInfo is not null) { - sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken) + sources = await _mediaSourceManager.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken) .ConfigureAwait(false); } else diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index f1758a9d80..bace703ada 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -116,6 +117,14 @@ namespace MediaBrowser.Controller.Library /// An instance of . public ILiveStream GetLiveStreamInfoByUniqueId(string uniqueId); + /// + /// Gets the media sources for an active recording. + /// + /// The . + /// The . + /// A task containing the 's for the recording. + Task> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken); + /// /// Closes the media source. /// From 7eba162879f6d1ff04539cac5c0d6372a955d82b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 Dec 2023 14:49:35 -0500 Subject: [PATCH 092/132] Move LiveTv tests to separate project --- .../Properties/AssemblyInfo.cs | 1 + Jellyfin.sln | 7 +++++ .../HdHomerunHostTests.cs | 2 +- .../HdHomerunManagerTests.cs | 2 +- .../Jellyfin.LiveTv.Tests.csproj | 29 +++++++++++++++++++ .../Listings/XmlTvListingsProviderTests.cs | 2 +- .../RecordingHelperTests.cs | 2 +- .../SchedulesDirectDeserializeTests.cs | 2 +- .../LiveTv/10.10.10.100/discover.json | 0 .../Test Data/LiveTv/10.10.10.100/lineup.json | 0 .../LiveTv/192.168.1.182/discover.json | 0 .../LiveTv/192.168.1.182/lineup.json | 0 .../LiveTv/Listings/XmlTv/emptycategory.xml | 0 .../LiveTv/Listings/XmlTv/notitle.xml | 0 .../SchedulesDirect/headends_response.json | 0 .../SchedulesDirect/lineup_response.json | 0 .../SchedulesDirect/lineups_response.json | 0 .../metadata_programs_response.json | 0 .../SchedulesDirect/programs_response.json | 0 .../SchedulesDirect/schedules_request.json | 0 .../SchedulesDirect/schedules_response.json | 0 .../SchedulesDirect/token_live_response.json | 0 .../token_offline_response.json | 0 23 files changed, 42 insertions(+), 5 deletions(-) rename tests/{Jellyfin.Server.Implementations.Tests/LiveTv => Jellyfin.LiveTv.Tests}/HdHomerunHostTests.cs (99%) rename tests/{Jellyfin.Server.Implementations.Tests/LiveTv => Jellyfin.LiveTv.Tests}/HdHomerunManagerTests.cs (99%) create mode 100644 tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj rename tests/{Jellyfin.Server.Implementations.Tests/LiveTv => Jellyfin.LiveTv.Tests}/Listings/XmlTvListingsProviderTests.cs (98%) rename tests/{Jellyfin.Server.Implementations.Tests/LiveTv => Jellyfin.LiveTv.Tests}/RecordingHelperTests.cs (98%) rename tests/{Jellyfin.Server.Implementations.Tests/LiveTv => Jellyfin.LiveTv.Tests}/SchedulesDirect/SchedulesDirectDeserializeTests.cs (99%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/10.10.10.100/discover.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/10.10.10.100/lineup.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/192.168.1.182/discover.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/192.168.1.182/lineup.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/LiveTv/Listings/XmlTv/notitle.xml (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/headends_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/lineup_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/lineups_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/metadata_programs_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/programs_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/schedules_request.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/schedules_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/token_live_response.json (100%) rename tests/{Jellyfin.Server.Implementations.Tests => Jellyfin.LiveTv.Tests}/Test Data/SchedulesDirect/token_offline_response.json (100%) diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs index 41c396ac1d..fb7377b1df 100644 --- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs +++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs @@ -17,6 +17,7 @@ using System.Runtime.InteropServices; [assembly: NeutralResourcesLanguage("en")] [assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")] [assembly: InternalsVisibleTo("Emby.Server.Implementations.Fuzz")] +[assembly: InternalsVisibleTo("Jellyfin.LiveTv.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/Jellyfin.sln b/Jellyfin.sln index 4385ac2417..31e302d944 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -87,6 +87,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Hls. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Keyframes.Tests", "tests\Jellyfin.MediaEncoding.Keyframes.Tests\Jellyfin.MediaEncoding.Keyframes.Tests.csproj", "{24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.LiveTv.Tests", "tests\Jellyfin.LiveTv.Tests\Jellyfin.LiveTv.Tests.csproj", "{C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -233,6 +235,10 @@ Global {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2}.Release|Any CPU.Build.0 = Release|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -259,6 +265,7 @@ Global {08FFF49B-F175-4807-A2B5-73B0EBD9F716} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {154872D9-6C12-4007-96E3-8F70A58386CE} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {0A3FCC4D-C714-4072-B90F-E374A15F9FF9} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} + {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs similarity index 99% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs rename to tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs index 13ac3ddb0f..bc4b2da5b9 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunHostTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs @@ -12,7 +12,7 @@ using Moq; using Moq.Protected; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public class HdHomerunHostTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs similarity index 99% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs rename to tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs index fd499d9cf9..fcebc0ff75 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs @@ -3,7 +3,7 @@ using System.Text; using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public class HdHomerunManagerTests { diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj new file mode 100644 index 0000000000..de448bada6 --- /dev/null +++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj @@ -0,0 +1,29 @@ + + + net8.0 + + + + + PreserveNewest + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs similarity index 98% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs rename to tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs index 92b4178fdb..71d135735d 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs @@ -12,7 +12,7 @@ using Moq; using Moq.Protected; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv.Listings; +namespace Jellyfin.LiveTv.Tests.Listings; public class XmlTvListingsProviderTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs similarity index 98% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs rename to tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs index f107b1ef97..a5941e7f08 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/RecordingHelperTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs @@ -3,7 +3,7 @@ using Emby.Server.Implementations.LiveTv.EmbyTV; using MediaBrowser.Controller.LiveTv; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv +namespace Jellyfin.LiveTv.Tests { public static class RecordingHelperTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs similarity index 99% rename from tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs rename to tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs index d4f28f327f..6b1f4d4161 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs @@ -6,7 +6,7 @@ using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions.Json; using Xunit; -namespace Jellyfin.Server.Implementations.Tests.LiveTv.SchedulesDirect +namespace Jellyfin.LiveTv.Tests.SchedulesDirect { public class SchedulesDirectDeserializeTests { diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/discover.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/discover.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/discover.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/discover.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/lineup.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/lineup.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/10.10.10.100/lineup.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/10.10.10.100/lineup.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/discover.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/discover.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/discover.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/discover.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/lineup.json b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/lineup.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/192.168.1.182/lineup.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/192.168.1.182/lineup.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml b/tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml rename to tests/Jellyfin.LiveTv.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/headends_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/headends_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/headends_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/headends_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineup_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineup_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineup_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineup_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineups_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineups_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/lineups_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/lineups_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/metadata_programs_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/metadata_programs_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/metadata_programs_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/metadata_programs_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/programs_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/programs_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/programs_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/programs_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_request.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_request.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_request.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_request.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/schedules_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/schedules_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_live_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_live_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_live_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_live_response.json diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_offline_response.json b/tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_offline_response.json similarity index 100% rename from tests/Jellyfin.Server.Implementations.Tests/Test Data/SchedulesDirect/token_offline_response.json rename to tests/Jellyfin.LiveTv.Tests/Test Data/SchedulesDirect/token_offline_response.json From c1a3084312fa4fb7796b83640bfe9ad2b5044afa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 Dec 2023 15:15:03 -0500 Subject: [PATCH 093/132] Move LiveTv to separate project --- .../ApplicationHost.cs | 4 ---- .../Emby.Server.Implementations.csproj | 1 - .../Properties/AssemblyInfo.cs | 1 - Jellyfin.Server/CoreAppHost.cs | 8 +++++++ Jellyfin.Server/Jellyfin.Server.csproj | 1 + Jellyfin.sln | 7 ++++++ .../Jellyfin.LiveTv}/EmbyTV/DirectRecorder.cs | 3 +-- .../Jellyfin.LiveTv}/EmbyTV/EmbyTV.cs | 4 +--- .../EmbyTV/EncodedRecorder.cs | 2 +- .../Jellyfin.LiveTv}/EmbyTV/EntryPoint.cs | 2 +- .../Jellyfin.LiveTv}/EmbyTV/EpgChannelData.cs | 2 +- .../Jellyfin.LiveTv}/EmbyTV/IRecorder.cs | 2 +- .../EmbyTV/ItemDataProvider.cs | 2 +- .../EmbyTV/NfoConfigurationExtensions.cs | 2 +- .../EmbyTV/RecordingHelper.cs | 2 +- .../EmbyTV/SeriesTimerManager.cs | 2 +- .../Jellyfin.LiveTv}/EmbyTV/TimerManager.cs | 2 +- .../Jellyfin.LiveTv}/ExclusiveLiveStream.cs | 3 ++- src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj | 22 +++++++++++++++++++ .../Listings/SchedulesDirect.cs | 6 +++-- .../SchedulesDirectDtos/BroadcasterDto.cs | 2 +- .../SchedulesDirectDtos/CaptionDto.cs | 2 +- .../Listings/SchedulesDirectDtos/CastDto.cs | 2 +- .../SchedulesDirectDtos/ChannelDto.cs | 2 +- .../SchedulesDirectDtos/ContentRatingDto.cs | 2 +- .../Listings/SchedulesDirectDtos/CrewDto.cs | 2 +- .../Listings/SchedulesDirectDtos/DayDto.cs | 2 +- .../SchedulesDirectDtos/Description1000Dto.cs | 2 +- .../SchedulesDirectDtos/Description100Dto.cs | 2 +- .../DescriptionsProgramDto.cs | 2 +- .../SchedulesDirectDtos/EventDetailsDto.cs | 2 +- .../SchedulesDirectDtos/GracenoteDto.cs | 2 +- .../SchedulesDirectDtos/HeadendsDto.cs | 2 +- .../SchedulesDirectDtos/ImageDataDto.cs | 2 +- .../Listings/SchedulesDirectDtos/LineupDto.cs | 2 +- .../SchedulesDirectDtos/LineupsDto.cs | 2 +- .../Listings/SchedulesDirectDtos/LogoDto.cs | 2 +- .../Listings/SchedulesDirectDtos/MapDto.cs | 2 +- .../SchedulesDirectDtos/MetadataDto.cs | 2 +- .../MetadataProgramsDto.cs | 2 +- .../MetadataScheduleDto.cs | 2 +- .../Listings/SchedulesDirectDtos/MovieDto.cs | 2 +- .../SchedulesDirectDtos/MultipartDto.cs | 2 +- .../SchedulesDirectDtos/ProgramDetailsDto.cs | 2 +- .../SchedulesDirectDtos/ProgramDto.cs | 2 +- .../SchedulesDirectDtos/QualityRatingDto.cs | 2 +- .../Listings/SchedulesDirectDtos/RatingDto.cs | 2 +- .../SchedulesDirectDtos/RecommendationDto.cs | 2 +- .../RequestScheduleForChannelDto.cs | 2 +- .../SchedulesDirectDtos/ShowImagesDto.cs | 2 +- .../SchedulesDirectDtos/StationDto.cs | 2 +- .../Listings/SchedulesDirectDtos/TitleDto.cs | 2 +- .../Listings/SchedulesDirectDtos/TokenDto.cs | 2 +- .../Listings/XmlTvListingsProvider.cs | 2 +- .../LiveTvConfigurationFactory.cs | 2 +- .../Jellyfin.LiveTv}/LiveTvDtoService.cs | 2 +- .../Jellyfin.LiveTv}/LiveTvManager.cs | 5 +++-- .../LiveTvMediaSourceProvider.cs | 2 +- .../RefreshGuideScheduledTask.cs | 2 +- .../TunerHosts/BaseTunerHost.cs | 2 +- .../TunerHosts/HdHomerun/Channels.cs | 2 +- .../TunerHosts/HdHomerun/DiscoverResponse.cs | 2 +- .../HdHomerun/HdHomerunChannelCommands.cs | 2 +- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 8 +++---- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- .../HdHomerun/HdHomerunUdpStream.cs | 3 ++- .../HdHomerun/IHdHomerunChannelCommands.cs | 2 +- .../LegacyHdHomerunChannelCommands.cs | 2 +- .../Jellyfin.LiveTv}/TunerHosts/LiveStream.cs | 3 ++- .../TunerHosts/M3UTunerHost.cs | 2 +- .../Jellyfin.LiveTv}/TunerHosts/M3uParser.cs | 2 +- .../TunerHosts/SharedHttpStream.cs | 3 ++- .../HdHomerunHostTests.cs | 2 +- .../HdHomerunManagerTests.cs | 2 +- .../Jellyfin.LiveTv.Tests.csproj | 2 +- .../Listings/XmlTvListingsProviderTests.cs | 2 +- .../RecordingHelperTests.cs | 2 +- .../SchedulesDirectDeserializeTests.cs | 2 +- 78 files changed, 121 insertions(+), 85 deletions(-) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/DirectRecorder.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/EmbyTV.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/EncodedRecorder.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/EntryPoint.cs (87%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/EpgChannelData.cs (97%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/IRecorder.cs (95%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/ItemDataProvider.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/NfoConfigurationExtensions.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/RecordingHelper.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/SeriesTimerManager.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/EmbyTV/TimerManager.cs (99%) rename {Emby.Server.Implementations/Library => src/Jellyfin.LiveTv}/ExclusiveLiveStream.cs (96%) create mode 100644 src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirect.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/BroadcasterDto.cs (91%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/CaptionDto.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/CastDto.cs (93%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ChannelDto.cs (91%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ContentRatingDto.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/CrewDto.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/DayDto.cs (91%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/Description1000Dto.cs (87%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/Description100Dto.cs (87%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs (90%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/EventDetailsDto.cs (81%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/GracenoteDto.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/HeadendsDto.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ImageDataDto.cs (95%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/LineupDto.cs (94%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/LineupsDto.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/LogoDto.cs (91%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MapDto.cs (95%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MetadataDto.cs (89%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs (82%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs (93%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MovieDto.cs (91%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/MultipartDto.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ProgramDto.cs (97%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/QualityRatingDto.cs (93%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/RatingDto.cs (85%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/RecommendationDto.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs (89%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/ShowImagesDto.cs (88%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/StationDto.cs (96%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/TitleDto.cs (80%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/SchedulesDirectDtos/TokenDto.cs (94%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/Listings/XmlTvListingsProvider.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/LiveTvConfigurationFactory.cs (93%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/LiveTvDtoService.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/LiveTvManager.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/LiveTvMediaSourceProvider.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/RefreshGuideScheduledTask.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/BaseTunerHost.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/Channels.cs (86%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/DiscoverResponse.cs (92%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs (93%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/HdHomerunHost.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/HdHomerunManager.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/HdHomerunUdpStream.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs (75%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs (94%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/LiveStream.cs (98%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/M3UTunerHost.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/M3uParser.cs (99%) rename {Emby.Server.Implementations/LiveTv => src/Jellyfin.LiveTv}/TunerHosts/SharedHttpStream.cs (98%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f385f6a512..bb565fb2ba 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -25,7 +25,6 @@ using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.HttpServer.Security; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Library; -using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Plugins; @@ -567,9 +566,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b3344bb9ff..34276355a7 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -22,7 +22,6 @@ - diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs index fb7377b1df..41c396ac1d 100644 --- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs +++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs @@ -17,7 +17,6 @@ using System.Runtime.InteropServices; [assembly: NeutralResourcesLanguage("en")] [assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")] [assembly: InternalsVisibleTo("Emby.Server.Implementations.Fuzz")] -[assembly: InternalsVisibleTo("Jellyfin.LiveTv.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/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 93277a981e..b1ac51f998 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -6,6 +6,7 @@ using Emby.Server.Implementations.Session; using Jellyfin.Api.WebSocketListeners; using Jellyfin.Drawing; using Jellyfin.Drawing.Skia; +using Jellyfin.LiveTv; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Devices; @@ -20,6 +21,7 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; @@ -96,6 +98,9 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + foreach (var type in GetExportTypes()) { serviceCollection.AddSingleton(typeof(ILyricProvider), type); @@ -117,6 +122,9 @@ namespace Jellyfin.Server // Jellyfin.Server.Implementations yield return typeof(JellyfinDbContext).Assembly; + + // Jellyfin.LiveTv + yield return typeof(LiveTvManager).Assembly; } } } diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 1d4d97551e..21c6e6f01d 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -58,6 +58,7 @@ + diff --git a/Jellyfin.sln b/Jellyfin.sln index 31e302d944..30eab6cc21 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -89,6 +89,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.MediaEncoding.Keyf EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.LiveTv.Tests", "tests\Jellyfin.LiveTv.Tests\Jellyfin.LiveTv.Tests.csproj", "{C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.LiveTv", "src\Jellyfin.LiveTv\Jellyfin.LiveTv.csproj", "{8C6B2B13-58A4-4506-9DAB-1F882A093FE0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -239,6 +241,10 @@ Global {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3}.Release|Any CPU.Build.0 = Release|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -266,6 +272,7 @@ Global {154872D9-6C12-4007-96E3-8F70A58386CE} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {0A3FCC4D-C714-4072-B90F-E374A15F9FF9} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {C4F71272-C6BE-4C30-BE0D-4E6ED651D6D3} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {8C6B2B13-58A4-4506-9DAB-1F882A093FE0} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs index 7df66d3585..2a25218b63 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/DirectRecorder.cs @@ -5,7 +5,6 @@ using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Helpers; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Streaming; @@ -13,7 +12,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class DirectRecorder : IRecorder { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs rename to src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index e2e0dfb2bb..439ed965b0 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -14,14 +14,12 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using Emby.Server.Implementations.Library; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -39,7 +37,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs index 9a9fd02739..132a5fc516 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EncodedRecorder.cs @@ -23,7 +23,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class EncodedRecorder : IRecorder { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs rename to src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs index a2ec2df375..e750c05ac8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EntryPoint.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Plugins; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public sealed class EntryPoint : IServerEntryPoint { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs similarity index 97% rename from Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs rename to src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs index 20a8213a77..43d308c434 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EpgChannelData.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { internal class EpgChannelData { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs rename to src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs index de14d6d086..7ed42e2634 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/IRecorder.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public interface IRecorder : IDisposable { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs rename to src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs index d5a6feb470..547ffeb668 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/ItemDataProvider.cs @@ -9,7 +9,7 @@ using System.Text.Json; using Jellyfin.Extensions.Json; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class ItemDataProvider where T : class diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs b/src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs rename to src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs index 83f5e84131..e8570f0e0d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/NfoConfigurationExtensions.cs @@ -1,7 +1,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { /// /// Class containing extension methods for working with the nfo configuration. diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs rename to src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs index 7bbeae866a..6bda231b24 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/RecordingHelper.cs @@ -5,7 +5,7 @@ using System.Globalization; using System.Text; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { internal static class RecordingHelper { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs rename to src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs index bf28f3b67d..2ebe60b296 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/SeriesTimerManager.cs @@ -4,7 +4,7 @@ using System; using MediaBrowser.Controller.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class SeriesTimerManager : ItemDataProvider { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs rename to src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs index 9f8441fa48..37b1fa14cc 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/TimerManager.cs @@ -10,7 +10,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.EmbyTV +namespace Jellyfin.LiveTv.EmbyTV { public class TimerManager : ItemDataProvider { diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/src/Jellyfin.LiveTv/ExclusiveLiveStream.cs similarity index 96% rename from Emby.Server.Implementations/Library/ExclusiveLiveStream.cs rename to src/Jellyfin.LiveTv/ExclusiveLiveStream.cs index b1649afad5..9d442e20cc 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/src/Jellyfin.LiveTv/ExclusiveLiveStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -10,7 +11,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.Library +namespace Jellyfin.LiveTv { public sealed class ExclusiveLiveStream : ILiveStream { diff --git a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj new file mode 100644 index 0000000000..3910064499 --- /dev/null +++ b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj @@ -0,0 +1,22 @@ + + + net8.0 + true + + + + + <_Parameter1>Jellyfin.LiveTv.Tests + + + + + + + + + + + + + diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs index 5be3a7488a..3b20cd160b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirect.cs @@ -16,9 +16,9 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; +using Jellyfin.LiveTv.Listings.SchedulesDirectDtos; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.LiveTv; @@ -27,7 +27,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.Listings +namespace Jellyfin.LiveTv.Listings { public class SchedulesDirect : IListingsProvider, IDisposable { @@ -613,6 +613,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings // Response is automatically disposed in the calling function, // so dispose manually if not returning. +#pragma warning disable IDISP016, IDISP017 response.Dispose(); if (!enableRetry || (int)response.StatusCode >= 500) { @@ -621,6 +622,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings null, response.StatusCode); } +#pragma warning restore IDISP016, IDISP017 _tokens.Clear(); options.Headers.TryAddWithoutValidation("token", await GetToken(providerInfo, cancellationToken).ConfigureAwait(false)); diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs index 95ac996e01..c1a502fd5a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Broadcaster dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs index f6251b9ad8..0cc39f3bb5 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Caption dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs index 0b7a2c63ad..bdcf87fdac 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CastDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Cast dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs index 87c327ed82..4e0d740785 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Channel dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs index c19cd2e48d..5c624c2885 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Content rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs index f00c9accdd..6d3c79c185 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Crew dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs index 1a371965cf..094f9a3194 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DayDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Day dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs index ca6ae7fb13..0063f4cc34 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Description 1_000 dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs similarity index 87% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs index 1577219ed2..1d9a18cc7f 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Description 100 dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs similarity index 90% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs index eaf4a340bd..75e91547bb 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Descriptions program dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs similarity index 81% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs index fbdfb1f716..28abe094ed 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Event details dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs index 6852d89d71..6eefc1744b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Gracenote dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs index b9844562f3..a62ae61f91 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Headends dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs index a1ae3ca6d4..21b595f244 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Image data dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs index 3dc64e5d8a..856b7a89b0 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// The lineup dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs index f190817813..99f80ce8a1 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Lineups dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs index fecc55e037..d7836384e2 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Logo dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs similarity index 95% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs index ffd02d474b..ea583a1cea 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MapDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Map dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs similarity index 89% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs index 40faa493c5..cafc8e2738 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs similarity index 82% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs index 43f2901566..243ccff5c0 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata programs dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs index 04560ab55d..1c5c5333ce 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Metadata schedule dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs similarity index 91% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs index 31bef423b2..aea7408336 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Movie dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs index e8b15dc07c..328cefadc3 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Multipart dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs index 84c48f67f3..8c3906f863 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Program details dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs similarity index 97% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs index 60389b45bf..527a6f8a19 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Program dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs index c5ddcf7c51..61496155ac 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Quality rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs similarity index 85% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs index e04b619a4d..287cd4ed5c 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Rating dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs index c8f79fd1c2..d380ec7aee 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Recommendation dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs similarity index 89% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs index 0cd05709b3..6fc695a393 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Request schedule for channel dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs similarity index 88% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs index 84e224b71e..523900a96a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Show image dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs similarity index 96% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs index d797fd49b1..dbde1e117d 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/StationDto.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Station dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs similarity index 80% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs index 61cd4a9b00..146124f987 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// Title dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs rename to src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs index afb9994869..b3bc618376 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs +++ b/src/Jellyfin.LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json.Serialization; -namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos +namespace Jellyfin.LiveTv.Listings.SchedulesDirectDtos { /// /// The token dto. diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs rename to src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs index e60e9dcc1c..cecc363f07 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/src/Jellyfin.LiveTv/Listings/XmlTvListingsProvider.cs @@ -23,7 +23,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.Listings +namespace Jellyfin.LiveTv.Listings { public class XmlTvListingsProvider : IListingsProvider { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs rename to src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs index 098f193fba..ddbf6345c5 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs +++ b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Model.LiveTv; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// implementation for . diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/src/Jellyfin.LiveTv/LiveTvDtoService.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs rename to src/Jellyfin.LiveTv/LiveTvDtoService.cs index 9326fbd5c7..7c7c26eb4a 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/src/Jellyfin.LiveTv/LiveTvDtoService.cs @@ -20,7 +20,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { public class LiveTvDtoService { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/LiveTvManager.cs rename to src/Jellyfin.LiveTv/LiveTvManager.cs index 426165de61..4fc9956538 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Implementations.Library; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; @@ -34,7 +33,7 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// Class LiveTvManager. @@ -231,7 +230,9 @@ namespace Emby.Server.Implementations.LiveTv _logger.LogInformation("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId); MediaSourceInfo info; +#pragma warning disable CA1859 // TODO: Analyzer bug? ILiveStream liveStream; +#pragma warning restore CA1859 if (service is ISupportsDirectStreamProvider supportsManagedStream) { liveStream = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, currentLiveStreams, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs rename to src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs index a5a1a4a4c5..ce9361089c 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/src/Jellyfin.LiveTv/LiveTvMediaSourceProvider.cs @@ -16,7 +16,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { public class LiveTvMediaSourceProvider : IMediaSourceProvider { diff --git a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs rename to src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs index 72bbdd14a6..e58296a70a 100644 --- a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs +++ b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs @@ -7,7 +7,7 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Tasks; -namespace Emby.Server.Implementations.LiveTv +namespace Jellyfin.LiveTv { /// /// The "Refresh Guide" scheduled task. diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs index da597056a4..769f196bdf 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs @@ -19,7 +19,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public abstract class BaseTunerHost { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs similarity index 86% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs index 0f04531896..311a71d137 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/Channels.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/Channels.cs @@ -1,6 +1,6 @@ #nullable disable -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { internal class Channels { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs similarity index 92% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs index 39b3571422..3ece181f25 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/DiscoverResponse.cs @@ -2,7 +2,7 @@ using System; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { internal class DiscoverResponse { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs similarity index 93% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs index aae33503fa..50a887826a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunChannelCommands : IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 79e15a82e2..b1b08e992b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -30,7 +30,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { @@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (!throwAllExceptions && ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) { const string DefaultValue = "HDHR"; - var response = new DiscoverResponse + var discoverResponse = new DiscoverResponse { ModelNumber = DefaultValue }; @@ -152,11 +152,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // HDHR4 doesn't have this api lock (_modelCache) { - _modelCache[cacheKey] = response; + _modelCache[cacheKey] = discoverResponse; } } - return response; + return discoverResponse; } throw; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 68383a5547..8613387270 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Controller.LiveTv; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public sealed class HdHomerunManager : IDisposable { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 6195c7648f..6c8cde62c9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -19,7 +20,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public class HdHomerunUdpStream : LiveStream, IDirectStreamProvider { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs similarity index 75% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs index 11bd40ab10..9fcf386f9c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public interface IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs similarity index 94% rename from Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs rename to src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs index 654474e971..6dc9c885f6 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun +namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { public partial class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs index c18594a29b..70d8afc5d0 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/LiveStream.cs @@ -1,5 +1,6 @@ #nullable disable +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -14,7 +15,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class LiveStream : ILiveStream { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs rename to src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs index 11bf03b182..7235e65b64 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs @@ -24,7 +24,7 @@ using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs similarity index 99% rename from Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs rename to src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs index 0b5575b994..5900d1c5be 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3uParser.cs @@ -18,7 +18,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public partial class M3uParser { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs similarity index 98% rename from Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs rename to src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs index efb84a5152..5ef04ad9e9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/SharedHttpStream.cs @@ -1,3 +1,4 @@ +#pragma warning disable CA1711 #pragma warning disable CS1591 using System; @@ -16,7 +17,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.LiveTv.TunerHosts +namespace Jellyfin.LiveTv.TunerHosts { public class SharedHttpStream : LiveStream, IDirectStreamProvider { diff --git a/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs index bc4b2da5b9..cb6eb91844 100644 --- a/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunHostTests.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; -using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; +using Jellyfin.LiveTv.TunerHosts.HdHomerun; using MediaBrowser.Model.LiveTv; using Moq; using Moq.Protected; diff --git a/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs index fcebc0ff75..4ab0bd723c 100644 --- a/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/HdHomerunManagerTests.cs @@ -1,6 +1,6 @@ using System; using System.Text; -using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun; +using Jellyfin.LiveTv.TunerHosts.HdHomerun; using Xunit; namespace Jellyfin.LiveTv.Tests diff --git a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj index de448bada6..f645f38c44 100644 --- a/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj +++ b/tests/Jellyfin.LiveTv.Tests/Jellyfin.LiveTv.Tests.csproj @@ -24,6 +24,6 @@ - + diff --git a/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs index 71d135735d..0fb7894e51 100644 --- a/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/Listings/XmlTvListingsProviderTests.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; -using Emby.Server.Implementations.LiveTv.Listings; +using Jellyfin.LiveTv.Listings; using MediaBrowser.Model.LiveTv; using Moq; using Moq.Protected; diff --git a/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs index a5941e7f08..b4960dc0b3 100644 --- a/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/RecordingHelperTests.cs @@ -1,5 +1,5 @@ using System; -using Emby.Server.Implementations.LiveTv.EmbyTV; +using Jellyfin.LiveTv.EmbyTV; using MediaBrowser.Controller.LiveTv; using Xunit; diff --git a/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs index 6b1f4d4161..6975d56d9e 100644 --- a/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs +++ b/tests/Jellyfin.LiveTv.Tests/SchedulesDirect/SchedulesDirectDeserializeTests.cs @@ -2,8 +2,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Text.Json; -using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions.Json; +using Jellyfin.LiveTv.Listings.SchedulesDirectDtos; using Xunit; namespace Jellyfin.LiveTv.Tests.SchedulesDirect From 126aa9c893a5b5e3107ca9b6355d354753d45ed3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 9 Jan 2024 09:54:02 -0500 Subject: [PATCH 094/132] Move channels to LiveTv project --- Emby.Server.Implementations/ApplicationHost.cs | 3 --- Jellyfin.Server/CoreAppHost.cs | 3 +++ .../Channels/ChannelDynamicMediaSourceProvider.cs | 2 +- .../Jellyfin.LiveTv}/Channels/ChannelImageProvider.cs | 2 +- .../Jellyfin.LiveTv}/Channels/ChannelManager.cs | 2 +- .../Jellyfin.LiveTv}/Channels/ChannelPostScanTask.cs | 2 +- .../Jellyfin.LiveTv}/Channels/RefreshChannelsScheduledTask.cs | 2 +- src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj | 1 + 8 files changed, 9 insertions(+), 8 deletions(-) rename {Emby.Server.Implementations => src/Jellyfin.LiveTv}/Channels/ChannelDynamicMediaSourceProvider.cs (96%) rename {Emby.Server.Implementations => src/Jellyfin.LiveTv}/Channels/ChannelImageProvider.cs (97%) rename {Emby.Server.Implementations => src/Jellyfin.LiveTv}/Channels/ChannelManager.cs (99%) rename {Emby.Server.Implementations => src/Jellyfin.LiveTv}/Channels/ChannelPostScanTask.cs (98%) rename {Emby.Server.Implementations => src/Jellyfin.LiveTv}/Channels/RefreshChannelsScheduledTask.cs (98%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index bb565fb2ba..0b6f4178bc 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -15,7 +15,6 @@ using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Emby.Naming.Common; using Emby.Photos; -using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Cryptography; @@ -556,8 +555,6 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index b1ac51f998..fb01f6edb9 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -7,6 +7,7 @@ using Jellyfin.Api.WebSocketListeners; using Jellyfin.Drawing; using Jellyfin.Drawing.Skia; using Jellyfin.LiveTv; +using Jellyfin.LiveTv.Channels; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Devices; @@ -17,6 +18,7 @@ using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.BaseItemManager; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; @@ -100,6 +102,7 @@ namespace Jellyfin.Server serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); foreach (var type in GetExportTypes()) { diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs similarity index 96% rename from Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs rename to src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs index 3e149cc82c..839549ed67 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelDynamicMediaSourceProvider.cs @@ -8,7 +8,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// A media source provider for channels. diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs similarity index 97% rename from Emby.Server.Implementations/Channels/ChannelImageProvider.cs rename to src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs index 25cbfcf146..32e224550f 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelImageProvider.cs @@ -7,7 +7,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// An image provider for channels. diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs similarity index 99% rename from Emby.Server.Implementations/Channels/ChannelManager.cs rename to src/Jellyfin.LiveTv/Channels/ChannelManager.cs index c505e8fe13..f5ce75ff4d 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -34,7 +34,7 @@ using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; using Season = MediaBrowser.Controller.Entities.TV.Season; using Series = MediaBrowser.Controller.Entities.TV.Series; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// The LiveTV channel manager. diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs similarity index 98% rename from Emby.Server.Implementations/Channels/ChannelPostScanTask.cs rename to src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs index b358ba4d55..b4f6cf731e 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelPostScanTask.cs @@ -8,7 +8,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// A task to remove all non-installed channels from the database. diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs similarity index 98% rename from Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs rename to src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs index cfd08e6535..556e052d4e 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.Channels +namespace Jellyfin.LiveTv.Channels { /// /// The "Refresh Channels" scheduled task. diff --git a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj index 3910064499..5a826a1da0 100644 --- a/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj +++ b/src/Jellyfin.LiveTv/Jellyfin.LiveTv.csproj @@ -12,6 +12,7 @@ + From c964ea23bf19246da647907051ce4ff88f2a599d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 9 Jan 2024 10:05:31 -0500 Subject: [PATCH 095/132] Move RecordingNotifier to LiveTv project --- .../EntryPoints => src/Jellyfin.LiveTv}/RecordingNotifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {Emby.Server.Implementations/EntryPoints => src/Jellyfin.LiveTv}/RecordingNotifier.cs (98%) diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/src/Jellyfin.LiveTv/RecordingNotifier.cs similarity index 98% rename from Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs rename to src/Jellyfin.LiveTv/RecordingNotifier.cs index e0ca02d986..2923948ebf 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/src/Jellyfin.LiveTv/RecordingNotifier.cs @@ -15,7 +15,7 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; -namespace Emby.Server.Implementations.EntryPoints +namespace Jellyfin.LiveTv { public sealed class RecordingNotifier : IServerEntryPoint { From c8e979cdd6bcdd2f7bfcb33e4e9a6fb126bba295 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:46:30 +0000 Subject: [PATCH 096/132] chore(deps): update dotnet monorepo to v8.0.1 --- .config/dotnet-tools.json | 2 +- Directory.Packages.props | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c03564f970..81fe5add42 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-ef": { - "version": "8.0.0", + "version": "8.0.1", "commands": [ "dotnet-ef" ] diff --git a/Directory.Packages.props b/Directory.Packages.props index 6c662d600b..b619535e5f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -23,30 +23,30 @@ - + - + - - - - - + + + + + - + - - + + - + @@ -77,7 +77,7 @@ - + From 41de6d17411a3e5cb962da06c18ffb2a69b40849 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 9 Jan 2024 11:57:55 -0500 Subject: [PATCH 097/132] Move StreamHelper to LiveTv project --- Emby.Server.Implementations/ApplicationHost.cs | 2 -- Jellyfin.Server/CoreAppHost.cs | 2 ++ .../IO => src/Jellyfin.LiveTv}/StreamHelper.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename {Emby.Server.Implementations/IO => src/Jellyfin.LiveTv}/StreamHelper.cs (99%) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0b6f4178bc..48d5d8c6a9 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -502,8 +502,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(_xmlSerializer); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index fb01f6edb9..5192b9e21b 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -29,6 +29,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Trickplay; using MediaBrowser.Model.Activity; +using MediaBrowser.Model.IO; using MediaBrowser.Providers.Lyric; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -103,6 +104,7 @@ namespace Jellyfin.Server serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); foreach (var type in GetExportTypes()) { diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/src/Jellyfin.LiveTv/StreamHelper.cs similarity index 99% rename from Emby.Server.Implementations/IO/StreamHelper.cs rename to src/Jellyfin.LiveTv/StreamHelper.cs index 6eaf22ce48..ab4b6e9b15 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/src/Jellyfin.LiveTv/StreamHelper.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; -namespace Emby.Server.Implementations.IO +namespace Jellyfin.LiveTv { public class StreamHelper : IStreamHelper { From 56eea081768e598d8f47718df31d72c91991303d Mon Sep 17 00:00:00 2001 From: Achim Walz Date: Tue, 9 Jan 2024 17:47:44 +0000 Subject: [PATCH 098/132] Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index f1dbf3c89d..7a4c2067ba 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -121,8 +121,8 @@ "Default": "Standard", "TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.", "TaskOptimizeDatabase": "Datenbank optimieren", - "TaskKeyframeExtractorDescription": "Extrahiere Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.", - "TaskKeyframeExtractor": "Keyframe Extraktor", + "TaskKeyframeExtractorDescription": "Extrahiert Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.", + "TaskKeyframeExtractor": "Keyframe-Extraktor", "External": "Extern", "HearingImpaired": "Hörgeschädigt", "TaskRefreshTrickplayImages": "Trickplay-Bilder generieren", From 61f99dfffb6130c2fd56e2cf7b7f600a963ad6c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:50:14 +0000 Subject: [PATCH 099/132] chore(deps): update actions/download-artifact action to v4.1.1 --- .github/workflows/ci-openapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index c9f17429a2..693d28a48f 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -78,12 +78,12 @@ jobs: - openapi-base steps: - name: Download openapi-head - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: openapi-head path: openapi-head - name: Download openapi-base - uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0 + uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1 with: name: openapi-base path: openapi-base From ad2f38ba0bcf26495a76a4cc80f8f9f65c0886ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 09:22:17 -0700 Subject: [PATCH 100/132] chore(deps): update skiasharp monorepo (#10849) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b619535e5f..294414ee12 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + @@ -66,9 +66,9 @@ - - - + + + From 68fd9c469f013acef3e661d15adb61c4eb965ec7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 11:24:32 -0500 Subject: [PATCH 101/132] Remove DLNA-specific DeviceProfile code (#10850) * Remove DLNA-specific methods from DeviceProfile * Remove DLNA-specific fields from DeviceProfile * Remove unused DLNA models --- .../Dlna/DeviceIdentification.cs | 63 --- MediaBrowser.Model/Dlna/DeviceProfile.cs | 439 +----------------- MediaBrowser.Model/Dlna/DeviceProfileInfo.cs | 26 -- MediaBrowser.Model/Dlna/DeviceProfileType.cs | 10 - MediaBrowser.Model/Dlna/HeaderMatchType.cs | 11 - MediaBrowser.Model/Dlna/HttpHeaderInfo.cs | 19 - MediaBrowser.Model/Dlna/ResponseProfile.cs | 51 -- MediaBrowser.Model/Dlna/XmlAttribute.cs | 25 - 8 files changed, 1 insertion(+), 643 deletions(-) delete mode 100644 MediaBrowser.Model/Dlna/DeviceIdentification.cs delete mode 100644 MediaBrowser.Model/Dlna/DeviceProfileInfo.cs delete mode 100644 MediaBrowser.Model/Dlna/DeviceProfileType.cs delete mode 100644 MediaBrowser.Model/Dlna/HeaderMatchType.cs delete mode 100644 MediaBrowser.Model/Dlna/HttpHeaderInfo.cs delete mode 100644 MediaBrowser.Model/Dlna/ResponseProfile.cs delete mode 100644 MediaBrowser.Model/Dlna/XmlAttribute.cs diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs deleted file mode 100644 index 6625b79814..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs +++ /dev/null @@ -1,63 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Dlna -{ - public class DeviceIdentification - { - /// - /// Gets or sets the name of the friendly. - /// - /// The name of the friendly. - public string FriendlyName { get; set; } = string.Empty; - - /// - /// Gets or sets the model number. - /// - /// The model number. - public string ModelNumber { get; set; } = string.Empty; - - /// - /// Gets or sets the serial number. - /// - /// The serial number. - public string SerialNumber { get; set; } = string.Empty; - - /// - /// Gets or sets the name of the model. - /// - /// The name of the model. - public string ModelName { get; set; } = string.Empty; - - /// - /// Gets or sets the model description. - /// - /// The model description. - public string ModelDescription { get; set; } = string.Empty; - - /// - /// Gets or sets the model URL. - /// - /// The model URL. - public string ModelUrl { get; set; } = string.Empty; - - /// - /// Gets or sets the manufacturer. - /// - /// The manufacturer. - public string Manufacturer { get; set; } = string.Empty; - - /// - /// Gets or sets the manufacturer URL. - /// - /// The manufacturer URL. - public string ManufacturerUrl { get; set; } = string.Empty; - - /// - /// Gets or sets the headers. - /// - /// The headers. - public HttpHeaderInfo[] Headers { get; set; } = Array.Empty(); - } -} diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 71d0896a70..2addebbfca 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -1,11 +1,7 @@ #pragma warning disable CA1819 // Properties should not return arrays + using System; -using System.ComponentModel; -using System.Linq; using System.Xml.Serialization; -using Jellyfin.Data.Enums; -using Jellyfin.Extensions; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Model.Dlna { @@ -17,7 +13,6 @@ namespace MediaBrowser.Model.Dlna /// the device is able to direct play (without transcoding or remuxing), /// as well as which containers/codecs to transcode to in case it isn't. /// - [XmlRoot("Profile")] public class DeviceProfile { /// @@ -31,104 +26,6 @@ namespace MediaBrowser.Model.Dlna [XmlIgnore] public string? Id { get; set; } - /// - /// Gets or sets the Identification. - /// - public DeviceIdentification? Identification { get; set; } - - /// - /// Gets or sets the friendly name of the device profile, which can be shown to users. - /// - public string? FriendlyName { get; set; } - - /// - /// Gets or sets the manufacturer of the device which this profile represents. - /// - public string? Manufacturer { get; set; } - - /// - /// Gets or sets an url for the manufacturer of the device which this profile represents. - /// - public string? ManufacturerUrl { get; set; } - - /// - /// Gets or sets the model name of the device which this profile represents. - /// - public string? ModelName { get; set; } - - /// - /// Gets or sets the model description of the device which this profile represents. - /// - public string? ModelDescription { get; set; } - - /// - /// Gets or sets the model number of the device which this profile represents. - /// - public string? ModelNumber { get; set; } - - /// - /// Gets or sets the ModelUrl. - /// - public string? ModelUrl { get; set; } - - /// - /// Gets or sets the serial number of the device which this profile represents. - /// - public string? SerialNumber { get; set; } - - /// - /// Gets or sets a value indicating whether EnableAlbumArtInDidl. - /// - [DefaultValue(false)] - public bool EnableAlbumArtInDidl { get; set; } - - /// - /// Gets or sets a value indicating whether EnableSingleAlbumArtLimit. - /// - [DefaultValue(false)] - public bool EnableSingleAlbumArtLimit { get; set; } - - /// - /// Gets or sets a value indicating whether EnableSingleSubtitleLimit. - /// - [DefaultValue(false)] - public bool EnableSingleSubtitleLimit { get; set; } - - /// - /// Gets or sets the SupportedMediaTypes. - /// - public string SupportedMediaTypes { get; set; } = "Audio,Photo,Video"; - - /// - /// Gets or sets the UserId. - /// - public string? UserId { get; set; } - - /// - /// Gets or sets the AlbumArtPn. - /// - public string? AlbumArtPn { get; set; } - - /// - /// Gets or sets the MaxAlbumArtWidth. - /// - public int? MaxAlbumArtWidth { get; set; } - - /// - /// Gets or sets the MaxAlbumArtHeight. - /// - public int? MaxAlbumArtHeight { get; set; } - - /// - /// Gets or sets the maximum allowed width of embedded icons. - /// - public int? MaxIconWidth { get; set; } - - /// - /// Gets or sets the maximum allowed height of embedded icons. - /// - public int? MaxIconHeight { get; set; } - /// /// Gets or sets the maximum allowed bitrate for all streamed content. /// @@ -149,51 +46,6 @@ namespace MediaBrowser.Model.Dlna /// public int? MaxStaticMusicBitrate { get; set; } = 8000000; - /// - /// Gets or sets the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. - /// - public string? SonyAggregationFlags { get; set; } - - /// - /// Gets or sets the ProtocolInfo. - /// - public string? ProtocolInfo { get; set; } - - /// - /// Gets or sets the TimelineOffsetSeconds. - /// - [DefaultValue(0)] - public int TimelineOffsetSeconds { get; set; } - - /// - /// Gets or sets a value indicating whether RequiresPlainVideoItems. - /// - [DefaultValue(false)] - public bool RequiresPlainVideoItems { get; set; } - - /// - /// Gets or sets a value indicating whether RequiresPlainFolders. - /// - [DefaultValue(false)] - public bool RequiresPlainFolders { get; set; } - - /// - /// Gets or sets a value indicating whether EnableMSMediaReceiverRegistrar. - /// - [DefaultValue(false)] - public bool EnableMSMediaReceiverRegistrar { get; set; } - - /// - /// Gets or sets a value indicating whether IgnoreTranscodeByteRangeRequests. - /// - [DefaultValue(false)] - public bool IgnoreTranscodeByteRangeRequests { get; set; } - - /// - /// Gets or sets the XmlRootAttributes. - /// - public XmlAttribute[] XmlRootAttributes { get; set; } = Array.Empty(); - /// /// Gets or sets the direct play profiles. /// @@ -214,298 +66,9 @@ namespace MediaBrowser.Model.Dlna /// public CodecProfile[] CodecProfiles { get; set; } = Array.Empty(); - /// - /// Gets or sets the ResponseProfiles. - /// - public ResponseProfile[] ResponseProfiles { get; set; } = Array.Empty(); - /// /// Gets or sets the subtitle profiles. /// public SubtitleProfile[] SubtitleProfiles { get; set; } = Array.Empty(); - - /// - /// The GetSupportedMediaTypes. - /// - /// The . - public MediaType[] GetSupportedMediaTypes() - { - return ContainerProfile.SplitValue(SupportedMediaTypes) - .Select(m => Enum.TryParse(m, out var parsed) ? parsed : MediaType.Unknown) - .Where(m => m != MediaType.Unknown) - .ToArray(); - } - - /// - /// Gets the audio transcoding profile. - /// - /// The container. - /// The audio Codec. - /// A . - public TranscodingProfile? GetAudioTranscodingProfile(string? container, string? audioCodec) - { - container = (container ?? string.Empty).TrimStart('.'); - - foreach (var i in TranscodingProfiles) - { - if (i.Type != DlnaProfileType.Audio) - { - continue; - } - - if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the video transcoding profile. - /// - /// The container. - /// The audio Codec. - /// The video Codec. - /// The . - public TranscodingProfile? GetVideoTranscodingProfile(string? container, string? audioCodec, string? videoCodec) - { - container = (container ?? string.Empty).TrimStart('.'); - - foreach (var i in TranscodingProfiles) - { - if (i.Type != DlnaProfileType.Video) - { - continue; - } - - if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!i.GetAudioCodecs().Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!string.Equals(videoCodec, i.VideoCodec, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the audio media profile. - /// - /// The container. - /// The audio codec. - /// The audio channels. - /// The audio bitrate. - /// The audio sample rate. - /// The audio bit depth. - /// The . - public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Audio) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var anyOff = false; - foreach (ProfileCondition c in i.Conditions) - { - if (!ConditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the model profile condition. - /// - /// The c. - /// The . - private ProfileCondition GetModelProfileCondition(ProfileCondition c) - { - return new ProfileCondition - { - Condition = c.Condition, - IsRequired = c.IsRequired, - Property = c.Property, - Value = c.Value - }; - } - - /// - /// Gets the image media profile. - /// - /// The container. - /// The width. - /// The height. - /// The . - public ResponseProfile? GetImageMediaProfile(string container, int? width, int? height) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Photo) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var anyOff = false; - foreach (var c in i.Conditions) - { - if (!ConditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } - - /// - /// Gets the video media profile. - /// - /// The container. - /// The audio codec. - /// The video codec. - /// The width. - /// The height. - /// The bit depth. - /// The video bitrate. - /// The video profile. - /// The video range type. - /// The video level. - /// The video framerate. - /// The packet length. - /// The timestamp. - /// True if anamorphic. - /// True if interlaced. - /// The ref frames. - /// The number of video streams. - /// The number of audio streams. - /// The video Codec tag. - /// True if Avc. - /// The . - public ResponseProfile? GetVideoMediaProfile( - string? container, - string? audioCodec, - string? videoCodec, - int? width, - int? height, - int? bitDepth, - int? videoBitrate, - string? videoProfile, - VideoRangeType videoRangeType, - double? videoLevel, - float? videoFramerate, - int? packetLength, - TransportStreamTimestamp timestamp, - bool? isAnamorphic, - bool? isInterlaced, - int? refFrames, - int? numVideoStreams, - int? numAudioStreams, - string? videoCodecTag, - bool? isAvc) - { - foreach (var i in ResponseProfiles) - { - if (i.Type != DlnaProfileType.Video) - { - continue; - } - - if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) - { - continue; - } - - var audioCodecs = i.GetAudioCodecs(); - if (audioCodecs.Length > 0 && !audioCodecs.Contains(audioCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var videoCodecs = i.GetVideoCodecs(); - if (videoCodecs.Length > 0 && !videoCodecs.Contains(videoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var anyOff = false; - foreach (ProfileCondition c in i.Conditions) - { - if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) - { - anyOff = true; - break; - } - } - - if (anyOff) - { - continue; - } - - return i; - } - - return null; - } } } diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs deleted file mode 100644 index 74c32c523e..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public class DeviceProfileInfo - { - /// - /// Gets or sets the identifier. - /// - /// The identifier. - public string Id { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the type. - /// - /// The type. - public DeviceProfileType Type { get; set; } - } -} diff --git a/MediaBrowser.Model/Dlna/DeviceProfileType.cs b/MediaBrowser.Model/Dlna/DeviceProfileType.cs deleted file mode 100644 index 46062abd09..0000000000 --- a/MediaBrowser.Model/Dlna/DeviceProfileType.cs +++ /dev/null @@ -1,10 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public enum DeviceProfileType - { - System = 0, - User = 1 - } -} diff --git a/MediaBrowser.Model/Dlna/HeaderMatchType.cs b/MediaBrowser.Model/Dlna/HeaderMatchType.cs deleted file mode 100644 index 2a9abb20eb..0000000000 --- a/MediaBrowser.Model/Dlna/HeaderMatchType.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Dlna -{ - public enum HeaderMatchType - { - Equals = 0, - Regex = 1, - Substring = 2 - } -} diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs deleted file mode 100644 index 17c4dffcc0..0000000000 --- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - public class HttpHeaderInfo - { - [XmlAttribute("name")] - public string Name { get; set; } - - [XmlAttribute("value")] - public string Value { get; set; } - - [XmlAttribute("match")] - public HeaderMatchType Match { get; set; } - } -} diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs deleted file mode 100644 index bf9661f7f3..0000000000 --- a/MediaBrowser.Model/Dlna/ResponseProfile.cs +++ /dev/null @@ -1,51 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - public class ResponseProfile - { - public ResponseProfile() - { - Conditions = Array.Empty(); - } - - [XmlAttribute("container")] - public string Container { get; set; } - - [XmlAttribute("audioCodec")] - public string AudioCodec { get; set; } - - [XmlAttribute("videoCodec")] - public string VideoCodec { get; set; } - - [XmlAttribute("type")] - public DlnaProfileType Type { get; set; } - - [XmlAttribute("orgPn")] - public string OrgPn { get; set; } - - [XmlAttribute("mimeType")] - public string MimeType { get; set; } - - public ProfileCondition[] Conditions { get; set; } - - public string[] GetContainers() - { - return ContainerProfile.SplitValue(Container); - } - - public string[] GetAudioCodecs() - { - return ContainerProfile.SplitValue(AudioCodec); - } - - public string[] GetVideoCodecs() - { - return ContainerProfile.SplitValue(VideoCodec); - } - } -} diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs deleted file mode 100644 index 03bb2e4b11..0000000000 --- a/MediaBrowser.Model/Dlna/XmlAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System.Xml.Serialization; - -namespace MediaBrowser.Model.Dlna -{ - /// - /// Defines the . - /// - public class XmlAttribute - { - /// - /// Gets or sets the name of the attribute. - /// - [XmlAttribute("name")] - public string Name { get; set; } - - /// - /// Gets or sets the value of the attribute. - /// - [XmlAttribute("value")] - public string Value { get; set; } - } -} From a9e0fb0322491ee71e6f8f09b11f8f68bd58019e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 20:17:54 +0000 Subject: [PATCH 102/132] chore(deps): update actions/upload-artifact action to v4.1.0 --- .github/workflows/ci-openapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 693d28a48f..75ec82c5f1 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -25,7 +25,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 with: name: openapi-head retention-days: 14 @@ -59,7 +59,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 + uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 with: name: openapi-base retention-days: 14 From ad51f4f95d09f8ed64fed6013a47f87c01fcca5f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 18:37:14 -0500 Subject: [PATCH 103/132] Add LiveTv configuration extension --- .../LiveTvConfigurationExtensions.cs | 18 ++++++++++++ src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs | 28 ++++++++----------- src/Jellyfin.LiveTv/LiveTvManager.cs | 25 +++++++---------- .../RefreshGuideScheduledTask.cs | 9 ++---- .../TunerHosts/BaseTunerHost.cs | 9 ++---- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 3 +- 6 files changed, 46 insertions(+), 46 deletions(-) create mode 100644 src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs diff --git a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs new file mode 100644 index 0000000000..67d0e5295b --- /dev/null +++ b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationExtensions.cs @@ -0,0 +1,18 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.LiveTv; + +namespace Jellyfin.LiveTv.Configuration; + +/// +/// extensions for Live TV. +/// +public static class LiveTvConfigurationExtensions +{ + /// + /// Gets the . + /// + /// The . + /// The . + public static LiveTvOptions GetLiveTvConfiguration(this IConfigurationManager configurationManager) + => configurationManager.GetConfiguration("livetv"); +} diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index 439ed965b0..532e1c897d 100644 --- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -17,6 +17,7 @@ using System.Xml; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Extensions; +using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; @@ -126,7 +127,7 @@ namespace Jellyfin.LiveTv.EmbyTV { get { - var path = GetConfiguration().RecordingPath; + var path = _config.GetLiveTvConfiguration().RecordingPath; return string.IsNullOrWhiteSpace(path) ? DefaultRecordingPath @@ -189,7 +190,7 @@ namespace Jellyfin.LiveTv.EmbyTV pathsAdded.AddRange(pathsToCreate); } - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); var pathsToRemove = config.MediaLocationsCreated .Except(recordingFolders.SelectMany(i => i.Locations)) @@ -831,7 +832,7 @@ namespace Jellyfin.LiveTv.EmbyTV public Task GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null) { - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); var defaults = new SeriesTimerInfo() { @@ -932,7 +933,7 @@ namespace Jellyfin.LiveTv.EmbyTV private List> GetListingProviders() { - return GetConfiguration().ListingProviders + return _config.GetLiveTvConfiguration().ListingProviders .Select(i => { var provider = _liveTvManager.ListingProviders.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase)); @@ -1076,7 +1077,7 @@ namespace Jellyfin.LiveTv.EmbyTV private string GetRecordingPath(TimerInfo timer, RemoteSearchResult metadata, out string seriesPath) { var recordPath = RecordingPath; - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); seriesPath = null; if (timer.IsProgramSeries) @@ -1596,7 +1597,7 @@ namespace Jellyfin.LiveTv.EmbyTV private void PostProcessRecording(TimerInfo timer, string path) { - var options = GetConfiguration(); + var options = _config.GetLiveTvConfiguration(); if (string.IsNullOrWhiteSpace(options.RecordingPostProcessor)) { return; @@ -1777,7 +1778,7 @@ namespace Jellyfin.LiveTv.EmbyTV program.AddGenre("News"); } - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); if (config.SaveRecordingNFO) { @@ -2128,11 +2129,6 @@ namespace Jellyfin.LiveTv.EmbyTV return _libraryManager.GetItemList(query).Cast().FirstOrDefault(); } - private LiveTvOptions GetConfiguration() - { - return _config.GetConfiguration("livetv"); - } - private bool ShouldCancelTimerForSeriesTimer(SeriesTimerInfo seriesTimer, TimerInfo timer) { if (timer.IsManual) @@ -2519,7 +2515,7 @@ namespace Jellyfin.LiveTv.EmbyTV }; } - var customPath = GetConfiguration().MovieRecordingPath; + var customPath = _config.GetLiveTvConfiguration().MovieRecordingPath; if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath)) { yield return new VirtualFolderInfo @@ -2530,7 +2526,7 @@ namespace Jellyfin.LiveTv.EmbyTV }; } - customPath = GetConfiguration().SeriesRecordingPath; + customPath = _config.GetLiveTvConfiguration().SeriesRecordingPath; if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath)) { yield return new VirtualFolderInfo @@ -2546,7 +2542,7 @@ namespace Jellyfin.LiveTv.EmbyTV { var list = new List(); - var configuredDeviceIds = GetConfiguration().TunerHosts + var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId)) .Select(i => i.DeviceId) .ToList(); @@ -2579,7 +2575,7 @@ namespace Jellyfin.LiveTv.EmbyTV { var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); - var configuredDevices = GetConfiguration().TunerHosts + var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) .ToList(); diff --git a/src/Jellyfin.LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs index 4fc9956538..0b3d35731a 100644 --- a/src/Jellyfin.LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; -using MediaBrowser.Common.Configuration; +using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; @@ -108,11 +108,6 @@ namespace Jellyfin.LiveTv public IReadOnlyList ListingProviders => _listingProviders; - private LiveTvOptions GetConfiguration() - { - return _config.GetConfiguration("livetv"); - } - public string GetEmbyTvActiveRecordingPath(string id) { return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id); @@ -1302,7 +1297,7 @@ namespace Jellyfin.LiveTv private double GetGuideDays() { - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); if (config.GuideDays.HasValue) { @@ -2125,7 +2120,7 @@ namespace Jellyfin.LiveTv private bool IsLiveTvEnabled(User user) { - return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0); + return user.HasPermission(PermissionKind.EnableLiveTvAccess) && (Services.Count > 1 || _config.GetLiveTvConfiguration().TunerHosts.Length > 0); } public IEnumerable GetEnabledUsers() @@ -2187,7 +2182,7 @@ namespace Jellyfin.LiveTv await configurable.Validate(info).ConfigureAwait(false); } - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); var list = config.TunerHosts.ToList(); var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); @@ -2232,7 +2227,7 @@ namespace Jellyfin.LiveTv await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false); - LiveTvOptions config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); var list = config.ListingProviders.ToList(); int index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); @@ -2257,7 +2252,7 @@ namespace Jellyfin.LiveTv public void DeleteListingsProvider(string id) { - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray(); @@ -2267,7 +2262,7 @@ namespace Jellyfin.LiveTv public async Task SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber) { - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase)); listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)).ToArray(); @@ -2327,7 +2322,7 @@ namespace Jellyfin.LiveTv public Task> GetLineups(string providerType, string providerId, string country, string location) { - var config = GetConfiguration(); + var config = _config.GetLiveTvConfiguration(); if (string.IsNullOrWhiteSpace(providerId)) { @@ -2357,13 +2352,13 @@ namespace Jellyfin.LiveTv public Task> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken) { - var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); + var info = _config.GetLiveTvConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); return EmbyTV.EmbyTV.Current.GetChannelsForListingsProvider(info, cancellationToken); } public Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken) { - var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); + var info = _config.GetLiveTvConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); var provider = _listingProviders.First(i => string.Equals(i.Type, info.Type, StringComparison.OrdinalIgnoreCase)); return provider.GetChannels(info, cancellationToken); } diff --git a/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs index e58296a70a..18bd61d999 100644 --- a/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs +++ b/src/Jellyfin.LiveTv/RefreshGuideScheduledTask.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Tasks; namespace Jellyfin.LiveTv @@ -38,7 +38,7 @@ namespace Jellyfin.LiveTv public string Category => "Live TV"; /// - public bool IsHidden => _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Length == 0; + public bool IsHidden => _liveTvManager.Services.Count == 1 && _config.GetLiveTvConfiguration().TunerHosts.Length == 0; /// public bool IsEnabled => true; @@ -66,10 +66,5 @@ namespace Jellyfin.LiveTv new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } - - private LiveTvOptions GetConfiguration() - { - return _config.GetConfiguration("livetv"); - } } } diff --git a/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs index 769f196bdf..afc2e4f9ce 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/BaseTunerHost.cs @@ -10,7 +10,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; +using Jellyfin.LiveTv.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -69,7 +69,7 @@ namespace Jellyfin.LiveTv.TunerHosts protected virtual IList GetTunerHosts() { - return GetConfiguration().TunerHosts + return Config.GetLiveTvConfiguration().TunerHosts .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) .ToList(); } @@ -228,10 +228,5 @@ namespace Jellyfin.LiveTv.TunerHosts return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); } - - protected LiveTvOptions GetConfiguration() - { - return Config.GetConfiguration("livetv"); - } } } diff --git a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index b1b08e992b..a56af65a6f 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -16,6 +16,7 @@ using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; +using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -278,7 +279,7 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun { var list = new List(); - foreach (var host in GetConfiguration().TunerHosts + foreach (var host in Config.GetLiveTvConfiguration().TunerHosts .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))) { try From 063168fc1c5942e1e386eaa6f13755192a0527c4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 18:38:23 -0500 Subject: [PATCH 104/132] Move LiveTvConfigurationFactory to Configuration folder --- .../LiveTvConfigurationFactory.cs | 24 ++++++++++++++++++ .../LiveTvConfigurationFactory.cs | 25 ------------------- 2 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs delete mode 100644 src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs diff --git a/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs new file mode 100644 index 0000000000..258afbb056 --- /dev/null +++ b/src/Jellyfin.LiveTv/Configuration/LiveTvConfigurationFactory.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.LiveTv; + +namespace Jellyfin.LiveTv.Configuration; + +/// +/// implementation for . +/// +public class LiveTvConfigurationFactory : IConfigurationFactory +{ + /// + public IEnumerable GetConfigurations() + { + return new[] + { + new ConfigurationStore + { + ConfigurationType = typeof(LiveTvOptions), + Key = "livetv" + } + }; + } +} diff --git a/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs b/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs deleted file mode 100644 index ddbf6345c5..0000000000 --- a/src/Jellyfin.LiveTv/LiveTvConfigurationFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.LiveTv; - -namespace Jellyfin.LiveTv -{ - /// - /// implementation for . - /// - public class LiveTvConfigurationFactory : IConfigurationFactory - { - /// - public IEnumerable GetConfigurations() - { - return new ConfigurationStore[] - { - new ConfigurationStore - { - ConfigurationType = typeof(LiveTvOptions), - Key = "livetv" - } - }; - } - } -} From 449365182cb710e5d0d18b1d599a53f76b814dd8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 19:07:44 -0500 Subject: [PATCH 105/132] Move LiveTV service registration to extension method --- Jellyfin.Server/CoreAppHost.cs | 9 ------- Jellyfin.Server/Startup.cs | 2 ++ .../LiveTvServiceCollectionExtensions.cs | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) create mode 100644 src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 5192b9e21b..d5b6e93b8e 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -7,7 +7,6 @@ using Jellyfin.Api.WebSocketListeners; using Jellyfin.Drawing; using Jellyfin.Drawing.Skia; using Jellyfin.LiveTv; -using Jellyfin.LiveTv.Channels; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; using Jellyfin.Server.Implementations.Devices; @@ -18,18 +17,15 @@ using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.BaseItemManager; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Trickplay; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.IO; using MediaBrowser.Providers.Lyric; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -101,11 +97,6 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - foreach (var type in GetExportTypes()) { serviceCollection.AddSingleton(typeof(ILyricProvider), type); diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 1030c6f5fc..7d5f22545d 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -5,6 +5,7 @@ using System.Net.Http.Headers; using System.Net.Mime; using System.Text; using Jellyfin.Api.Middleware; +using Jellyfin.LiveTv.Extensions; using Jellyfin.MediaEncoding.Hls.Extensions; using Jellyfin.Networking; using Jellyfin.Networking.HappyEyeballs; @@ -121,6 +122,7 @@ namespace Jellyfin.Server .AddCheck>(nameof(JellyfinDbContext)); services.AddHlsPlaylistGenerator(); + services.AddLiveTvServices(); services.AddHostedService(); } diff --git a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs new file mode 100644 index 0000000000..5865e88af0 --- /dev/null +++ b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +using Jellyfin.LiveTv.Channels; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.IO; +using Microsoft.Extensions.DependencyInjection; + +namespace Jellyfin.LiveTv.Extensions; + +/// +/// Live TV extensions for . +/// +public static class LiveTvServiceCollectionExtensions +{ + /// + /// Adds Live TV services to the . + /// + /// The to add services to. + public static void AddLiveTvServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } +} From 9c2c066e6f62fb713d5bad0fcf5a0b3dcf58e6e1 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 20:45:05 -0500 Subject: [PATCH 106/132] Add ITunerHostManager service --- .../ApplicationHost.cs | 2 +- Jellyfin.Api/Controllers/LiveTvController.cs | 19 +- .../LiveTv/ILiveTvManager.cs | 15 +- .../LiveTv/ITunerHostManager.cs | 47 +++++ src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs | 89 +-------- .../LiveTvServiceCollectionExtensions.cs | 6 + src/Jellyfin.LiveTv/LiveTvManager.cs | 76 +------- .../TunerHosts/TunerHostManager.cs | 181 ++++++++++++++++++ 8 files changed, 258 insertions(+), 177 deletions(-) create mode 100644 MediaBrowser.Controller/LiveTv/ITunerHostManager.cs create mode 100644 src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 48d5d8c6a9..5870fed761 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -695,7 +695,7 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - Resolve().AddParts(GetExports(), GetExports(), GetExports()); + Resolve().AddParts(GetExports(), GetExports()); Resolve().AddParts(GetExports()); } diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 5502836239..1a2a3caae8 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -10,7 +10,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; -using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; @@ -43,6 +42,7 @@ namespace Jellyfin.Api.Controllers; public class LiveTvController : BaseJellyfinApiController { private readonly ILiveTvManager _liveTvManager; + private readonly ITunerHostManager _tunerHostManager; private readonly IUserManager _userManager; private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; @@ -55,6 +55,7 @@ public class LiveTvController : BaseJellyfinApiController /// 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. @@ -64,6 +65,7 @@ public class LiveTvController : BaseJellyfinApiController /// Instance of the interface. public LiveTvController( ILiveTvManager liveTvManager, + ITunerHostManager tunerHostManager, IUserManager userManager, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, @@ -73,6 +75,7 @@ public class LiveTvController : BaseJellyfinApiController ITranscodeManager transcodeManager) { _liveTvManager = liveTvManager; + _tunerHostManager = tunerHostManager; _userManager = userManager; _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; @@ -951,9 +954,7 @@ public class LiveTvController : BaseJellyfinApiController [Authorize(Policy = Policies.LiveTvManagement)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo) - { - return await _liveTvManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false); - } + => await _tunerHostManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false); /// /// Deletes a tuner host. @@ -1130,10 +1131,8 @@ public class LiveTvController : BaseJellyfinApiController [HttpGet("TunerHosts/Types")] [Authorize(Policy = Policies.LiveTvAccess)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetTunerHostTypes() - { - return _liveTvManager.GetTunerHostTypes(); - } + public IEnumerable GetTunerHostTypes() + => _tunerHostManager.GetTunerHostTypes(); /// /// Discover tuners. @@ -1146,9 +1145,7 @@ public class LiveTvController : BaseJellyfinApiController [Authorize(Policy = Policies.LiveTvManagement)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> DiscoverTuners([FromQuery] bool newDevicesOnly = false) - { - return await _liveTvManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false); - } + => await _tunerHostManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false); /// /// Gets a live tv recording stream. diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 4206159e7b..26f9fe42d3 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -71,9 +71,8 @@ namespace MediaBrowser.Controller.LiveTv /// Adds the parts. /// /// The services. - /// The tuner hosts. /// The listing providers. - void AddParts(IEnumerable services, IEnumerable tunerHosts, IEnumerable listingProviders); + void AddParts(IEnumerable services, IEnumerable listingProviders); /// /// Gets the timer. @@ -253,14 +252,6 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList fields, User user = null); - /// - /// Saves the tuner host. - /// - /// Turner host to save. - /// Option to specify that data source has changed. - /// Tuner host information wrapped in a task. - Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true); - /// /// Saves the listing provider. /// @@ -298,10 +289,6 @@ namespace MediaBrowser.Controller.LiveTv Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); - List GetTunerHostTypes(); - - Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken); - string GetEmbyTvActiveRecordingPath(string id); ActiveRecordingInfo GetActiveRecordingInfo(string path); diff --git a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs new file mode 100644 index 0000000000..7e4caaf136 --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.LiveTv; + +namespace MediaBrowser.Controller.LiveTv; + +/// +/// Service responsible for managing the s. +/// +public interface ITunerHostManager +{ + /// + /// Gets the available s. + /// + IReadOnlyList TunerHosts { get; } + + /// + /// Gets the s for the available s. + /// + /// The s. + IEnumerable GetTunerHostTypes(); + + /// + /// Saves the tuner host. + /// + /// Turner host to save. + /// Option to specify that data source has changed. + /// Tuner host information wrapped in a task. + Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true); + + /// + /// Discovers the available tuners. + /// + /// A value indicating whether to only return new devices. + /// The to use. + /// The s. + Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken); + + /// + /// Scans for tuner devices that have changed URLs. + /// + /// The to use. + /// A task that represents the scanning operation. + Task ScanForTunerDeviceChanges(CancellationToken cancellationToken); +} diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index 532e1c897d..625451fa37 100644 --- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -44,8 +44,6 @@ namespace Jellyfin.LiveTv.EmbyTV { public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; - private const int TunerDiscoveryDurationMs = 3000; - private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _config; @@ -54,6 +52,7 @@ namespace Jellyfin.LiveTv.EmbyTV private readonly TimerManager _timerProvider; private readonly LiveTvManager _liveTvManager; + private readonly ITunerHostManager _tunerHostManager; private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _libraryMonitor; @@ -80,6 +79,7 @@ namespace Jellyfin.LiveTv.EmbyTV IHttpClientFactory httpClientFactory, IServerConfigurationManager config, ILiveTvManager liveTvManager, + ITunerHostManager tunerHostManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, @@ -97,6 +97,7 @@ namespace Jellyfin.LiveTv.EmbyTV _providerManager = providerManager; _mediaEncoder = mediaEncoder; _liveTvManager = (LiveTvManager)liveTvManager; + _tunerHostManager = tunerHostManager; _mediaSourceManager = mediaSourceManager; _streamHelper = streamHelper; @@ -310,7 +311,7 @@ namespace Jellyfin.LiveTv.EmbyTV { var list = new List(); - foreach (var hostInstance in _liveTvManager.TunerHosts) + foreach (var hostInstance in _tunerHostManager.TunerHosts) { try { @@ -510,7 +511,7 @@ namespace Jellyfin.LiveTv.EmbyTV { var list = new List(); - foreach (var hostInstance in _liveTvManager.TunerHosts) + foreach (var hostInstance in _tunerHostManager.TunerHosts) { try { @@ -966,7 +967,7 @@ namespace Jellyfin.LiveTv.EmbyTV return result; } - foreach (var hostInstance in _liveTvManager.TunerHosts) + foreach (var hostInstance in _tunerHostManager.TunerHosts) { try { @@ -998,7 +999,7 @@ namespace Jellyfin.LiveTv.EmbyTV throw new ArgumentNullException(nameof(channelId)); } - foreach (var hostInstance in _liveTvManager.TunerHosts) + foreach (var hostInstance in _tunerHostManager.TunerHosts) { try { @@ -2537,81 +2538,5 @@ namespace Jellyfin.LiveTv.EmbyTV }; } } - - public async Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) - { - var list = new List(); - - var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts - .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId)) - .Select(i => i.DeviceId) - .ToList(); - - foreach (var host in _liveTvManager.TunerHosts) - { - var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); - - if (newDevicesOnly) - { - discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase)) - .ToList(); - } - - list.AddRange(discoveredDevices); - } - - return list; - } - - public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken) - { - foreach (var host in _liveTvManager.TunerHosts) - { - await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false); - } - } - - private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken) - { - var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); - - var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts - .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - foreach (var device in discoveredDevices) - { - var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase)); - - if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase)) - { - _logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url); - - configuredDevice.Url = device.Url; - await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false); - } - } - } - - private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken) - { - try - { - var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false); - - foreach (var device in discoveredDevices) - { - _logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url); - } - - return discoveredDevices; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error discovering tuner devices"); - - return new List(); - } - } } } diff --git a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs index 5865e88af0..5490547ec3 100644 --- a/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs +++ b/src/Jellyfin.LiveTv/Extensions/LiveTvServiceCollectionExtensions.cs @@ -1,4 +1,6 @@ using Jellyfin.LiveTv.Channels; +using Jellyfin.LiveTv.TunerHosts; +using Jellyfin.LiveTv.TunerHosts.HdHomerun; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.IO; @@ -21,5 +23,9 @@ public static class LiveTvServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); } } diff --git a/src/Jellyfin.LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs index 0b3d35731a..71822f3762 100644 --- a/src/Jellyfin.LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -57,9 +57,9 @@ namespace Jellyfin.LiveTv private readonly IFileSystem _fileSystem; private readonly IChannelManager _channelManager; private readonly LiveTvDtoService _tvDtoService; + private readonly ITunerHostManager _tunerHostManager; private ILiveTvService[] _services = Array.Empty(); - private ITunerHost[] _tunerHosts = Array.Empty(); private IListingsProvider[] _listingProviders = Array.Empty(); public LiveTvManager( @@ -74,7 +74,8 @@ namespace Jellyfin.LiveTv ILocalizationManager localization, IFileSystem fileSystem, IChannelManager channelManager, - LiveTvDtoService liveTvDtoService) + LiveTvDtoService liveTvDtoService, + ITunerHostManager tunerHostManager) { _config = config; _logger = logger; @@ -88,6 +89,7 @@ namespace Jellyfin.LiveTv _userDataManager = userDataManager; _channelManager = channelManager; _tvDtoService = liveTvDtoService; + _tunerHostManager = tunerHostManager; } public event EventHandler> SeriesTimerCancelled; @@ -104,8 +106,6 @@ namespace Jellyfin.LiveTv /// The services. public IReadOnlyList Services => _services; - public IReadOnlyList TunerHosts => _tunerHosts; - public IReadOnlyList ListingProviders => _listingProviders; public string GetEmbyTvActiveRecordingPath(string id) @@ -113,16 +113,10 @@ namespace Jellyfin.LiveTv return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id); } - /// - /// Adds the parts. - /// - /// The services. - /// The tuner hosts. - /// The listing providers. - public void AddParts(IEnumerable services, IEnumerable tunerHosts, IEnumerable listingProviders) + /// + public void AddParts(IEnumerable services, IEnumerable listingProviders) { _services = services.ToArray(); - _tunerHosts = tunerHosts.Where(i => i.IsSupported).ToArray(); _listingProviders = listingProviders.ToArray(); @@ -154,20 +148,6 @@ namespace Jellyfin.LiveTv })); } - public List GetTunerHostTypes() - { - return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair - { - Name = i.Name, - Id = i.Type - }).ToList(); - } - - public Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) - { - return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken); - } - public QueryResult GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken) { var user = query.UserId.Equals(default) @@ -1029,7 +1009,7 @@ namespace Jellyfin.LiveTv { await EmbyTV.EmbyTV.Current.CreateRecordingFolders().ConfigureAwait(false); - await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false); + await _tunerHostManager.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false); var numComplete = 0; double progressPerService = _services.Length == 0 @@ -2166,48 +2146,6 @@ namespace Jellyfin.LiveTv return _libraryManager.GetNamedView(name, CollectionType.livetv, name); } - public async Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true) - { - info = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(info)); - - var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase)); - - if (provider is null) - { - throw new ResourceNotFoundException(); - } - - if (provider is IConfigurableTunerHost configurable) - { - await configurable.Validate(info).ConfigureAwait(false); - } - - var config = _config.GetLiveTvConfiguration(); - - var list = config.TunerHosts.ToList(); - var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); - - if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) - { - info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - list.Add(info); - config.TunerHosts = list.ToArray(); - } - else - { - config.TunerHosts[index] = info; - } - - _config.SaveConfiguration("livetv", config); - - if (dataSourceChanged) - { - _taskManager.CancelIfRunningAndQueue(); - } - - return info; - } - public async Task SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings) { // Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider diff --git a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs new file mode 100644 index 0000000000..04eb8293a1 --- /dev/null +++ b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Extensions; +using Jellyfin.LiveTv.Configuration; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.LiveTv.TunerHosts; + +/// +public class TunerHostManager : ITunerHostManager +{ + private const int TunerDiscoveryDurationMs = 3000; + + private readonly ILogger _logger; + private readonly IConfigurationManager _config; + private readonly ITaskManager _taskManager; + private readonly ITunerHost[] _tunerHosts; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + /// The . + public TunerHostManager( + ILogger logger, + IConfigurationManager config, + ITaskManager taskManager, + IEnumerable tunerHosts) + { + _logger = logger; + _config = config; + _taskManager = taskManager; + _tunerHosts = tunerHosts.Where(t => t.IsSupported).ToArray(); + } + + /// + public IReadOnlyList TunerHosts => _tunerHosts; + + /// + public IEnumerable GetTunerHostTypes() + => _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair + { + Name = i.Name, + Id = i.Type + }); + + /// + public async Task SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true) + { + info = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(info))!; + + var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase)); + + if (provider is null) + { + throw new ResourceNotFoundException(); + } + + if (provider is IConfigurableTunerHost configurable) + { + await configurable.Validate(info).ConfigureAwait(false); + } + + var config = _config.GetLiveTvConfiguration(); + + var list = config.TunerHosts.ToList(); + var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase)); + + if (index == -1 || string.IsNullOrWhiteSpace(info.Id)) + { + info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + list.Add(info); + config.TunerHosts = list.ToArray(); + } + else + { + config.TunerHosts[index] = info; + } + + _config.SaveConfiguration("livetv", config); + + if (dataSourceChanged) + { + _taskManager.CancelIfRunningAndQueue(); + } + + return info; + } + + /// + public async Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) + { + var list = new List(); + + var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts + .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId)) + .Select(i => i.DeviceId) + .ToList(); + + foreach (var host in _tunerHosts) + { + var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); + + if (newDevicesOnly) + { + discoveredDevices = discoveredDevices + .Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase)) + .ToList(); + } + + list.AddRange(discoveredDevices); + } + + return list; + } + + /// + public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken) + { + foreach (var host in _tunerHosts) + { + await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false); + } + } + + private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken) + { + var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); + + var configuredDevices = _config.GetLiveTvConfiguration().TunerHosts + .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var device in discoveredDevices) + { + var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase)); + + if (configuredDevice is not null && !string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogInformation("Tuner url has changed from {PreviousUrl} to {NewUrl}", configuredDevice.Url, device.Url); + + configuredDevice.Url = device.Url; + await SaveTunerHost(configuredDevice).ConfigureAwait(false); + } + } + } + + private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken) + { + try + { + var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false); + + foreach (var device in discoveredDevices) + { + _logger.LogInformation("Discovered tuner device {0} at {1}", host.Name, device.Url); + } + + return discoveredDevices; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error discovering tuner devices"); + + return new List(); + } + } +} From d40224128c031e38054090e063247b9a06e46f6d Mon Sep 17 00:00:00 2001 From: SuperDumbTM Date: Fri, 12 Jan 2024 08:37:06 +0000 Subject: [PATCH 107/132] Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- .../Localization/Core/zh-HK.json | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index e8b8c2c5fa..66cbee3948 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -1,15 +1,15 @@ { "Albums": "專輯", - "AppDeviceValues": "程式: {0}, 設備: {1}", + "AppDeviceValues": "程式:{0},設備:{1}", "Application": "應用程式", "Artists": "藝人", - "AuthenticationSucceededWithUserName": "{0} 授權成功", + "AuthenticationSucceededWithUserName": "成功授權 {0}", "Books": "書籍", "CameraImageUploadedFrom": "{0} 成功上傳一張新照片", "Channels": "頻道", "ChapterNameValue": "第 {0} 章", "Collections": "系列", - "DeviceOfflineWithName": "{0} 已斷開連接", + "DeviceOfflineWithName": "{0} 已中斷連接", "DeviceOnlineWithName": "{0} 已連接", "FailedLoginAttemptWithUserName": "{0} 登入失敗", "Favorites": "我的最愛", @@ -27,15 +27,15 @@ "HeaderRecordingGroups": "錄製組", "HomeVideos": "家庭影片", "Inherit": "繼承", - "ItemAddedWithName": "{0} 已被添加至媒體庫", + "ItemAddedWithName": "{0} 已被加入至媒體庫", "ItemRemovedWithName": "{0} 已從媒體庫移除", - "LabelIpAddressValue": "IP 地址: {0}", - "LabelRunningTimeValue": "運行時間: {0}", + "LabelIpAddressValue": "IP 地址:{0}", + "LabelRunningTimeValue": "運作時間:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin 已被更新", "MessageApplicationUpdatedTo": "Jellyfin 已被更新至 {0}", "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已被更新", - "MessageServerConfigurationUpdated": "伺服器設定已經被更新", + "MessageServerConfigurationUpdated": "已更新伺服器設定", "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", @@ -43,23 +43,23 @@ "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知的季度", - "NewVersionIsAvailable": "有較新版本的 Jellyfin 可供下載。", + "NewVersionIsAvailable": "有新版本的 Jellyfin 可供下載。", "NotificationOptionApplicationUpdateAvailable": "有可用的更新", - "NotificationOptionApplicationUpdateInstalled": "應用程式已被更新", - "NotificationOptionAudioPlayback": "開始播放音訊", + "NotificationOptionApplicationUpdateInstalled": "完成更新應用程式", + "NotificationOptionAudioPlayback": "播放音訊", "NotificationOptionAudioPlaybackStopped": "停止播放音訊", - "NotificationOptionCameraImageUploaded": "相片已被上傳", + "NotificationOptionCameraImageUploaded": "相片上傳", "NotificationOptionInstallationFailed": "安裝失敗", - "NotificationOptionNewLibraryContent": "已添加新内容", - "NotificationOptionPluginError": "插件出現錯誤", - "NotificationOptionPluginInstalled": "插件已被安裝", - "NotificationOptionPluginUninstalled": "插件已被移除", - "NotificationOptionPluginUpdateInstalled": "插件已被更新", + "NotificationOptionNewLibraryContent": "新增媒體", + "NotificationOptionPluginError": "插件錯誤", + "NotificationOptionPluginInstalled": "安裝插件", + "NotificationOptionPluginUninstalled": "解除安裝插件", + "NotificationOptionPluginUpdateInstalled": "完成更新插件", "NotificationOptionServerRestartRequired": "伺服器需要重啟", - "NotificationOptionTaskFailed": "排程任務執行失敗", - "NotificationOptionUserLockedOut": "用戶已被鎖定", - "NotificationOptionVideoPlayback": "開始播放影片", - "NotificationOptionVideoPlaybackStopped": "已停止播放影片", + "NotificationOptionTaskFailed": "排程工作執行失敗", + "NotificationOptionUserLockedOut": "封鎖用戶", + "NotificationOptionVideoPlayback": "播放影片", + "NotificationOptionVideoPlaybackStopped": "停止播放影片", "Photos": "相片", "Playlists": "播放清單", "Plugin": "插件", @@ -68,7 +68,7 @@ "PluginUpdatedWithName": "已更新 {0}", "ProviderValue": "提供者:{0}", "ScheduledTaskFailedWithName": "{0} 執行失敗", - "ScheduledTaskStartedWithName": "{0} 開始執行", + "ScheduledTaskStartedWithName": "開始執行 {0}", "ServerNameNeedsToBeRestarted": "{0} 需要重啟", "Shows": "節目", "Songs": "歌曲", @@ -79,50 +79,52 @@ "System": "系統", "TvShows": "電視節目", "User": "用戶", - "UserCreatedWithName": "用戶 {0} 已被建立", + "UserCreatedWithName": "建立新用戶 {0}", "UserDeletedWithName": "用戶 {0} 已被移除", "UserDownloadingItemWithValues": "{0} 正在下載 {1}", - "UserLockedOutWithName": "使用者 {0} 已被鎖定", + "UserLockedOutWithName": "用戶 {0} 已被封鎖", "UserOfflineFromDevice": "{0} 從 {1} 斷開連接", "UserOnlineFromDevice": "{0} 從 {1} 連線", "UserPasswordChangedWithName": "{0} 的密碼已被變改", - "UserPolicyUpdatedWithName": "使用者協議已更新為 {0}", - "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}", + "UserPolicyUpdatedWithName": "使用條款已更新為 {0}", + "UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}", "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}", "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", "VersionNumber": "版本 {0}", - "TaskDownloadMissingSubtitles": "下載缺少的字幕", + "TaskDownloadMissingSubtitles": "下載欠缺字幕", "TaskUpdatePlugins": "更新插件", "TasksApplicationCategory": "應用程式", - "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增檔案及重新載入 metadata。", + "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增的檔案及重新載入元數據。", "TasksMaintenanceCategory": "維護", - "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在互聯網上搜索缺少的字幕。", + "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在網上搜尋欠缺的字幕。", "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。", "TaskRefreshChannels": "重新載入頻道", - "TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。", - "TaskCleanTranscode": "清理轉碼目錄", + "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。", + "TaskCleanTranscode": "清理轉碼檔資料夾", "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。", - "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。", + "TaskRefreshPeopleDescription": "更新你的媒體中有關的演員和導演的元數據。", "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔。", - "TaskCleanLogs": "清理紀錄檔目錄", + "TaskCleanLogs": "清理紀錄檔資料夾", "TaskRefreshLibrary": "掃描媒體庫", "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。", "TaskRefreshChapterImages": "提取章節圖像", "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。", - "TaskCleanCache": "清理緩存目錄", + "TaskCleanCache": "清理緩存資料夾", "TasksChannelsCategory": "網絡頻道", - "TasksLibraryCategory": "庫", + "TasksLibraryCategory": "媒體庫", "TaskRefreshPeople": "重新載入人物", "TaskCleanActivityLog": "清理活動記錄", "Undefined": "未定義", "Forced": "強制", "Default": "預設", - "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", + "TaskOptimizeDatabaseDescription": "壓縮數據庫及釋放可用空間。完成任何會修改數據庫的工作(例如掃描媒體庫)後,執行此工作或可提升伺服器速度。", "TaskOptimizeDatabase": "最佳化數據庫", "TaskCleanActivityLogDescription": "刪除早於設定時間的活動記錄。", - "TaskKeyframeExtractorDescription": "提取關鍵幀以建立更準確的 HLS 播放列表。此工作或需要使用較長時間來完成。", - "TaskKeyframeExtractor": "關鍵幀提取器", + "TaskKeyframeExtractorDescription": "提取關鍵影格(Keyframe)以建立更準確的 HLS playlist。此工作可能需要使用較長時間來完成。", + "TaskKeyframeExtractor": "關鍵影格提取器", "External": "外部", - "HearingImpaired": "聽力障礙" + "HearingImpaired": "聽力障礙", + "TaskRefreshTrickplayImages": "建立 Trickplay 圖像", + "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。" } From 3ce16713dd013b5aabdedebafd025f2224d2475f Mon Sep 17 00:00:00 2001 From: JPVenson Date: Sun, 14 Jan 2024 16:50:09 +0100 Subject: [PATCH 108/132] Fixed disposable not being called (#10613) * Fixed disposable not being called * PulledUp usage of IAsyncDisposable for sessioninfo Co-authored-by: Patrick Barron --- .../Session/SessionManager.cs | 12 ++++---- .../Session/ISessionManager.cs | 3 +- .../Session/SessionInfo.cs | 30 +++++-------------- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6f599e4c7c..f457a78b3b 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -189,7 +189,7 @@ namespace Emby.Server.Implementations.Session _logger); } - private void OnSessionEnded(SessionInfo info) + private async ValueTask OnSessionEnded(SessionInfo info) { EventHelper.QueueEventIfNotNull( SessionEnded, @@ -202,7 +202,7 @@ namespace Emby.Server.Implementations.Session _eventManager.Publish(new SessionEndedEventArgs(info)); - info.Dispose(); + await info.DisposeAsync().ConfigureAwait(false); } /// @@ -301,12 +301,12 @@ namespace Emby.Server.Implementations.Session await _mediaSourceManager.CloseLiveStream(session.PlayState.LiveStreamId).ConfigureAwait(false); } - OnSessionEnded(session); + await OnSessionEnded(session).ConfigureAwait(false); } } /// - public void ReportSessionEnded(string sessionId) + public async ValueTask ReportSessionEnded(string sessionId) { CheckDisposed(); var session = GetSession(sessionId, false); @@ -317,7 +317,7 @@ namespace Emby.Server.Implementations.Session _activeConnections.TryRemove(key, out _); - OnSessionEnded(session); + await OnSessionEnded(session).ConfigureAwait(false); } } @@ -1590,7 +1590,7 @@ namespace Emby.Server.Implementations.Session { try { - ReportSessionEnded(session.Id); + await ReportSessionEnded(session.Id).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 53df7133b5..5a47236f92 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -111,7 +111,8 @@ namespace MediaBrowser.Controller.Session /// Reports the session ended. /// /// The session identifier. - void ReportSessionEnded(string sessionId); + /// Task. + ValueTask ReportSessionEnded(string sessionId); /// /// Sends the general command. diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 3e30c8dc45..3a12a56f1e 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Controller.Session /// /// Class SessionInfo. /// - public sealed class SessionInfo : IAsyncDisposable, IDisposable + public sealed class SessionInfo : IAsyncDisposable { // 1 second private const long ProgressIncrement = 10000000; @@ -374,8 +374,7 @@ namespace MediaBrowser.Controller.Session } } - /// - public void Dispose() + public async ValueTask DisposeAsync() { _disposed = true; @@ -386,30 +385,17 @@ namespace MediaBrowser.Controller.Session foreach (var controller in controllers) { - if (controller is IDisposable disposable) + if (controller is IAsyncDisposable disposableAsync) + { + _logger.LogDebug("Disposing session controller asynchronously {TypeName}", disposableAsync.GetType().Name); + await disposableAsync.DisposeAsync().ConfigureAwait(false); + } + else if (controller is IDisposable disposable) { _logger.LogDebug("Disposing session controller synchronously {TypeName}", disposable.GetType().Name); disposable.Dispose(); } } } - - public async ValueTask DisposeAsync() - { - _disposed = true; - - StopAutomaticProgress(); - - var controllers = SessionControllers.ToList(); - - foreach (var controller in controllers) - { - if (controller is IAsyncDisposable disposableAsync) - { - _logger.LogDebug("Disposing session controller asynchronously {TypeName}", disposableAsync.GetType().Name); - await disposableAsync.DisposeAsync().ConfigureAwait(false); - } - } - } } } From c23a038ba8f275e061c148ea27e458174a9a7cbe Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 20:56:58 -0500 Subject: [PATCH 109/132] Remove unnecessary allocations in TunerHostManager --- Jellyfin.Api/Controllers/LiveTvController.cs | 4 +-- .../LiveTv/ITunerHostManager.cs | 3 +-- .../TunerHosts/TunerHostManager.cs | 25 +++++++------------ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 1a2a3caae8..27eb88b60f 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1144,8 +1144,8 @@ public class LiveTvController : BaseJellyfinApiController [HttpGet("Tuners/Discover")] [Authorize(Policy = Policies.LiveTvManagement)] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> DiscoverTuners([FromQuery] bool newDevicesOnly = false) - => await _tunerHostManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false); + public IAsyncEnumerable DiscoverTuners([FromQuery] bool newDevicesOnly = false) + => _tunerHostManager.DiscoverTuners(newDevicesOnly); /// /// Gets a live tv recording stream. diff --git a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs index 7e4caaf136..3df6066f66 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHostManager.cs @@ -34,9 +34,8 @@ public interface ITunerHostManager /// Discovers the available tuners. /// /// A value indicating whether to only return new devices. - /// The to use. /// The s. - Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken); + IAsyncEnumerable DiscoverTuners(bool newDevicesOnly); /// /// Scans for tuner devices that have changed URLs. diff --git a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs index 04eb8293a1..3e4b0e13fc 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/TunerHostManager.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Extensions; using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -101,10 +100,8 @@ public class TunerHostManager : ITunerHostManager } /// - public async Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) + public async IAsyncEnumerable DiscoverTuners(bool newDevicesOnly) { - var list = new List(); - var configuredDeviceIds = _config.GetLiveTvConfiguration().TunerHosts .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId)) .Select(i => i.DeviceId) @@ -112,19 +109,15 @@ public class TunerHostManager : ITunerHostManager foreach (var host in _tunerHosts) { - var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false); - - if (newDevicesOnly) + var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, CancellationToken.None).ConfigureAwait(false); + foreach (var tuner in discoveredDevices) { - discoveredDevices = discoveredDevices - .Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparison.OrdinalIgnoreCase)) - .ToList(); + if (!newDevicesOnly || !configuredDeviceIds.Contains(tuner.DeviceId, StringComparer.OrdinalIgnoreCase)) + { + yield return tuner; + } } - - list.AddRange(discoveredDevices); } - - return list; } /// @@ -158,7 +151,7 @@ public class TunerHostManager : ITunerHostManager } } - private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken) + private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken) { try { @@ -175,7 +168,7 @@ public class TunerHostManager : ITunerHostManager { _logger.LogError(ex, "Error discovering tuner devices"); - return new List(); + return Array.Empty(); } } } From c101d287f24cf53bce0674bf70d88ae61da67ed9 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jan 2024 21:16:08 -0500 Subject: [PATCH 110/132] Remove unused Live TV code --- .../Channels/IChannelManager.cs | 7 - .../LiveTv/ILiveTvService.cs | 13 -- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 7 - .../LiveTv/LiveTvServiceStatusInfo.cs | 54 ----- .../LiveTv/LiveTvTunerInfo.cs | 77 ------- .../LiveTv/RecordingInfo.cs | 210 ------------------ .../LiveTv/RecordingStatusChangedEventArgs.cs | 16 -- MediaBrowser.Model/IO/IStreamHelper.cs | 2 - .../LiveTv/LiveTvTunerStatus.cs | 12 - .../Channels/ChannelManager.cs | 34 --- src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs | 5 - src/Jellyfin.LiveTv/StreamHelper.cs | 30 --- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 186 +--------------- .../TunerHosts/M3UTunerHost.cs | 16 -- 14 files changed, 11 insertions(+), 658 deletions(-) delete mode 100644 MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs delete mode 100644 MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs delete mode 100644 MediaBrowser.Controller/LiveTv/RecordingInfo.cs delete mode 100644 MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs delete mode 100644 MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 8eb27888ab..c8b432ecb2 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -95,12 +95,5 @@ namespace MediaBrowser.Controller.Channels /// The cancellation token. /// The item media sources. IEnumerable GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken); - - /// - /// Whether the item supports media probe. - /// - /// The item. - /// Whether media probe should be enabled. - bool EnableMediaProbe(BaseItem item); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index ce34954e3f..52fb156481 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -140,14 +140,6 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task CloseLiveStream(string id, CancellationToken cancellationToken); - /// - /// Records the live stream. - /// - /// The identifier. - /// The cancellation token. - /// Task. - Task RecordLiveStream(string id, CancellationToken cancellationToken); - /// /// Resets the tuner. /// @@ -180,9 +172,4 @@ namespace MediaBrowser.Controller.LiveTv { Task GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List currentLiveStreams, CancellationToken cancellationToken); } - - public interface ISupportsUpdatingDefaults - { - Task UpdateTimerDefaults(SeriesTimerInfo info, CancellationToken cancellationToken); - } } diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index b983091588..3689a2adf6 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -35,13 +35,6 @@ namespace MediaBrowser.Controller.LiveTv /// Task<IEnumerable<ChannelInfo>>. Task> GetChannels(bool enableCache, CancellationToken cancellationToken); - /// - /// Gets the tuner infos. - /// - /// The cancellation token. - /// Task<List<LiveTvTunerInfo>>. - Task> GetTunerInfos(CancellationToken cancellationToken); - /// /// Gets the channel stream. /// diff --git a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs deleted file mode 100644 index eb3babc180..0000000000 --- a/MediaBrowser.Controller/LiveTv/LiveTvServiceStatusInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System.Collections.Generic; -using MediaBrowser.Model.LiveTv; - -namespace MediaBrowser.Controller.LiveTv -{ - public class LiveTvServiceStatusInfo - { - public LiveTvServiceStatusInfo() - { - Tuners = new List(); - IsVisible = true; - } - - /// - /// Gets or sets the status. - /// - /// The status. - public LiveTvServiceStatus Status { get; set; } - - /// - /// Gets or sets the status message. - /// - /// The status message. - public string StatusMessage { get; set; } - - /// - /// Gets or sets the version. - /// - /// The version. - public string Version { get; set; } - - /// - /// Gets or sets a value indicating whether this instance has update available. - /// - /// true if this instance has update available; otherwise, false. - public bool HasUpdateAvailable { get; set; } - - /// - /// Gets or sets the tuners. - /// - /// The tuners. - public List Tuners { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is visible. - /// - /// true if this instance is visible; otherwise, false. - public bool IsVisible { get; set; } - } -} diff --git a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs b/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs deleted file mode 100644 index aa5eb59d16..0000000000 --- a/MediaBrowser.Controller/LiveTv/LiveTvTunerInfo.cs +++ /dev/null @@ -1,77 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System.Collections.Generic; -using MediaBrowser.Model.LiveTv; - -namespace MediaBrowser.Controller.LiveTv -{ - public class LiveTvTunerInfo - { - public LiveTvTunerInfo() - { - Clients = new List(); - } - - /// - /// Gets or sets the type of the source. - /// - /// The type of the source. - public string SourceType { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the identifier. - /// - /// The identifier. - public string Id { get; set; } - - /// - /// Gets or sets the URL. - /// - /// The URL. - public string Url { get; set; } - - /// - /// Gets or sets the status. - /// - /// The status. - public LiveTvTunerStatus Status { get; set; } - - /// - /// Gets or sets the channel identifier. - /// - /// The channel identifier. - public string ChannelId { get; set; } - - /// - /// Gets or sets the recording identifier. - /// - /// The recording identifier. - public string RecordingId { get; set; } - - /// - /// Gets or sets the name of the program. - /// - /// The name of the program. - public string ProgramName { get; set; } - - /// - /// Gets or sets the clients. - /// - /// The clients. - public List Clients { get; set; } - - /// - /// Gets or sets a value indicating whether this instance can reset. - /// - /// true if this instance can reset; otherwise, false. - public bool CanReset { get; set; } - } -} diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs deleted file mode 100644 index 1dcf7a58fe..0000000000 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ /dev/null @@ -1,210 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using MediaBrowser.Model.LiveTv; - -namespace MediaBrowser.Controller.LiveTv -{ - public class RecordingInfo - { - public RecordingInfo() - { - Genres = new List(); - } - - /// - /// Gets or sets the id of the recording. - /// - public string Id { get; set; } - - /// - /// Gets or sets the series timer identifier. - /// - /// The series timer identifier. - public string SeriesTimerId { get; set; } - - /// - /// Gets or sets the timer identifier. - /// - /// The timer identifier. - public string TimerId { get; set; } - - /// - /// Gets or sets the channelId of the recording. - /// - public string ChannelId { get; set; } - - /// - /// Gets or sets the type of the channel. - /// - /// The type of the channel. - public ChannelType ChannelType { get; set; } - - /// - /// Gets or sets the name of the recording. - /// - public string Name { get; set; } - - /// - /// Gets or sets the path. - /// - /// The path. - public string Path { get; set; } - - /// - /// Gets or sets the URL. - /// - /// The URL. - public string Url { get; set; } - - /// - /// Gets or sets the overview. - /// - /// The overview. - public string Overview { get; set; } - - /// - /// Gets or sets the start date of the recording, in UTC. - /// - public DateTime StartDate { get; set; } - - /// - /// Gets or sets the end date of the recording, in UTC. - /// - public DateTime EndDate { get; set; } - - /// - /// Gets or sets the program identifier. - /// - /// The program identifier. - public string ProgramId { get; set; } - - /// - /// Gets or sets the status. - /// - /// The status. - public RecordingStatus Status { get; set; } - - /// - /// Gets or sets the genre of the program. - /// - public List Genres { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is repeat. - /// - /// true if this instance is repeat; otherwise, false. - public bool IsRepeat { get; set; } - - /// - /// Gets or sets the episode title. - /// - /// The episode title. - public string EpisodeTitle { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is hd. - /// - /// true if this instance is hd; otherwise, false. - public bool? IsHD { get; set; } - - /// - /// Gets or sets the audio. - /// - /// The audio. - public ProgramAudio? Audio { get; set; } - - /// - /// Gets or sets the original air date. - /// - /// The original air date. - public DateTime? OriginalAirDate { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is movie. - /// - /// true if this instance is movie; otherwise, false. - public bool IsMovie { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is sports. - /// - /// true if this instance is sports; otherwise, false. - public bool IsSports { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is series. - /// - /// true if this instance is series; otherwise, false. - public bool IsSeries { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is live. - /// - /// true if this instance is live; otherwise, false. - public bool IsLive { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is news. - /// - /// true if this instance is news; otherwise, false. - public bool IsNews { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is kids. - /// - /// true if this instance is kids; otherwise, false. - public bool IsKids { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is premiere. - /// - /// true if this instance is premiere; otherwise, false. - public bool IsPremiere { get; set; } - - /// - /// Gets or sets the official rating. - /// - /// The official rating. - public string OfficialRating { get; set; } - - /// - /// Gets or sets the community rating. - /// - /// The community rating. - public float? CommunityRating { get; set; } - - /// - /// Gets or sets the image path if it can be accessed directly from the file system. - /// - /// The image path. - public string ImagePath { get; set; } - - /// - /// Gets or sets the image url if it can be downloaded. - /// - /// The image URL. - public string ImageUrl { get; set; } - - /// - /// Gets or sets a value indicating whether this instance has image. - /// - /// null if [has image] contains no value, true if [has image]; otherwise, false. - public bool? HasImage { get; set; } - - /// - /// Gets or sets the show identifier. - /// - /// The show identifier. - public string ShowId { get; set; } - - /// - /// Gets or sets the date last updated. - /// - /// The date last updated. - public DateTime DateLastUpdated { get; set; } - } -} diff --git a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs b/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs deleted file mode 100644 index 0b943c9396..0000000000 --- a/MediaBrowser.Controller/LiveTv/RecordingStatusChangedEventArgs.cs +++ /dev/null @@ -1,16 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using MediaBrowser.Model.LiveTv; - -namespace MediaBrowser.Controller.LiveTv -{ - public class RecordingStatusChangedEventArgs : EventArgs - { - public string RecordingId { get; set; } - - public RecordingStatus NewStatus { get; set; } - } -} diff --git a/MediaBrowser.Model/IO/IStreamHelper.cs b/MediaBrowser.Model/IO/IStreamHelper.cs index f900da5567..034a6bf8bf 100644 --- a/MediaBrowser.Model/IO/IStreamHelper.cs +++ b/MediaBrowser.Model/IO/IStreamHelper.cs @@ -13,8 +13,6 @@ namespace MediaBrowser.Model.IO Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken); - Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken); - Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs deleted file mode 100644 index 80a6461957..0000000000 --- a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs +++ /dev/null @@ -1,12 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.LiveTv -{ - public enum LiveTvTunerStatus - { - Available = 0, - Disabled = 1, - RecordingTv = 2, - LiveTv = 3 - } -} diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs index f5ce75ff4d..51abb503eb 100644 --- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -113,15 +113,6 @@ namespace Jellyfin.LiveTv.Channels return channel is ISupportsDelete supportsDelete && supportsDelete.CanDelete(item); } - /// - public bool EnableMediaProbe(BaseItem item) - { - var internalChannel = _libraryManager.GetItemById(item.ChannelId); - var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); - - return channel is ISupportsMediaProbe; - } - /// public Task DeleteItem(BaseItem item) { @@ -562,18 +553,6 @@ namespace Jellyfin.LiveTv.Channels return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures()); } - /// - /// Checks whether the provided Guid supports external transfer. - /// - /// The Guid. - /// Whether or not the provided Guid supports external transfer. - public bool SupportsExternalTransfer(Guid channelId) - { - var channelProvider = GetChannelProvider(channelId); - - return channelProvider.GetChannelFeatures().SupportsContentDownloading; - } - /// /// Gets the provided channel's supported features. /// @@ -1215,19 +1194,6 @@ namespace Jellyfin.LiveTv.Channels return result; } - internal IChannel GetChannelProvider(Guid internalChannelId) - { - var result = GetAllChannels() - .FirstOrDefault(i => internalChannelId.Equals(GetInternalChannelId(i.Name))); - - if (result is null) - { - throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId); - } - - return result; - } - /// public void Dispose() { diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index 625451fa37..9eb3aa2fd6 100644 --- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -1023,11 +1023,6 @@ namespace Jellyfin.LiveTv.EmbyTV return Task.CompletedTask; } - public Task RecordLiveStream(string id, CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - public Task ResetTuner(string id, CancellationToken cancellationToken) { return Task.CompletedTask; diff --git a/src/Jellyfin.LiveTv/StreamHelper.cs b/src/Jellyfin.LiveTv/StreamHelper.cs index ab4b6e9b15..e9644e95e7 100644 --- a/src/Jellyfin.LiveTv/StreamHelper.cs +++ b/src/Jellyfin.LiveTv/StreamHelper.cs @@ -81,36 +81,6 @@ namespace Jellyfin.LiveTv } } - public async Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) - { - byte[] buffer = ArrayPool.Shared.Rent(IODefaults.CopyToBufferSize); - try - { - int bytesRead; - - while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) - { - var bytesToWrite = Math.Min(bytesRead, copyLength); - - if (bytesToWrite > 0) - { - await destination.WriteAsync(buffer.AsMemory(0, Convert.ToInt32(bytesToWrite)), cancellationToken).ConfigureAwait(false); - } - - copyLength -= bytesToWrite; - - if (copyLength <= 0) - { - break; - } - } - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - public async Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken) { byte[] buffer = ArrayPool.Shared.Rent(bufferSize); diff --git a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index a56af65a6f..fef84dd000 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -16,7 +15,6 @@ using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; -using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -164,152 +162,6 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun } } - private async Task> GetTunerInfosHttp(TunerHostInfo info, CancellationToken cancellationToken) - { - var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - - using var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .GetAsync(string.Format(CultureInfo.InvariantCulture, "{0}/tuners.html", GetApiUrl(info)), HttpCompletionOption.ResponseHeadersRead, cancellationToken) - .ConfigureAwait(false); - var tuners = new List(); - var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using (stream.ConfigureAwait(false)) - { - using var sr = new StreamReader(stream, System.Text.Encoding.UTF8); - await foreach (var line in sr.ReadAllLinesAsync().ConfigureAwait(false)) - { - string stripedLine = StripXML(line); - if (stripedLine.Contains("Channel", StringComparison.Ordinal)) - { - LiveTvTunerStatus status; - var index = stripedLine.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); - var name = stripedLine.Substring(0, index - 1); - var currentChannel = stripedLine.Substring(index + 7); - if (string.Equals(currentChannel, "none", StringComparison.Ordinal)) - { - status = LiveTvTunerStatus.LiveTv; - } - else - { - status = LiveTvTunerStatus.Available; - } - - tuners.Add(new LiveTvTunerInfo - { - Name = name, - SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber, - ProgramName = currentChannel, - Status = status - }); - } - } - } - - return tuners; - } - - private static string StripXML(string source) - { - if (string.IsNullOrEmpty(source)) - { - return string.Empty; - } - - char[] buffer = new char[source.Length]; - int bufferIndex = 0; - bool inside = false; - - for (int i = 0; i < source.Length; i++) - { - char let = source[i]; - if (let == '<') - { - inside = true; - continue; - } - - if (let == '>') - { - inside = false; - continue; - } - - if (!inside) - { - buffer[bufferIndex++] = let; - } - } - - return new string(buffer, 0, bufferIndex); - } - - private async Task> GetTunerInfosUdp(TunerHostInfo info, CancellationToken cancellationToken) - { - var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - - var tuners = new List(model.TunerCount); - - var uri = new Uri(GetApiUrl(info)); - - using (var manager = new HdHomerunManager()) - { - // Legacy HdHomeruns are IPv4 only - var ipInfo = IPAddress.Parse(uri.Host); - - for (int i = 0; i < model.TunerCount; i++) - { - var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1); - var currentChannel = "none"; // TODO: Get current channel and map back to Station Id - var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false); - var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv; - tuners.Add(new LiveTvTunerInfo - { - Name = name, - SourceType = string.IsNullOrWhiteSpace(model.ModelNumber) ? Name : model.ModelNumber, - ProgramName = currentChannel, - Status = status - }); - } - } - - return tuners; - } - - public async Task> GetTunerInfos(CancellationToken cancellationToken) - { - var list = new List(); - - foreach (var host in Config.GetLiveTvConfiguration().TunerHosts - .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))) - { - try - { - list.AddRange(await GetTunerInfos(host, cancellationToken).ConfigureAwait(false)); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error getting tuner info"); - } - } - - return list; - } - - public async Task> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken) - { - // TODO Need faster way to determine UDP vs HTTP - var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false); - - var hdHomerunChannelInfo = channels.FirstOrDefault() as HdHomerunChannelInfo; - - if (hdHomerunChannelInfo is null || hdHomerunChannelInfo.IsLegacyTuner) - { - return await GetTunerInfosUdp(info, cancellationToken).ConfigureAwait(false); - } - - return await GetTunerInfosHttp(info, cancellationToken).ConfigureAwait(false); - } - private static string GetApiUrl(TunerHostInfo info) { var url = info.Url; @@ -575,40 +427,24 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun _streamHelper); } - var enableHttpStream = true; - if (enableHttpStream) + mediaSource.Protocol = MediaProtocol.Http; + + var httpUrl = channel.Path; + + // If raw was used, the tuner doesn't support params + if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { - mediaSource.Protocol = MediaProtocol.Http; - - var httpUrl = channel.Path; - - // If raw was used, the tuner doesn't support params - if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) - { - httpUrl += "?transcode=" + profile; - } - - mediaSource.Path = httpUrl; - - return new SharedHttpStream( - mediaSource, - tunerHost, - streamId, - FileSystem, - _httpClientFactory, - Logger, - Config, - _appHost, - _streamHelper); + httpUrl += "?transcode=" + profile; } - return new HdHomerunUdpStream( + mediaSource.Path = httpUrl; + + return new SharedHttpStream( mediaSource, tunerHost, streamId, - new HdHomerunChannelCommands(hdhomerunChannel.Number, profile), - modelInfo.TunerCount, FileSystem, + _httpClientFactory, Logger, Config, _appHost, diff --git a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs index 7235e65b64..3666d342ed 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs @@ -80,22 +80,6 @@ namespace Jellyfin.LiveTv.TunerHosts .ConfigureAwait(false); } - public Task> GetTunerInfos(CancellationToken cancellationToken) - { - var list = GetTunerHosts() - .Select(i => new LiveTvTunerInfo() - { - Name = Name, - SourceType = Type, - Status = LiveTvTunerStatus.Available, - Id = i.Url.GetMD5().ToString("N", CultureInfo.InvariantCulture), - Url = i.Url - }) - .ToList(); - - return Task.FromResult(list); - } - protected override async Task GetChannelStream(TunerHostInfo tunerHost, ChannelInfo channel, string streamId, IList currentLiveStreams, CancellationToken cancellationToken) { var tunerCount = tunerHost.TunerCount; From 70feba6540c1c4f6d9fbfacc974fc18d1da050b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:33:08 -0700 Subject: [PATCH 111/132] chore(deps): update dependency xunit to v2.6.6 (#10867) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 294414ee12..3343971fe4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -85,6 +85,6 @@ - + From fa9b1d5f65d135ff3021cce60ce3ac2867837268 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:33:28 -0700 Subject: [PATCH 112/132] chore(deps): update dependency diacritics to v3.3.27 (#10862) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3343971fe4..b88e43f9db 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,7 +12,7 @@ - + From 021bfd1ecd3b72013926fe7602c03ac9b6b89692 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:34:18 -0700 Subject: [PATCH 113/132] chore(deps): update dependency svg.skia to v1.0.0.10 (#10480) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b88e43f9db..bc87f9fc96 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -71,7 +71,7 @@ - + From d4ca845a260b0975b6c47287bec8ccfa1e545ed5 Mon Sep 17 00:00:00 2001 From: SuperDumbTM Date: Tue, 16 Jan 2024 09:00:34 +0000 Subject: [PATCH 114/132] Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 66cbee3948..3ab9774c27 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -83,13 +83,13 @@ "UserDeletedWithName": "用戶 {0} 已被移除", "UserDownloadingItemWithValues": "{0} 正在下載 {1}", "UserLockedOutWithName": "用戶 {0} 已被封鎖", - "UserOfflineFromDevice": "{0} 從 {1} 斷開連接", + "UserOfflineFromDevice": "{0} 終止了 {1} 的連接", "UserOnlineFromDevice": "{0} 從 {1} 連線", - "UserPasswordChangedWithName": "{0} 的密碼已被變改", + "UserPasswordChangedWithName": "{0} 的密碼已被更改", "UserPolicyUpdatedWithName": "使用條款已更新為 {0}", "UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}", - "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}", - "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫", + "UserStoppedPlayingItemWithValues": "{0} 停止在 {2} 上播放 {1}", + "ValueHasBeenAddedToLibrary": "{0} 已被加入至你的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", "VersionNumber": "版本 {0}", "TaskDownloadMissingSubtitles": "下載欠缺字幕", From bf57faff6506df82ba2302ae08e5f1da5698798e Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 16 Jan 2024 16:20:37 +0000 Subject: [PATCH 115/132] Translated using Weblate (Georgian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ka/ --- Emby.Server.Implementations/Localization/Core/ka.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json index 5368b8eb6e..2d02522fea 100644 --- a/Emby.Server.Implementations/Localization/Core/ka.json +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -4,9 +4,9 @@ "HeaderFavoriteAlbums": "რჩეული ალბომები", "TasksApplicationCategory": "აპლიკაცია", "Albums": "ალბომები", - "AppDeviceValues": "აპი: {0}, მოწყობილობა: {1}", + "AppDeviceValues": "აპლიკაცია: {0}, მოწყობილობა: {1}", "Application": "აპლიკაცია", - "Artists": "შემსრულებლები", + "Artists": "არტისტი", "AuthenticationSucceededWithUserName": "{0} -ის ავთენტიკაცია წარმატებულია", "Books": "წიგნები", "Forced": "ძალით", From 244a739675a140be4c6ad88ee8028cdadec4ac58 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 16 Jan 2024 20:24:28 +0100 Subject: [PATCH 116/132] Fix incorrect path check in CleanupCollectionAndPlaylistPathsTask --- .../Tasks/CleanupCollectionAndPlaylistPathsTask.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs index acd4bf9056..812df81922 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs @@ -115,9 +115,10 @@ public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask List? itemsToRemove = null; foreach (var linkedChild in folder.LinkedChildren) { - if (!File.Exists(folder.Path)) + var path = linkedChild.Path; + if (!File.Exists(path)) { - _logger.LogInformation("Item in {FolderName} cannot be found at {ItemPath}", folder.Name, linkedChild.Path); + _logger.LogInformation("Item in {FolderName} cannot be found at {ItemPath}", folder.Name, path); (itemsToRemove ??= new List()).Add(linkedChild); } } From e7b8d45bbb0f2b832245dae7ac0d401c56cb10a4 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Wed, 17 Jan 2024 08:51:39 -0700 Subject: [PATCH 117/132] Use helper function to compare guid (#10825) --- .../Data/SqliteItemRepository.cs | 32 +++++++++---------- .../EntryPoints/LibraryChangedNotifier.cs | 3 +- .../Library/LibraryManager.cs | 30 ++++++++--------- .../Library/MediaSourceManager.cs | 5 +-- .../Library/MusicManager.cs | 3 +- .../Library/SearchEngine.cs | 4 +-- .../Library/UserViewManager.cs | 5 +-- .../Playlists/PlaylistManager.cs | 3 +- .../Session/SessionManager.cs | 22 ++++++------- Emby.Server.Implementations/SyncPlay/Group.cs | 3 +- .../TV/TVSeriesManager.cs | 7 ++-- .../Updates/InstallationManager.cs | 3 +- .../DefaultAuthorizationHandler.cs | 3 +- .../FirstTimeSetupHandler.cs | 3 +- Jellyfin.Api/Controllers/ArtistsController.cs | 7 ++-- .../Controllers/ChannelsController.cs | 5 +-- Jellyfin.Api/Controllers/FilterController.cs | 5 +-- Jellyfin.Api/Controllers/GenresController.cs | 5 +-- .../Controllers/InstantMixController.cs | 15 +++++---- Jellyfin.Api/Controllers/ItemsController.cs | 5 +-- Jellyfin.Api/Controllers/LibraryController.cs | 26 +++++++-------- Jellyfin.Api/Controllers/LiveTvController.cs | 25 ++++++++------- Jellyfin.Api/Controllers/MoviesController.cs | 3 +- .../Controllers/MusicGenresController.cs | 5 +-- Jellyfin.Api/Controllers/PersonsController.cs | 5 +-- .../Controllers/PlaylistsController.cs | 3 +- Jellyfin.Api/Controllers/SearchController.cs | 2 +- Jellyfin.Api/Controllers/SessionController.cs | 7 ++-- Jellyfin.Api/Controllers/StudiosController.cs | 5 +-- .../Controllers/SuggestionsController.cs | 3 +- Jellyfin.Api/Controllers/TvShowsController.cs | 10 +++--- Jellyfin.Api/Controllers/UserController.cs | 3 +- .../Controllers/UserLibraryController.cs | 19 +++++------ Jellyfin.Api/Controllers/VideosController.cs | 7 ++-- Jellyfin.Api/Controllers/YearsController.cs | 6 ++-- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 3 +- Jellyfin.Api/Helpers/RequestHelpers.cs | 3 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 2 +- .../Users/UserManager.cs | 3 +- .../Entities/AggregateFolder.cs | 3 +- .../Entities/Audio/MusicArtist.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 16 +++++----- MediaBrowser.Controller/Entities/Folder.cs | 9 +++--- .../Entities/TV/Episode.cs | 11 ++++--- MediaBrowser.Controller/Entities/TV/Season.cs | 5 +-- MediaBrowser.Controller/Entities/UserView.cs | 8 ++--- .../Entities/UserViewBuilder.cs | 2 +- MediaBrowser.Controller/Entities/Video.cs | 2 +- .../Transcoding/TranscodeManager.cs | 3 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 3 +- .../Manager/ProviderManager.cs | 4 +-- src/Jellyfin.Extensions/GuidExtensions.cs | 26 +++++++++++++++ .../Converters/JsonNullableGuidConverter.cs | 2 +- .../Channels/ChannelManager.cs | 8 ++--- src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs | 6 ++-- src/Jellyfin.LiveTv/LiveTvDtoService.cs | 5 +-- src/Jellyfin.LiveTv/LiveTvManager.cs | 7 ++-- .../AuthHelper.cs | 3 +- 58 files changed, 249 insertions(+), 184 deletions(-) create mode 100644 src/Jellyfin.Extensions/GuidExtensions.cs diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d0772654ce..a6336f1451 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -699,7 +699,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBindNull("@EndDate"); } - saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(default) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture)); + saveItemStatement.TryBind("@ChannelId", item.ChannelId.IsEmpty() ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture)); if (item is IHasProgramAttributes hasProgramAttributes) { @@ -729,7 +729,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@ProductionYear", item.ProductionYear); var parentId = item.ParentId; - if (parentId.Equals(default)) + if (parentId.IsEmpty()) { saveItemStatement.TryBindNull("@ParentId"); } @@ -925,7 +925,7 @@ namespace Emby.Server.Implementations.Data { saveItemStatement.TryBind("@SeasonName", episode.SeasonName); - var nullableSeasonId = episode.SeasonId.Equals(default) ? (Guid?)null : episode.SeasonId; + var nullableSeasonId = episode.SeasonId.IsEmpty() ? (Guid?)null : episode.SeasonId; saveItemStatement.TryBind("@SeasonId", nullableSeasonId); } @@ -937,7 +937,7 @@ namespace Emby.Server.Implementations.Data if (item is IHasSeries hasSeries) { - var nullableSeriesId = hasSeries.SeriesId.Equals(default) ? (Guid?)null : hasSeries.SeriesId; + var nullableSeriesId = hasSeries.SeriesId.IsEmpty() ? (Guid?)null : hasSeries.SeriesId; saveItemStatement.TryBind("@SeriesId", nullableSeriesId); saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey); @@ -1010,7 +1010,7 @@ namespace Emby.Server.Implementations.Data } Guid ownerId = item.OwnerId; - if (ownerId.Equals(default)) + if (ownerId.IsEmpty()) { saveItemStatement.TryBindNull("@OwnerId"); } @@ -1266,7 +1266,7 @@ namespace Emby.Server.Implementations.Data /// is . public BaseItem RetrieveItem(Guid id) { - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentException("Guid can't be empty", nameof(id)); } @@ -1970,7 +1970,7 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentNullException(nameof(id)); } @@ -3230,7 +3230,7 @@ namespace Emby.Server.Implementations.Data whereClauses.Add($"ChannelId in ({inClause})"); } - if (!query.ParentId.Equals(default)) + if (!query.ParentId.IsEmpty()) { whereClauses.Add("ParentId=@ParentId"); statement?.TryBind("@ParentId", query.ParentId); @@ -4452,7 +4452,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public void DeleteItem(Guid id) { - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentNullException(nameof(id)); } @@ -4583,13 +4583,13 @@ AND Type = @InternalPersonType)"); statement?.TryBind("@UserId", query.User.InternalId); } - if (!query.ItemId.Equals(default)) + if (!query.ItemId.IsEmpty()) { whereClauses.Add("ItemId=@ItemId"); statement?.TryBind("@ItemId", query.ItemId); } - if (!query.AppearsInItemId.Equals(default)) + if (!query.AppearsInItemId.IsEmpty()) { whereClauses.Add("p.Name in (Select Name from People where ItemId=@AppearsInItemId)"); statement?.TryBind("@AppearsInItemId", query.AppearsInItemId); @@ -4640,7 +4640,7 @@ AND Type = @InternalPersonType)"); private void UpdateAncestors(Guid itemId, List ancestorIds, SqliteConnection db, SqliteCommand deleteAncestorsStatement) { - if (itemId.Equals(default)) + if (itemId.IsEmpty()) { throw new ArgumentNullException(nameof(itemId)); } @@ -5156,7 +5156,7 @@ AND Type = @InternalPersonType)"); private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, SqliteConnection db) { - if (itemId.Equals(default)) + if (itemId.IsEmpty()) { throw new ArgumentNullException(nameof(itemId)); } @@ -5228,7 +5228,7 @@ AND Type = @InternalPersonType)"); public void UpdatePeople(Guid itemId, List people) { - if (itemId.Equals(default)) + if (itemId.IsEmpty()) { throw new ArgumentNullException(nameof(itemId)); } @@ -5378,7 +5378,7 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentNullException(nameof(id)); } @@ -5758,7 +5758,7 @@ AND Type = @InternalPersonType)"); CancellationToken cancellationToken) { CheckDisposed(); - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentException("Guid can't be empty.", nameof(id)); } diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index a83d7a4105..83e7b230df 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Events; +using Jellyfin.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -241,7 +242,7 @@ public sealed class LibraryChangedNotifier : IServerEntryPoint { var userIds = _sessionManager.Sessions .Select(i => i.UserId) - .Where(i => !i.Equals(default)) + .Where(i => !i.IsEmpty()) .Distinct() .ToArray(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a79ffd9cb1..8ae913dad8 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -732,7 +732,7 @@ namespace Emby.Server.Implementations.Library Path = path }; - if (folder.Id.Equals(default)) + if (folder.Id.IsEmpty()) { if (string.IsNullOrEmpty(folder.Path)) { @@ -1219,7 +1219,7 @@ namespace Emby.Server.Implementations.Library /// is null. public BaseItem GetItemById(Guid id) { - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentException("Guid can't be empty", nameof(id)); } @@ -1241,7 +1241,7 @@ namespace Emby.Server.Implementations.Library public List GetItemList(InternalItemsQuery query, bool allowExternalContent) { - if (query.Recursive && !query.ParentId.Equals(default)) + if (query.Recursive && !query.ParentId.IsEmpty()) { var parent = GetItemById(query.ParentId); if (parent is not null) @@ -1272,7 +1272,7 @@ namespace Emby.Server.Implementations.Library public int GetCount(InternalItemsQuery query) { - if (query.Recursive && !query.ParentId.Equals(default)) + if (query.Recursive && !query.ParentId.IsEmpty()) { var parent = GetItemById(query.ParentId); if (parent is not null) @@ -1430,7 +1430,7 @@ namespace Emby.Server.Implementations.Library public QueryResult GetItemsResult(InternalItemsQuery query) { - if (query.Recursive && !query.ParentId.Equals(default)) + if (query.Recursive && !query.ParentId.IsEmpty()) { var parent = GetItemById(query.ParentId); if (parent is not null) @@ -1486,7 +1486,7 @@ namespace Emby.Server.Implementations.Library private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true) { if (query.AncestorIds.Length == 0 && - query.ParentId.Equals(default) && + query.ParentId.IsEmpty() && query.ChannelIds.Count == 0 && query.TopParentIds.Length == 0 && string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) && @@ -1520,7 +1520,7 @@ namespace Emby.Server.Implementations.Library } // Translate view into folders - if (!view.DisplayParentId.Equals(default)) + if (!view.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(view.DisplayParentId); if (displayParent is not null) @@ -1531,7 +1531,7 @@ namespace Emby.Server.Implementations.Library return Array.Empty(); } - if (!view.ParentId.Equals(default)) + if (!view.ParentId.IsEmpty()) { var displayParent = GetItemById(view.ParentId); if (displayParent is not null) @@ -2137,7 +2137,7 @@ namespace Emby.Server.Implementations.Library return null; } - while (!item.ParentId.Equals(default)) + while (!item.ParentId.IsEmpty()) { var parent = item.GetParent(); if (parent is null || parent is AggregateFolder) @@ -2215,7 +2215,7 @@ namespace Emby.Server.Implementations.Library CollectionType? viewType, string sortName) { - var parentIdString = parentId.Equals(default) + var parentIdString = parentId.IsEmpty() ? null : parentId.ToString("N", CultureInfo.InvariantCulture); var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty); @@ -2251,7 +2251,7 @@ namespace Emby.Server.Implementations.Library var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - if (!refresh && !item.DisplayParentId.Equals(default)) + if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; @@ -2315,7 +2315,7 @@ namespace Emby.Server.Implementations.Library var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - if (!refresh && !item.DisplayParentId.Equals(default)) + if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; @@ -2345,7 +2345,7 @@ namespace Emby.Server.Implementations.Library { ArgumentException.ThrowIfNullOrEmpty(name); - var parentIdString = parentId.Equals(default) + var parentIdString = parentId.IsEmpty() ? null : parentId.ToString("N", CultureInfo.InvariantCulture); var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType?.ToString() ?? string.Empty); @@ -2391,7 +2391,7 @@ namespace Emby.Server.Implementations.Library var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; - if (!refresh && !item.DisplayParentId.Equals(default)) + if (!refresh && !item.DisplayParentId.IsEmpty()) { var displayParent = GetItemById(item.DisplayParentId); refresh = displayParent is not null && displayParent.DateLastSaved > item.DateLastRefreshed; @@ -2419,7 +2419,7 @@ namespace Emby.Server.Implementations.Library return GetItemById(parentId.Value); } - if (userId.HasValue && !userId.Equals(default)) + if (!userId.IsNullOrEmpty()) { return GetUserRootFolder(); } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 68eccf311d..c38f1af912 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -524,10 +525,10 @@ namespace Emby.Server.Implementations.Library _logger.LogInformation("Live stream opened: {@MediaSource}", mediaSource); var clone = JsonSerializer.Deserialize(json, _jsonOptions); - if (!request.UserId.Equals(default)) + if (!request.UserId.IsEmpty()) { var user = _userManager.GetUserById(request.UserId); - var item = request.ItemId.Equals(default) + var item = request.ItemId.IsEmpty() ? null : _libraryManager.GetItemById(request.ItemId); SetDefaultAudioAndSubtitleStreamIndexes(item, clone, user); diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index b2439a87e1..078f4ad219 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Library { return Guid.Empty; } - }).Where(i => !i.Equals(default)).ToArray(); + }).Where(i => !i.IsEmpty()).ToArray(); return GetInstantMixFromGenreIds(genreIds, user, dtoOptions); } diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index b916b91708..020cb517d1 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library public QueryResult GetSearchHints(SearchQuery query) { User user = null; - if (!query.UserId.Equals(default)) + if (!query.UserId.IsEmpty()) { user = _userManager.GetUserById(query.UserId); } @@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.Library if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist) { - if (!searchQuery.ParentId.Equals(default)) + if (!searchQuery.ParentId.IsEmpty()) { searchQuery.AncestorIds = new[] { searchQuery.ParentId }; searchQuery.ParentId = Guid.Empty; diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 1d662ed8dd..83a66c8e47 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; @@ -151,7 +152,7 @@ namespace Emby.Server.Implementations.Library var index = Array.IndexOf(orders, i.Id); if (index == -1 && i is UserView view - && !view.DisplayParentId.Equals(default)) + && !view.DisplayParentId.IsEmpty()) { index = Array.IndexOf(orders, view.DisplayParentId); } @@ -253,7 +254,7 @@ namespace Emby.Server.Implementations.Library var parents = new List(); - if (!parentId.Equals(default)) + if (!parentId.IsEmpty()) { var parentItem = _libraryManager.GetItemById(parentId); if (parentItem is Channel) diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index d2e2fd7d56..aea8d65322 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -178,7 +179,7 @@ namespace Emby.Server.Implementations.Playlists public Task AddToPlaylistAsync(Guid playlistId, IReadOnlyCollection itemIds, Guid userId) { - var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); + var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) { diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index f457a78b3b..bbb3938dcf 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -337,7 +337,7 @@ namespace Emby.Server.Implementations.Session info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } - if (!info.ItemId.Equals(default) && info.Item is null && libraryItem is not null) + if (!info.ItemId.IsEmpty() && info.Item is null && libraryItem is not null) { var current = session.NowPlayingItem; @@ -529,7 +529,7 @@ namespace Emby.Server.Implementations.Session { var users = new List(); - if (session.UserId.Equals(default)) + if (session.UserId.IsEmpty()) { return users; } @@ -690,7 +690,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = info.ItemId.Equals(default) + var libraryItem = info.ItemId.IsEmpty() ? null : GetNowPlayingItem(session, info.ItemId); @@ -784,7 +784,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = info.ItemId.Equals(default) + var libraryItem = info.ItemId.IsEmpty() ? null : GetNowPlayingItem(session, info.ItemId); @@ -923,7 +923,7 @@ namespace Emby.Server.Implementations.Session session.StopAutomaticProgress(); - var libraryItem = info.ItemId.Equals(default) + var libraryItem = info.ItemId.IsEmpty() ? null : GetNowPlayingItem(session, info.ItemId); @@ -933,7 +933,7 @@ namespace Emby.Server.Implementations.Session info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture); } - if (!info.ItemId.Equals(default) && info.Item is null && libraryItem is not null) + if (!info.ItemId.IsEmpty() && info.Item is null && libraryItem is not null) { var current = session.NowPlayingItem; @@ -1154,7 +1154,7 @@ namespace Emby.Server.Implementations.Session var session = GetSessionToRemoteControl(sessionId); - var user = session.UserId.Equals(default) ? null : _userManager.GetUserById(session.UserId); + var user = session.UserId.IsEmpty() ? null : _userManager.GetUserById(session.UserId); List items; @@ -1223,7 +1223,7 @@ namespace Emby.Server.Implementations.Session { var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - if (!controllingSession.UserId.Equals(default)) + if (!controllingSession.UserId.IsEmpty()) { command.ControllingUserId = controllingSession.UserId; } @@ -1342,7 +1342,7 @@ namespace Emby.Server.Implementations.Session { var controllingSession = GetSession(controllingSessionId); AssertCanControl(session, controllingSession); - if (!controllingSession.UserId.Equals(default)) + if (!controllingSession.UserId.IsEmpty()) { command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture); } @@ -1463,7 +1463,7 @@ namespace Emby.Server.Implementations.Session ArgumentException.ThrowIfNullOrEmpty(request.AppVersion); User user = null; - if (!request.UserId.Equals(default)) + if (!request.UserId.IsEmpty()) { user = _userManager.GetUserById(request.UserId); } @@ -1766,7 +1766,7 @@ namespace Emby.Server.Implementations.Session { ArgumentNullException.ThrowIfNull(info); - var user = info.UserId.Equals(default) + var user = info.UserId.IsEmpty() ? null : _userManager.GetUserById(info.UserId); diff --git a/Emby.Server.Implementations/SyncPlay/Group.cs b/Emby.Server.Implementations/SyncPlay/Group.cs index da8f949326..a7821c0e0e 100644 --- a/Emby.Server.Implementations/SyncPlay/Group.cs +++ b/Emby.Server.Implementations/SyncPlay/Group.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay; @@ -553,7 +554,7 @@ namespace Emby.Server.Implementations.SyncPlay if (playingItemRemoved) { var itemId = PlayQueue.GetPlayingItemId(); - if (!itemId.Equals(default)) + if (!itemId.IsEmpty()) { var item = _libraryManager.GetItemById(itemId); RunTimeTicks = item.RunTimeTicks ?? 0; diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index ef890aeb4f..34c9e86f26 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -41,7 +42,7 @@ namespace Emby.Server.Implementations.TV } string? presentationUniqueKey = null; - if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default)) + if (!query.SeriesId.IsNullOrEmpty()) { if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series) { @@ -91,7 +92,7 @@ namespace Emby.Server.Implementations.TV string? presentationUniqueKey = null; int? limit = null; - if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default)) + if (!request.SeriesId.IsNullOrEmpty()) { if (_libraryManager.GetItemById(request.SeriesId.Value) is Series series) { @@ -146,7 +147,7 @@ namespace Emby.Server.Implementations.TV // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) - var alwaysEnableFirstEpisode = request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default); + var alwaysEnableFirstEpisode = !request.SeriesId.IsNullOrEmpty(); var anyFound = false; return allNextUp diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 15c4cfdf02..ce3d6cab88 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -11,6 +11,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Events; +using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; @@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.Updates availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } - if (!id.Equals(default)) + if (!id.IsEmpty()) { availablePackages = availablePackages.Where(x => x.Id.Equals(id)); } diff --git a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs index cf3cb69052..7d0fe55898 100644 --- a/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/DefaultAuthorizationPolicy/DefaultAuthorizationHandler.cs @@ -2,6 +2,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Library; @@ -41,7 +42,7 @@ namespace Jellyfin.Api.Auth.DefaultAuthorizationPolicy var isApiKey = context.User.GetIsApiKey(); var userId = context.User.GetUserId(); // This likely only happens during the wizard, so skip the default checks and let any other handlers do it - if (!isApiKey && userId.Equals(default)) + if (!isApiKey && userId.IsEmpty()) { return Task.CompletedTask; } diff --git a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs index 688a13bc0b..965b7e7e60 100644 --- a/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs +++ b/Jellyfin.Api/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandler.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Library; @@ -46,7 +47,7 @@ namespace Jellyfin.Api.Auth.FirstTimeSetupPolicy } var userId = contextUser.GetUserId(); - if (userId.Equals(default)) + if (userId.IsEmpty()) { context.Fail(); return Task.CompletedTask; diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index e7d3e694ab..8b931f1621 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -126,7 +127,7 @@ public class ArtistsController : BaseJellyfinApiController User? user = null; BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId); - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { user = _userManager.GetUserById(userId.Value); } @@ -330,7 +331,7 @@ public class ArtistsController : BaseJellyfinApiController User? user = null; BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId); - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { user = _userManager.GetUserById(userId.Value); } @@ -469,7 +470,7 @@ public class ArtistsController : BaseJellyfinApiController var item = _libraryManager.GetArtist(name, dtoOptions); - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { var user = _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index fdc16ee23f..f83c71b578 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/Jellyfin.Api/Controllers/ChannelsController.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -126,7 +127,7 @@ public class ChannelsController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -201,7 +202,7 @@ public class ChannelsController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index baeb8b81a0..d6e043e6a1 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -3,6 +3,7 @@ using System.Linq; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -53,7 +54,7 @@ public class FilterController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -146,7 +147,7 @@ public class FilterController : BaseJellyfinApiController [FromQuery] bool? recursive) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 6cb1993e46..54d48aec21 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -95,7 +96,7 @@ public class GenresController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); - User? user = userId.Value.Equals(default) + User? user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -172,7 +173,7 @@ public class GenresController : BaseJellyfinApiController item ??= new Genre(); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 4dc2a4253d..e7ff1f9868 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -76,7 +77,7 @@ public class InstantMixController : BaseJellyfinApiController { var item = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -113,7 +114,7 @@ public class InstantMixController : BaseJellyfinApiController { var album = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -150,7 +151,7 @@ public class InstantMixController : BaseJellyfinApiController { var playlist = (Playlist)_libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -186,7 +187,7 @@ public class InstantMixController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -223,7 +224,7 @@ public class InstantMixController : BaseJellyfinApiController { var item = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -260,7 +261,7 @@ public class InstantMixController : BaseJellyfinApiController { var item = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } @@ -334,7 +335,7 @@ public class InstantMixController : BaseJellyfinApiController { var item = _libraryManager.GetItemById(id); userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index a1fc8e11b2..d10fba920c 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -245,7 +246,7 @@ public class ItemsController : BaseJellyfinApiController var isApiKey = User.GetIsApiKey(); // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method userId = RequestHelpers.GetUserId(User, userId); - var user = !isApiKey && !userId.Value.Equals(default) + var user = !isApiKey && !userId.IsNullOrEmpty() ? _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException() : null; @@ -840,7 +841,7 @@ public class ItemsController : BaseJellyfinApiController var ancestorIds = Array.Empty(); var excludeFolderIds = user.GetPreferenceValues(PreferenceKind.LatestItemExcludes); - if (parentIdGuid.Equals(default) && excludeFolderIds.Length > 0) + if (parentIdGuid.IsEmpty() && excludeFolderIds.Length > 0) { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index de057bbab9..a0bbc961f0 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -146,12 +146,12 @@ public class LibraryController : BaseJellyfinApiController [FromQuery] bool inheritFromParent = false) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = itemId.Equals(default) - ? (userId.Value.Equals(default) + var item = itemId.IsEmpty() + ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) : _libraryManager.GetItemById(itemId); @@ -213,12 +213,12 @@ public class LibraryController : BaseJellyfinApiController [FromQuery] bool inheritFromParent = false) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = itemId.Equals(default) - ? (userId.Value.Equals(default) + var item = itemId.IsEmpty() + ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) : _libraryManager.GetItemById(itemId); @@ -339,7 +339,7 @@ public class LibraryController : BaseJellyfinApiController { var isApiKey = User.GetIsApiKey(); var userId = User.GetUserId(); - var user = !isApiKey && !userId.Equals(default) + var user = !isApiKey && !userId.IsEmpty() ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException() : null; if (!isApiKey && user is null) @@ -382,7 +382,7 @@ public class LibraryController : BaseJellyfinApiController { var isApiKey = User.GetIsApiKey(); var userId = User.GetUserId(); - var user = !isApiKey && !userId.Equals(default) + var user = !isApiKey && !userId.IsEmpty() ? _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException() : null; @@ -428,7 +428,7 @@ public class LibraryController : BaseJellyfinApiController [FromQuery] bool? isFavorite) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -471,7 +471,7 @@ public class LibraryController : BaseJellyfinApiController var baseItemDtos = new List(); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -702,8 +702,8 @@ public class LibraryController : BaseJellyfinApiController [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields) { userId = RequestHelpers.GetUserId(User, userId); - var item = itemId.Equals(default) - ? (userId.Value.Equals(default) + var item = itemId.IsEmpty() + ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) : _libraryManager.GetItemById(itemId); @@ -718,7 +718,7 @@ public class LibraryController : BaseJellyfinApiController return new QueryResult(); } - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 27eb88b60f..1b2f5750f6 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -15,6 +15,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LiveTvDtos; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; @@ -182,7 +183,7 @@ public class LiveTvController : BaseJellyfinApiController dtoOptions, CancellationToken.None); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -214,10 +215,10 @@ public class LiveTvController : BaseJellyfinApiController public ActionResult GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = channelId.Equals(default) + var item = channelId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(channelId); @@ -387,7 +388,7 @@ public class LiveTvController : BaseJellyfinApiController public async Task>> GetRecordingFolders([FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var folders = await _liveTvManager.GetRecordingFoldersAsync(user).ConfigureAwait(false); @@ -410,10 +411,10 @@ public class LiveTvController : BaseJellyfinApiController public ActionResult GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = recordingId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); + var item = recordingId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); var dtoOptions = new DtoOptions() .AddClientFields(User); @@ -567,7 +568,7 @@ public class LiveTvController : BaseJellyfinApiController [FromQuery] bool enableTotalRecordCount = true) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -594,7 +595,7 @@ public class LiveTvController : BaseJellyfinApiController GenreIds = genreIds }; - if (librarySeriesId.HasValue && !librarySeriesId.Equals(default)) + if (!librarySeriesId.IsNullOrEmpty()) { query.IsSeries = true; @@ -623,7 +624,7 @@ public class LiveTvController : BaseJellyfinApiController [Authorize(Policy = Policies.LiveTvAccess)] public async Task>> GetPrograms([FromBody] GetProgramsDto body) { - var user = body.UserId.Equals(default) ? null : _userManager.GetUserById(body.UserId); + var user = body.UserId.IsEmpty() ? null : _userManager.GetUserById(body.UserId); var query = new InternalItemsQuery(user) { @@ -648,7 +649,7 @@ public class LiveTvController : BaseJellyfinApiController GenreIds = body.GenreIds }; - if (!body.LibrarySeriesId.Equals(default)) + if (!body.LibrarySeriesId.IsEmpty()) { query.IsSeries = true; @@ -707,7 +708,7 @@ public class LiveTvController : BaseJellyfinApiController [FromQuery] bool enableTotalRecordCount = true) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -746,7 +747,7 @@ public class LiveTvController : BaseJellyfinApiController [FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index e1145481fa..471bcd096e 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -7,6 +7,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; @@ -69,7 +70,7 @@ public class MoviesController : BaseJellyfinApiController [FromQuery] int itemLimit = 8) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 69b9042646..5411baa3e7 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -6,6 +6,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -95,7 +96,7 @@ public class MusicGenresController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); - User? user = userId.Value.Equals(default) + User? user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -164,7 +165,7 @@ public class MusicGenresController : BaseJellyfinApiController return NotFound(); } - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { var user = _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index b4c6f490a0..6ca3086015 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -83,7 +84,7 @@ public class PersonsController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - User? user = userId.Value.Equals(default) + User? user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -129,7 +130,7 @@ public class PersonsController : BaseJellyfinApiController return NotFound(); } - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { var user = _userManager.GetUserById(userId.Value); return _dtoService.GetBaseItemDto(item, dtoOptions, user); diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index c4c89ccde0..921cc6031f 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -9,6 +9,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.PlaylistDtos; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; @@ -188,7 +189,7 @@ public class PlaylistsController : BaseJellyfinApiController return NotFound(); } - var user = userId.Equals(default) + var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 5b45941657..413b7b8340 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -209,7 +209,7 @@ public class SearchController : BaseJellyfinApiController break; } - if (!item.ChannelId.Equals(default)) + if (!item.ChannelId.IsEmpty()) { var channel = _libraryManager.GetItemById(item.ChannelId); result.ChannelName = channel?.Name; diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 083515a949..52b58b8f10 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -10,6 +10,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.SessionDtos; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; @@ -71,7 +72,7 @@ public class SessionController : BaseJellyfinApiController result = result.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); } - if (controllableByUserId.HasValue && !controllableByUserId.Equals(default)) + if (!controllableByUserId.IsNullOrEmpty()) { result = result.Where(i => i.SupportsRemoteControl); @@ -83,12 +84,12 @@ public class SessionController : BaseJellyfinApiController if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers)) { - result = result.Where(i => i.UserId.Equals(default) || i.ContainsUser(controllableByUserId.Value)); + result = result.Where(i => i.UserId.IsEmpty() || i.ContainsUser(controllableByUserId.Value)); } if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl)) { - result = result.Where(i => !i.UserId.Equals(default)); + result = result.Where(i => !i.UserId.IsEmpty()); } result = result.Where(i => diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index f434f60f51..708fc7436f 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -5,6 +5,7 @@ using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -91,7 +92,7 @@ public class StudiosController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - User? user = userId.Value.Equals(default) + User? user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -144,7 +145,7 @@ public class StudiosController : BaseJellyfinApiController var dtoOptions = new DtoOptions().AddClientFields(User); var item = _libraryManager.GetStudio(name); - if (!userId.Equals(default)) + if (!userId.IsNullOrEmpty()) { var user = _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index 675757fc51..2aa6d25a79 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; 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.Library; @@ -62,7 +63,7 @@ public class SuggestionsController : BaseJellyfinApiController [FromQuery] int? limit, [FromQuery] bool enableTotalRecordCount = false) { - var user = userId.Equals(default) + var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index 55a30d4692..3d84b61bf4 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -111,7 +111,7 @@ public class TvShowsController : BaseJellyfinApiController }, options); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -150,7 +150,7 @@ public class TvShowsController : BaseJellyfinApiController [FromQuery] bool? enableUserData) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -222,7 +222,7 @@ public class TvShowsController : BaseJellyfinApiController [FromQuery] ItemSortBy? sortBy) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); @@ -284,7 +284,7 @@ public class TvShowsController : BaseJellyfinApiController } // This must be the last filter - if (adjacentTo.HasValue && !adjacentTo.Value.Equals(default)) + if (!adjacentTo.IsNullOrEmpty()) { episodes = UserViewBuilder.FilterForAdjacency(episodes, adjacentTo.Value).ToList(); } @@ -339,7 +339,7 @@ public class TvShowsController : BaseJellyfinApiController [FromQuery] bool? enableUserData) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index f9f27f1480..ea10ee24f9 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -532,7 +533,7 @@ public class UserController : BaseJellyfinApiController public ActionResult GetCurrentUser() { var userId = User.GetUserId(); - if (userId.Equals(default)) + if (userId.IsEmpty()) { return BadRequest(); } diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 2c4fe91862..264e0a3dbd 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -8,6 +8,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -84,7 +85,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -145,7 +146,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -185,7 +186,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -221,7 +222,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -257,7 +258,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -294,7 +295,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -330,7 +331,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -375,7 +376,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); @@ -558,7 +559,7 @@ public class UserLibraryController : BaseJellyfinApiController return NotFound(); } - var item = itemId.Equals(default) + var item = itemId.IsEmpty() ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index c231c147fc..e6c3198698 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -11,6 +11,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; +using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; @@ -96,12 +97,12 @@ public class VideosController : BaseJellyfinApiController public ActionResult> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); - var user = userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); - var item = itemId.Equals(default) - ? (userId.Value.Equals(default) + var item = itemId.IsEmpty() + ? (userId.IsNullOrEmpty() ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder()) : _libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index ca46c38c59..e4aa0ea42d 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -90,7 +90,7 @@ public class YearsController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - User? user = userId.Value.Equals(default) + User? user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId); @@ -110,7 +110,7 @@ public class YearsController : BaseJellyfinApiController { var folder = (Folder)parentItem; - if (userId.Equals(default)) + if (userId.IsNullOrEmpty()) { items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToList(); } @@ -182,7 +182,7 @@ public class YearsController : BaseJellyfinApiController var dtoOptions = new DtoOptions() .AddClientFields(User); - if (!userId.Value.Equals(default)) + if (!userId.IsNullOrEmpty()) { var user = _userManager.GetUserById(userId.Value); return _dtoService.GetBaseItemDto(item, dtoOptions, user); diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 321987ca74..6a24ad32ab 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -86,7 +87,7 @@ public class MediaInfoHelper string? mediaSourceId = null, string? liveStreamId = null) { - var user = userId is null || userId.Value.Equals(default) + var user = userId.IsNullOrEmpty() ? null : _userManager.GetUserById(userId.Value); var item = _libraryManager.GetItemById(id); diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index be3d4dfb67..429e972132 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -7,6 +7,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -67,7 +68,7 @@ public static class RequestHelpers var authenticatedUserId = claimsPrincipal.GetUserId(); // UserId not provided, fall back to authenticated user id. - if (userId is null || userId.Value.Equals(default)) + if (userId.IsNullOrEmpty()) { return authenticatedUserId; } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 78943f7b58..7a3842a9f7 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -82,7 +82,7 @@ public static class StreamingHelpers }; var userId = httpContext.User.GetUserId(); - if (!userId.Equals(default)) + if (!userId.IsEmpty()) { state.User = userManager.GetUserById(userId); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index cc464a7a3d..c4a2bfdb87 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -11,6 +11,7 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Data.Events.Users; +using Jellyfin.Extensions; using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -117,7 +118,7 @@ namespace Jellyfin.Server.Implementations.Users /// public User? GetUserById(Guid id) { - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentException("Guid can't be empty", nameof(id)); } diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index d789033f16..b225f22df0 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Extensions; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -184,7 +185,7 @@ namespace MediaBrowser.Controller.Entities /// The id is empty. public BaseItem FindVirtualChild(Guid id) { - if (id.Equals(default)) + if (id.IsEmpty()) { throw new ArgumentNullException(nameof(id)); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 18d948a62f..11cdf84445 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Entities.Audio public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo { [JsonIgnore] - public bool IsAccessedByName => ParentId.Equals(default); + public bool IsAccessedByName => ParentId.IsEmpty(); [JsonIgnore] public override bool IsFolder => !IsAccessedByName; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index fdbceac961..ddcc994a0b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -240,7 +240,7 @@ namespace MediaBrowser.Controller.Entities { get { - if (!ChannelId.Equals(default)) + if (!ChannelId.IsEmpty()) { return SourceType.Channel; } @@ -530,7 +530,7 @@ namespace MediaBrowser.Controller.Entities get { var id = DisplayParentId; - if (id.Equals(default)) + if (id.IsEmpty()) { return null; } @@ -746,7 +746,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool StopRefreshIfLocalMetadataFound => true; [JsonIgnore] - protected virtual bool SupportsOwnedItems => !ParentId.Equals(default) && IsFileProtocol; + protected virtual bool SupportsOwnedItems => !ParentId.IsEmpty() && IsFileProtocol; [JsonIgnore] public virtual bool SupportsPeople => false; @@ -823,7 +823,7 @@ namespace MediaBrowser.Controller.Entities public BaseItem GetOwner() { var ownerId = OwnerId; - return ownerId.Equals(default) ? null : LibraryManager.GetItemById(ownerId); + return ownerId.IsEmpty() ? null : LibraryManager.GetItemById(ownerId); } public bool CanDelete(User user, List allCollectionFolders) @@ -968,7 +968,7 @@ namespace MediaBrowser.Controller.Entities public BaseItem GetParent() { var parentId = ParentId; - if (parentId.Equals(default)) + if (parentId.IsEmpty()) { return null; } @@ -1361,7 +1361,7 @@ namespace MediaBrowser.Controller.Entities var tasks = extras.Select(i => { var subOptions = new MetadataRefreshOptions(options); - if (!i.OwnerId.Equals(ownerId) || !i.ParentId.Equals(default)) + if (!i.OwnerId.Equals(ownerId) || !i.ParentId.IsEmpty()) { i.OwnerId = ownerId; i.ParentId = Guid.Empty; @@ -1673,7 +1673,7 @@ namespace MediaBrowser.Controller.Entities // First get using the cached Id if (info.ItemId.HasValue) { - if (info.ItemId.Value.Equals(default)) + if (info.ItemId.Value.IsEmpty()) { return null; } @@ -2439,7 +2439,7 @@ namespace MediaBrowser.Controller.Entities return Task.FromResult(true); } - if (video.OwnerId.Equals(default)) + if (video.OwnerId.IsEmpty()) { video.OwnerId = this.Id; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index e707eedbf6..74eb089de3 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; @@ -198,7 +199,7 @@ namespace MediaBrowser.Controller.Entities { item.SetParent(this); - if (item.Id.Equals(default)) + if (item.Id.IsEmpty()) { item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType()); } @@ -697,7 +698,7 @@ namespace MediaBrowser.Controller.Entities if (this is not UserRootFolder && this is not AggregateFolder - && query.ParentId.Equals(default)) + && query.ParentId.IsEmpty()) { query.Parent = this; } @@ -840,7 +841,7 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) + if (!query.AdjacentTo.IsNullOrEmpty()) { Logger.LogDebug("Query requires post-filtering due to AdjacentTo"); return true; @@ -987,7 +988,7 @@ namespace MediaBrowser.Controller.Entities #pragma warning restore CA1309 // This must be the last filter - if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) + if (!query.AdjacentTo.IsNullOrEmpty()) { items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index bf31508c1d..37e2414142 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Linq; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; @@ -74,12 +75,12 @@ namespace MediaBrowser.Controller.Entities.TV get { var seriesId = SeriesId; - if (seriesId.Equals(default)) + if (seriesId.IsEmpty()) { seriesId = FindSeriesId(); } - return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series); + return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series); } } @@ -89,12 +90,12 @@ namespace MediaBrowser.Controller.Entities.TV get { var seasonId = SeasonId; - if (seasonId.Equals(default)) + if (seasonId.IsEmpty()) { seasonId = FindSeasonId(); } - return seasonId.Equals(default) ? null : (LibraryManager.GetItemById(seasonId) as Season); + return seasonId.IsEmpty() ? null : (LibraryManager.GetItemById(seasonId) as Season); } } @@ -271,7 +272,7 @@ namespace MediaBrowser.Controller.Entities.TV var seasonId = SeasonId; - if (!seasonId.Equals(default) && !list.Contains(seasonId)) + if (!seasonId.IsEmpty() && !list.Contains(seasonId)) { list.Add(seasonId); } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 0a040a3c28..c29cefc15e 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text.Json.Serialization; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Querying; @@ -48,12 +49,12 @@ namespace MediaBrowser.Controller.Entities.TV get { var seriesId = SeriesId; - if (seriesId.Equals(default)) + if (seriesId.IsEmpty()) { seriesId = FindSeriesId(); } - return seriesId.Equals(default) ? null : (LibraryManager.GetItemById(seriesId) as Series); + return seriesId.IsEmpty() ? null : (LibraryManager.GetItemById(seriesId) as Series); } } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index eb026deb4f..c93488a85e 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -70,11 +70,11 @@ namespace MediaBrowser.Controller.Entities /// public override IEnumerable GetIdsForAncestorQuery() { - if (!DisplayParentId.Equals(default)) + if (!DisplayParentId.IsEmpty()) { yield return DisplayParentId; } - else if (!ParentId.Equals(default)) + else if (!ParentId.IsEmpty()) { yield return ParentId; } @@ -95,11 +95,11 @@ namespace MediaBrowser.Controller.Entities { var parent = this as Folder; - if (!DisplayParentId.Equals(default)) + if (!DisplayParentId.IsEmpty()) { parent = LibraryManager.GetItemById(DisplayParentId) as Folder ?? parent; } - else if (!ParentId.Equals(default)) + else if (!ParentId.IsEmpty()) { parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index a3525c8623..4af000557e 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -433,7 +433,7 @@ namespace MediaBrowser.Controller.Entities var user = query.User; // This must be the last filter - if (query.AdjacentTo.HasValue && !query.AdjacentTo.Value.Equals(default)) + if (!query.AdjacentTo.IsNullOrEmpty()) { items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index be2eb4d288..5adadec390 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -456,7 +456,7 @@ namespace MediaBrowser.Controller.Entities foreach (var child in LinkedAlternateVersions) { // Reset the cached value - if (child.ItemId.HasValue && child.ItemId.Value.Equals(default)) + if (child.ItemId.IsNullOrEmpty()) { child.ItemId = null; } diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index 483d0a1d82..d79e4441aa 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -9,6 +9,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -400,7 +401,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable if (state.VideoRequest is not null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { - var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); + var user = userId.IsEmpty() ? null : _userManager.GetUserById(userId); if (user is not null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) { this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bf18d46dcd..da683a17e6 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; @@ -1536,7 +1537,7 @@ namespace MediaBrowser.Model.Dlna private static void ValidateMediaOptions(MediaOptions options, bool isMediaSource) { - if (options.ItemId.Equals(default)) + if (options.ItemId.IsEmpty()) { ArgumentException.ThrowIfNullOrEmpty(options.DeviceId); } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 4ba8844182..b530b9de3f 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -706,7 +706,7 @@ namespace MediaBrowser.Providers.Manager { BaseItem? referenceItem = null; - if (!searchInfo.ItemId.Equals(default)) + if (!searchInfo.ItemId.IsEmpty()) { referenceItem = _libraryManager.GetItemById(searchInfo.ItemId); } @@ -944,7 +944,7 @@ namespace MediaBrowser.Providers.Manager public void QueueRefresh(Guid itemId, MetadataRefreshOptions options, RefreshPriority priority) { ArgumentNullException.ThrowIfNull(itemId); - if (itemId.Equals(default)) + if (itemId.IsEmpty()) { throw new ArgumentException("Guid can't be empty", nameof(itemId)); } diff --git a/src/Jellyfin.Extensions/GuidExtensions.cs b/src/Jellyfin.Extensions/GuidExtensions.cs new file mode 100644 index 0000000000..95c591a82e --- /dev/null +++ b/src/Jellyfin.Extensions/GuidExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Jellyfin.Extensions; + +/// +/// Guid specific extensions. +/// +public static class GuidExtensions +{ + /// + /// Determine whether the guid is default. + /// + /// The guid. + /// Whether the guid is the default value. + public static bool IsEmpty(this Guid guid) + => guid.Equals(default); + + /// + /// Determine whether the guid is null or default. + /// + /// The guid. + /// Whether the guid is null or the default valueF. + public static bool IsNullOrEmpty([NotNullWhen(false)] this Guid? guid) + => guid is null || guid.Value.IsEmpty(); +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs index 656e3c3dab..0a50b5c3b9 100644 --- a/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs +++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs @@ -18,7 +18,7 @@ namespace Jellyfin.Extensions.Json.Converters { // null got handled higher up the call stack var val = value!.Value; - if (val.Equals(default)) + if (val.IsEmpty()) { writer.WriteNullValue(); } diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs index 51abb503eb..bc968f8ee1 100644 --- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -150,7 +150,7 @@ namespace Jellyfin.LiveTv.Channels /// public async Task> GetChannelsInternalAsync(ChannelQuery query) { - var user = query.UserId.Equals(default) + var user = query.UserId.IsEmpty() ? null : _userManager.GetUserById(query.UserId); @@ -263,7 +263,7 @@ namespace Jellyfin.LiveTv.Channels /// public async Task> GetChannelsAsync(ChannelQuery query) { - var user = query.UserId.Equals(default) + var user = query.UserId.IsEmpty() ? null : _userManager.GetUserById(query.UserId); @@ -695,7 +695,7 @@ namespace Jellyfin.LiveTv.Channels // Find the corresponding channel provider plugin var channelProvider = GetChannelProvider(channel); - var parentItem = query.ParentId.Equals(default) + var parentItem = query.ParentId.IsEmpty() ? channel : _libraryManager.GetItemById(query.ParentId); @@ -708,7 +708,7 @@ namespace Jellyfin.LiveTv.Channels cancellationToken) .ConfigureAwait(false); - if (query.ParentId.Equals(default)) + if (query.ParentId.IsEmpty()) { query.Parent = channel; } diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index 9eb3aa2fd6..e7e927b2d0 100644 --- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -1992,7 +1992,7 @@ namespace Jellyfin.LiveTv.EmbyTV await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false); } - var people = item.Id.Equals(default) ? new List() : _libraryManager.GetPeople(item); + var people = item.Id.IsEmpty() ? new List() : _libraryManager.GetPeople(item); var directors = people .Where(i => i.IsType(PersonKind.Director)) @@ -2317,7 +2317,7 @@ namespace Jellyfin.LiveTv.EmbyTV { string channelId = seriesTimer.RecordAnyChannel ? null : seriesTimer.ChannelId; - if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.Equals(default)) + if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.IsEmpty()) { if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel)) { @@ -2376,7 +2376,7 @@ namespace Jellyfin.LiveTv.EmbyTV { string channelId = null; - if (!programInfo.ChannelId.Equals(default)) + if (!programInfo.ChannelId.IsEmpty()) { if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel)) { diff --git a/src/Jellyfin.LiveTv/LiveTvDtoService.cs b/src/Jellyfin.LiveTv/LiveTvDtoService.cs index 7c7c26eb4a..55b056d3d8 100644 --- a/src/Jellyfin.LiveTv/LiveTvDtoService.cs +++ b/src/Jellyfin.LiveTv/LiveTvDtoService.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Drawing; @@ -456,7 +457,7 @@ namespace Jellyfin.LiveTv info.Id = timer.ExternalId; } - if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId)) + if (!dto.ChannelId.IsEmpty() && string.IsNullOrEmpty(info.ChannelId)) { var channel = _libraryManager.GetItemById(dto.ChannelId); @@ -522,7 +523,7 @@ namespace Jellyfin.LiveTv info.Id = timer.ExternalId; } - if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId)) + if (!dto.ChannelId.IsEmpty() && string.IsNullOrEmpty(info.ChannelId)) { var channel = _libraryManager.GetItemById(dto.ChannelId); diff --git a/src/Jellyfin.LiveTv/LiveTvManager.cs b/src/Jellyfin.LiveTv/LiveTvManager.cs index 71822f3762..bada4249aa 100644 --- a/src/Jellyfin.LiveTv/LiveTvManager.cs +++ b/src/Jellyfin.LiveTv/LiveTvManager.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; +using Jellyfin.Extensions; using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; @@ -150,7 +151,7 @@ namespace Jellyfin.LiveTv public QueryResult GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken) { - var user = query.UserId.Equals(default) + var user = query.UserId.IsEmpty() ? null : _userManager.GetUserById(query.UserId); @@ -1245,7 +1246,7 @@ namespace Jellyfin.LiveTv { cancellationToken.ThrowIfCancellationRequested(); - if (itemId.Equals(default)) + if (itemId.IsEmpty()) { // Somehow some invalid data got into the db. It probably predates the boundary checking continue; @@ -1504,7 +1505,7 @@ namespace Jellyfin.LiveTv public async Task> GetRecordingsAsync(RecordingQuery query, DtoOptions options) { - var user = query.UserId.Equals(default) + var user = query.UserId.IsEmpty() ? null : _userManager.GetUserById(query.UserId); diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs index 4e8aec9f19..2f21495044 100644 --- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -7,6 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.StartupDtos; using Jellyfin.Api.Models.UserDtos; +using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Model.Dto; using Xunit; @@ -56,7 +57,7 @@ namespace Jellyfin.Server.Integration.Tests public static async Task GetRootFolderDtoAsync(HttpClient client, Guid userId = default) { - if (userId.Equals(default)) + if (userId.IsEmpty()) { var userDto = await GetUserDtoAsync(client); userId = userDto.Id; From 4962eb5b432c28bc829d915c29becc57db20cf45 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 17:48:56 -0700 Subject: [PATCH 118/132] chore(deps): update github/codeql-action action to v3.23.1 (#10880) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/ci-codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-codeql-analysis.yml b/.github/workflows/ci-codeql-analysis.yml index 4b2673e82d..d8c550e704 100644 --- a/.github/workflows/ci-codeql-analysis.yml +++ b/.github/workflows/ci-codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: dotnet-version: '8.0.x' - name: Initialize CodeQL - uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + uses: github/codeql-action/init@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 with: languages: ${{ matrix.language }} queries: +security-extended - name: Autobuild - uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + uses: github/codeql-action/autobuild@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + uses: github/codeql-action/analyze@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 From a884b1f7869436fb373d71106330ab1db79cee11 Mon Sep 17 00:00:00 2001 From: Gauvino <68083474+Gauvino@users.noreply.github.com> Date: Thu, 18 Jan 2024 02:11:03 +0100 Subject: [PATCH 119/132] Refactor Dockerfile and build (#10603) * Fix fedora * Fix RID Linux * Fix package and image versions * Fix buildling and optimize docker images ``` * Removed find obj * Changed curl command and added gpg * Added to Contributors * Removed apt-transport-https package * Removed RASPI * Update Intel drivers version * Update Dockerfile for CentOS, Fedora, and portable deployments - Changed Jammy docker image to Built-in Jammy Microsoft .NET SDK image - Switched from using "Yum" to "Dnf" for CentOS and Fedora - Added "dnf clean all" and "rm -rf /var/cache/dnf" to the end of CentOS and Fedora Dockerfiles - Added "apt-get clean", "apt-get autoremove", "rm -rf /var/lib/apt/lists/*" to the end of the Debian/Ubuntu Dockerfiles - Added ${DOTNET_VERSION} in every Dockerfile except CentOS/Fedora - Removed previous warning comment for dotnet publish build in parallel - Arranged package installation * Re-arranged Dockerfile package installation * Re-align * Remove curl * Remove curl --- CONTRIBUTORS.md | 3 +- Dockerfile | 68 +++++++++----------- Dockerfile.arm | 56 +++++++--------- Dockerfile.arm64 | 54 ++++++++-------- bump_version | 4 +- debian/rules | 12 ++-- deployment/Dockerfile.centos.amd64 | 29 +++++---- deployment/Dockerfile.debian.amd64 | 17 +++-- deployment/Dockerfile.debian.arm64 | 25 ++++--- deployment/Dockerfile.debian.armhf | 25 ++++--- deployment/Dockerfile.docker.amd64 | 9 ++- deployment/Dockerfile.docker.arm64 | 9 ++- deployment/Dockerfile.docker.armhf | 9 ++- deployment/Dockerfile.fedora.amd64 | 25 ++++--- deployment/Dockerfile.linux.amd64 | 13 +++- deployment/Dockerfile.linux.amd64-musl | 13 +++- deployment/Dockerfile.linux.arm64 | 13 +++- deployment/Dockerfile.linux.armhf | 13 +++- deployment/Dockerfile.linux.musl-linux-arm64 | 15 +++-- deployment/Dockerfile.macos.amd64 | 13 +++- deployment/Dockerfile.macos.arm64 | 13 +++- deployment/Dockerfile.portable | 13 +++- deployment/Dockerfile.ubuntu.amd64 | 19 +++--- deployment/Dockerfile.ubuntu.arm64 | 55 ++++++++-------- deployment/Dockerfile.ubuntu.armhf | 55 ++++++++-------- deployment/Dockerfile.windows.amd64 | 13 +++- deployment/build.centos.amd64 | 6 +- deployment/build.debian.amd64 | 2 +- deployment/build.debian.arm64 | 2 +- deployment/build.debian.armhf | 2 +- deployment/build.fedora.amd64 | 4 +- deployment/build.linux.amd64 | 8 +-- deployment/build.linux.amd64-musl | 8 +-- deployment/build.linux.arm64 | 8 +-- deployment/build.linux.armhf | 8 +-- deployment/build.linux.musl-linux-arm64 | 8 +-- deployment/build.macos.amd64 | 8 +-- deployment/build.macos.arm64 | 8 +-- deployment/build.portable | 8 +-- deployment/build.ubuntu.amd64 | 2 +- deployment/build.ubuntu.arm64 | 2 +- deployment/build.ubuntu.armhf | 2 +- deployment/build.windows.amd64 | 22 +++---- fedora/README.md | 12 ++-- fedora/jellyfin.spec | 16 +---- 45 files changed, 402 insertions(+), 327 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 4e45fd24ad..457f59e0f6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -172,9 +172,10 @@ - [tallbl0nde](https://github.com/tallbl0nde) - [sleepycatcoding](https://github.com/sleepycatcoding) - [scampower3](https://github.com/scampower3) - - [Chris-Codes-It] (https://github.com/Chris-Codes-It) + - [Chris-Codes-It](https://github.com/Chris-Codes-It) - [Pithaya](https://github.com/Pithaya) - [Çağrı Sakaoğlu](https://github.com/ilovepilav) + - [Gauvino](https://github.com/Gauvino) # Emby Contributors diff --git a/Dockerfile b/Dockerfile index d3f10cd12e..550c3203d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,72 +8,68 @@ FROM node:20-alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ + && apk del curl \ && cd jellyfin-web-* \ && npm ci --no-audit --unsafe-perm \ && npm run build:production \ && mv dist /dist -FROM debian:stable-slim as app +FROM debian:bookworm-slim as app # https://askubuntu.com/questions/972516/debian-frontend-environment-variable ARG DEBIAN_FRONTEND="noninteractive" # http://stackoverflow.com/questions/48162574/ddg#49462622 ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_VISIBLE_DEVICES="all" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" -# https://github.com/intel/compute-runtime/releases -ARG GMMLIB_VERSION=22.0.2 -ARG IGC_VERSION=1.0.10395 -ARG NEO_VERSION=22.08.22549 -ARG LEVEL_ZERO_VERSION=1.3.22549 +ENV JELLYFIN_DATA_DIR=/config +ENV JELLYFIN_CACHE_DIR=/cache + +# https://github.com/intel/compute-runtime/releases +ARG GMMLIB_VERSION=22.3.11.ci17757293 +ARG IGC_VERSION=1.0.15136.22 +ARG NEO_VERSION=23.39.27427.23 +ARG LEVEL_ZERO_VERSION=1.3.27427.23 -# Install dependencies: -# mesa-va-drivers: needed for AMD VAAPI. Mesa >= 20.1 is required for HEVC transcoding. -# curl: healthcheck RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget curl \ - && wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \ + && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \ && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - mesa-va-drivers \ - jellyfin-ffmpeg5 \ - openssl \ - locales \ + && apt-get install --no-install-recommends --no-install-suggests -y mesa-va-drivers jellyfin-ffmpeg6 openssl locales \ # Intel VAAPI Tone mapping dependencies: # Prefer NEO to Beignet since the latter one doesn't support Comet Lake or newer for now. # Do not use the intel-opencl-icd package from repo since they will not build with RELEASE_WITH_REGKEYS enabled. && mkdir intel-compute-runtime \ && cd intel-compute-runtime \ - && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-gmmlib_${GMMLIB_VERSION}_amd64.deb \ - && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \ - && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \ - && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}_amd64.deb \ - && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \ + && curl -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \ + -LO https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \ + -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \ + -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}_amd64.deb \ + -LO https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libigdgmm12_${GMMLIB_VERSION}_amd64.deb \ && dpkg -i *.deb \ && cd .. \ && rm -rf intel-compute-runtime \ - && apt-get remove gnupg wget -y \ + && apt-get remove gnupg -y \ && apt-get clean autoclean -y \ && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /cache /config /media \ - && chmod 777 /cache /config /media \ + && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ + && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen -# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder WORKDIR /repo COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# because of changes in docker and systemd we need to not build in parallel at the moment -# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none + +RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none FROM app @@ -83,11 +79,9 @@ COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web EXPOSE 8096 -VOLUME /cache /config -ENTRYPOINT ["./jellyfin/jellyfin", \ - "--datadir", "/config", \ - "--cachedir", "/cache", \ - "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"] +VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} +ENTRYPOINT [ "./jellyfin/jellyfin", \ + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ] HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \ - CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 + CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 diff --git a/Dockerfile.arm b/Dockerfile.arm index db1acc838a..07039e43b5 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -4,64 +4,58 @@ # https://github.com/multiarch/qemu-user-static#binfmt_misc-register ARG DOTNET_VERSION=8.0 - FROM node:20-alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ + && apk del curl \ && cd jellyfin-web-* \ && npm ci --no-audit --unsafe-perm \ && npm run build:production \ && mv dist /dist FROM multiarch/qemu-user-static:x86_64-arm as qemu -FROM arm32v7/debian:stable-slim as app +FROM arm32v7/debian:bookworm-slim as app # https://askubuntu.com/questions/972516/debian-frontend-environment-variable ARG DEBIAN_FRONTEND="noninteractive" # http://stackoverflow.com/questions/48162574/ddg#49462622 ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_VISIBLE_DEVICES="all" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" +ENV JELLYFIN_DATA_DIR=/config +ENV JELLYFIN_CACHE_DIR=/cache + COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin -# curl: setup & healthcheck RUN apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \ - curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \ - curl -ks https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \ - echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \ - echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \ - apt-get update && \ - apt-get install --no-install-recommends --no-install-suggests -y \ - jellyfin-ffmpeg \ - libssl-dev \ - libfontconfig1 \ - libfreetype6 \ - vainfo \ - libva2 \ - locales \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \ + && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \ + && curl -fsSL https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | gpg --dearmor -o /etc/apt/trusted.gpg.d/ubuntu-jellyfin.gpg \ + && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + jellyfin-ffmpeg6 libssl-dev libfontconfig1 \ + libfreetype6 vainfo libva2 locales \ && apt-get remove gnupg -y \ && apt-get clean autoclean -y \ && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /cache /config /media \ - && chmod 777 /cache /config /media \ + && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ + && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen -# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder WORKDIR /repo COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# Discard objs - may cause failures if exists -RUN find . -type d -name obj | xargs -r rm -r -# Build + RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none FROM app @@ -72,11 +66,9 @@ COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web EXPOSE 8096 -VOLUME /cache /config -ENTRYPOINT ["./jellyfin/jellyfin", \ - "--datadir", "/config", \ - "--cachedir", "/cache", \ - "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"] +VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} +ENTRYPOINT [ "/jellyfin/jellyfin", \ + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ] HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \ - CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 + CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 3eb5f45fc4..54023794fc 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -4,58 +4,58 @@ # https://github.com/multiarch/qemu-user-static#binfmt_misc-register ARG DOTNET_VERSION=8.0 - FROM node:20-alpine as web-builder ARG JELLYFIN_WEB_VERSION=master RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3 \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ + && apk del curl \ && cd jellyfin-web-* \ && npm ci --no-audit --unsafe-perm \ && npm run build:production \ && mv dist /dist FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu -FROM arm64v8/debian:stable-slim as app +FROM arm64v8/debian:bookworm-slim as app # https://askubuntu.com/questions/972516/debian-frontend-environment-variable ARG DEBIAN_FRONTEND="noninteractive" # http://stackoverflow.com/questions/48162574/ddg#49462622 ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +ENV NVIDIA_VISIBLE_DEVICES="all" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" +ENV JELLYFIN_DATA_DIR=/config +ENV JELLYFIN_CACHE_DIR=/cache + COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin -# curl: healcheck -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ - ffmpeg \ - libssl-dev \ - ca-certificates \ - libfontconfig1 \ - libfreetype6 \ - libomxil-bellagio0 \ - libomxil-bellagio-bin \ - locales \ - curl \ +RUN apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl \ + && curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/debian-jellyfin.gpg \ + && curl -fsSL https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | gpg --dearmor -o /etc/apt/trusted.gpg.d/ubuntu-jellyfin.gpg \ + && echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + jellyfin-ffmpeg6 locales libssl-dev libfontconfig1 \ + libfreetype6 libomxil-bellagio0 libomxil-bellagio-bin \ + && apt-get remove gnupg -y \ && apt-get clean autoclean -y \ && apt-get autoremove -y \ && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /cache /config /media \ - && chmod 777 /cache /config /media \ + && mkdir -p ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ + && chmod 777 ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen -# ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder WORKDIR /repo COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# Discard objs - may cause failures if exists -RUN find . -type d -name obj | xargs -r rm -r -# Build + RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none FROM app @@ -66,11 +66,9 @@ COPY --from=builder /jellyfin /jellyfin COPY --from=web-builder /dist /jellyfin/jellyfin-web EXPOSE 8096 -VOLUME /cache /config -ENTRYPOINT ["./jellyfin/jellyfin", \ - "--datadir", "/config", \ - "--cachedir", "/cache", \ - "--ffmpeg", "/usr/bin/ffmpeg"] +VOLUME ${JELLYFIN_DATA_DIR} ${JELLYFIN_CACHE_DIR} +ENTRYPOINT [ "/jellyfin/jellyfin", \ + "--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg" ] HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \ - CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 + CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1 diff --git a/bump_version b/bump_version index 41d27f5c8a..dd55e62c79 100755 --- a/bump_version +++ b/bump_version @@ -21,7 +21,7 @@ fi shared_version_file="./SharedVersion.cs" build_file="./build.yaml" # csproj files for nuget packages -jellyfin_subprojects=( +jellyfin_subprojects=( MediaBrowser.Common/MediaBrowser.Common.csproj Jellyfin.Data/Jellyfin.Data.csproj MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -97,7 +97,7 @@ cat ${debian_changelog_file} >> ${debian_changelog_temp} # Move into place mv ${debian_changelog_temp} ${debian_changelog_file} -# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting +# Write out a temporary Dnf changelog with our new stuff prepended and some templated formatting fedora_spec_file="fedora/jellyfin.spec" fedora_changelog_temp="$( mktemp )" fedora_spec_temp_dir="$( mktemp -d )" diff --git a/debian/rules b/debian/rules index 069d48aad1..79cd55a15d 100755 --- a/debian/rules +++ b/debian/rules @@ -7,27 +7,27 @@ HOST_ARCH := $(shell arch) BUILD_ARCH := ${DEB_HOST_MULTIARCH} ifeq ($(HOST_ARCH),x86_64) # Building AMD64 - DOTNETRUNTIME := debian-x64 + DOTNETRUNTIME := linux-x64 ifeq ($(BUILD_ARCH),arm-linux-gnueabihf) # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm + DOTNETRUNTIME := linux-arm endif ifeq ($(BUILD_ARCH),aarch64-linux-gnu) # Cross-building ARM on AMD64 - DOTNETRUNTIME := debian-arm64 + DOTNETRUNTIME := linux-arm64 endif endif ifeq ($(HOST_ARCH),armv7l) # Building ARM - DOTNETRUNTIME := debian-arm + DOTNETRUNTIME := linux-arm endif ifeq ($(HOST_ARCH),arm64) # Building ARM - DOTNETRUNTIME := debian-arm64 + DOTNETRUNTIME := linux-arm64 endif ifeq ($(HOST_ARCH),aarch64) # Building ARM - DOTNETRUNTIME := debian-arm64 + DOTNETRUNTIME := linux-arm64 endif export DH_VERBOSE=1 diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 7c9bbf39e9..3db184f494 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -1,29 +1,36 @@ -FROM centos:7 +FROM quay.io/centos/centos:stream9 + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist ENV IS_DOCKER=YES # Prepare CentOS environment -RUN yum update -yq \ - && yum install -yq epel-release \ - && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget +RUN dnf update -yq \ + && dnf install -yq epel-release \ + && dnf install -yq \ + rpmdevtools libcurl-devel fontconfig-devel \ + freetype-devel openssl-devel glibc-devel \ + libicu-devel git wget dnf-plugins-core \ + && dnf clean all \ + && rm -rf /var/cache/dnf # Install DotNET SDK RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet # Create symlinks and directories RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.amd64 /build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES VOLUME ${SOURCE_DIR}/ diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index d344c59646..da0c9dabd3 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -10,11 +14,14 @@ ENV ARCH=amd64 ENV IS_DOCKER=YES # Prepare Debian build environment -RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ +RUN apt-get update -yq \ + && apt-get install --no-install-recommends -yq \ debhelper gnupg devscripts build-essential mmv \ - libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev libssl-dev \ - libssl1.1 liblttng-ust0 + libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev \ + libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yq \ + && apt-get autoremove -yq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.amd64 /build.sh diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index 8a5411f059..6c4cb816f5 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,23 +15,26 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts build-essential mmv # Prepare the cross-toolchain RUN dpkg --add-architecture arm64 \ - && apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 9 \ - && cd cross-gcc-packages-amd64/cross-gcc-9-arm64 \ - && apt-get install -yqq --no-install-recommends \ - gcc-9-source libstdc++-9-dev-arm64-cross \ + && apt-get update -yqq \ + && apt-get install --no-install-recommends -yqq cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 12 \ + && cd cross-gcc-packages-amd64/cross-gcc-12-arm64 \ + && apt-get install --no-install-recommends -yqq \ + gcc-12-source libstdc++-12-dev-arm64-cross \ binutils-aarch64-linux-gnu bison flex libtool \ gdb sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \ systemtap-sdt-dev autogen expect chrpath zlib1g-dev zip \ libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 \ libcurl4-openssl-dev:arm64 libfontconfig1-dev:arm64 \ - libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust0:arm64 libstdc++-9-dev:arm64 + libfreetype6-dev:arm64 libssl-dev:arm64 liblttng-ust1:arm64 libstdc++-12-dev:arm64 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.arm64 /build.sh diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index e95ba16962..b1fa6cee52 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,24 +15,27 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts build-essential mmv # Prepare the cross-toolchain RUN dpkg --add-architecture armhf \ - && apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 9 \ - && cd cross-gcc-packages-amd64/cross-gcc-9-armhf \ - && apt-get install -yqq --no-install-recommends\ - gcc-9-source libstdc++-9-dev-armhf-cross \ + && apt-get update -yqq \ + && apt-get install --no-install-recommends -yqq cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 12 \ + && cd cross-gcc-packages-amd64/cross-gcc-12-armhf \ + && apt-get install --no-install-recommends -yqq \ + gcc-12-source libstdc++-12-dev-armhf-cross \ binutils-aarch64-linux-gnu bison flex libtool gdb \ sharutils netbase libmpc-dev libmpfr-dev libgmp-dev \ systemtap-sdt-dev autogen expect chrpath zlib1g-dev \ zip binutils-arm-linux-gnueabihf libc6-dev:armhf \ linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \ libfontconfig1-dev:armhf libfreetype6-dev:armhf libssl-dev:armhf \ - liblttng-ust0:armhf libstdc++-9-dev:armhf + liblttng-ust1:armhf libstdc++-12-dev:armhf \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh diff --git a/deployment/Dockerfile.docker.amd64 b/deployment/Dockerfile.docker.amd64 index 1749ca563c..ca16a08fbc 100644 --- a/deployment/Dockerfile.docker.amd64 +++ b/deployment/Dockerfile.docker.amd64 @@ -1,13 +1,12 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin WORKDIR ${SOURCE_DIR} COPY . . - ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# because of changes in docker and systemd we need to not build in parallel at the moment -# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none +RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-x64 -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/Dockerfile.docker.arm64 b/deployment/Dockerfile.docker.arm64 index bbddb61e4d..6e0f7d18e9 100644 --- a/deployment/Dockerfile.docker.arm64 +++ b/deployment/Dockerfile.docker.arm64 @@ -1,13 +1,12 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin WORKDIR ${SOURCE_DIR} COPY . . - ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# because of changes in docker and systemd we need to not build in parallel at the moment -# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none +RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm64 -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/Dockerfile.docker.armhf b/deployment/Dockerfile.docker.armhf index 3de1d68878..44fb705e6d 100644 --- a/deployment/Dockerfile.docker.armhf +++ b/deployment/Dockerfile.docker.armhf @@ -1,13 +1,12 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim ARG SOURCE_DIR=/src ARG ARTIFACT_DIR=/jellyfin WORKDIR ${SOURCE_DIR} COPY . . - ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -# because of changes in docker and systemd we need to not build in parallel at the moment -# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting -RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none +RUN dotnet publish Jellyfin.Server --configuration Release --output="${ARTIFACT_DIR}" --self-contained --runtime linux-arm -p:DebugSymbols=false -p:DebugType=none diff --git a/deployment/Dockerfile.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 66ead37d7d..75a6d1e649 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -1,7 +1,9 @@ FROM fedora:39 + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -9,21 +11,26 @@ ENV IS_DOCKER=YES # Prepare Fedora environment RUN dnf update -yq \ - && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make + && dnf install -yq \ + @buildsys-build rpmdevtools git \ + dnf-plugins-core libcurl-devel fontconfig-devel \ + freetype-devel openssl-devel glibc-devel \ + libicu-devel systemd wget make \ + && dnf clean all \ + && rm -rf /var/cache/dnf # Install DotNET SDK RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - + && mkdir -p dotnet-sdk \ + && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ + && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet # Create symlinks and directories RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.amd64 /build.sh \ - && mkdir -p ${SOURCE_DIR}/SPECS \ - && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ - && mkdir -p ${SOURCE_DIR}/SOURCES \ - && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES + && mkdir -p ${SOURCE_DIR}/SPECS \ + && ln -s ${SOURCE_DIR}/fedora/jellyfin.spec ${SOURCE_DIR}/SPECS/jellyfin.spec \ + && mkdir -p ${SOURCE_DIR}/SOURCES \ + && ln -s ${SOURCE_DIR}/fedora ${SOURCE_DIR}/SOURCES VOLUME ${SOURCE_DIR}/ diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 index 386f7cefe0..6b8de3773f 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.amd64 /build.sh diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl index 56c8773332..49d98da2ac 100644 --- a/deployment/Dockerfile.linux.amd64-musl +++ b/deployment/Dockerfile.linux.amd64-musl @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.amd64-musl /build.sh diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64 index c9692c440a..aba33c8b23 100644 --- a/deployment/Dockerfile.linux.arm64 +++ b/deployment/Dockerfile.linux.arm64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.arm64 /build.sh diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf index 2304615560..247f756150 100644 --- a/deployment/Dockerfile.linux.armhf +++ b/deployment/Dockerfile.linux.armhf @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.armhf /build.sh diff --git a/deployment/Dockerfile.linux.musl-linux-arm64 b/deployment/Dockerfile.linux.musl-linux-arm64 index 240d091869..a6e1ba217e 100644 --- a/deployment/Dockerfile.linux.musl-linux-arm64 +++ b/deployment/Dockerfile.linux.musl-linux-arm64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ - apt-transport-https debhelper gnupg devscripts unzip \ + && apt-get install --no-install-recommends -yqq \ + debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.linux.musl-linux-arm64 /build.sh diff --git a/deployment/Dockerfile.macos.amd64 b/deployment/Dockerfile.macos.amd64 index 1b054dfc47..45980c363e 100644 --- a/deployment/Dockerfile.macos.amd64 +++ b/deployment/Dockerfile.macos.amd64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.amd64 /build.sh diff --git a/deployment/Dockerfile.macos.arm64 b/deployment/Dockerfile.macos.arm64 index 07e18da55a..ee3a813dde 100644 --- a/deployment/Dockerfile.macos.arm64 +++ b/deployment/Dockerfile.macos.arm64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,10 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.macos.arm64 /build.sh diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable index 36135f7a6e..0ab1b19147 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -10,10 +14,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 84fa2028e5..2326d3e852 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -1,7 +1,11 @@ -FROM ubuntu:bionic +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,16 +15,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg wget ca-certificates devscripts \ mmv build-essential libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 - -# Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.amd64 /build.sh diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index ca3aa35085..461a287a18 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -1,7 +1,11 @@ -FROM ubuntu:bionic +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,39 +15,36 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg wget ca-certificates devscripts \ mmv build-essential lsb-release -# Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - # Prepare the cross-toolchain RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ - && dpkg --add-architecture arm64 \ - && apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends cross-gcc-dev \ - && TARGET_LIST="arm64" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-arm64 \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -yqq --no-install-recommends \ - gcc-6-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu \ - bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && echo "deb [arch=arm64] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/arm64.list \ + && dpkg --add-architecture arm64 \ + && apt-get update -yqq \ + && apt-get install --no-install-recommends -yqq cross-gcc-dev \ + && TARGET_LIST="arm64" cross-gcc-gensource 12 \ + && cd cross-gcc-packages-amd64/cross-gcc-12-arm64 \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install --no-install-recommends -yqq \ + gcc-12-source libstdc++6-arm64-cross binutils-aarch64-linux-gnu \ + bison flex libtool gdb sharutils netbase libmpc-dev \ libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev \ zip libc6-dev:arm64 linux-libc-dev:arm64 libgcc1:arm64 libcurl4-openssl-dev:arm64 \ - libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust0:arm64 libstdc++6:arm64 libssl-dev:arm64 + libfontconfig1-dev:arm64 libfreetype6-dev:arm64 liblttng-ust1:arm64 libstdc++6:arm64 libssl-dev:arm64 \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.ubuntu.arm64 /build.sh diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index e52b7fba35..83fe32acf8 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -1,7 +1,11 @@ -FROM ubuntu:bionic +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-jammy + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -11,39 +15,36 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg wget ca-certificates devscripts \ mmv build-essential lsb-release -# Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ - && mkdir -p dotnet-sdk \ - && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ - && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet - # Prepare the cross-toolchain RUN rm /etc/apt/sources.list \ - && export CODENAME="$( lsb_release -c -s )" \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ - && dpkg --add-architecture armhf \ - && apt-get update -yqq \ - && apt-get install -yqq cross-gcc-dev \ - && TARGET_LIST="armhf" cross-gcc-gensource 6 \ - && cd cross-gcc-packages-amd64/cross-gcc-6-armhf \ - && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ - && apt-get install -yqq --no-install-recommends \ - gcc-6-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf \ - bison flex libtool gdb sharutils netbase libcloog-isl-dev libmpc-dev \ + && export CODENAME="$( lsb_release -c -s )" \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/amd64.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME} main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-updates main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-backports main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && echo "deb [arch=armhf] http://ports.ubuntu.com/ ${CODENAME}-security main restricted universe multiverse" >>/etc/apt/sources.list.d/armhf.list \ + && dpkg --add-architecture armhf \ + && apt-get update -yqq \ + && apt-get install --no-install-recommends -yqq cross-gcc-dev \ + && TARGET_LIST="armhf" cross-gcc-gensource 12 \ + && cd cross-gcc-packages-amd64/cross-gcc-12-armhf \ + && ln -fs /usr/share/zoneinfo/America/Toronto /etc/localtime \ + && apt-get install --no-install-recommends -yqq \ + gcc-12-source libstdc++6-armhf-cross binutils-arm-linux-gnueabihf \ + bison flex libtool gdb sharutils netbase libmpc-dev \ libmpfr-dev libgmp-dev systemtap-sdt-dev autogen expect chrpath zlib1g-dev \ zip libc6-dev:armhf linux-libc-dev:armhf libgcc1:armhf libcurl4-openssl-dev:armhf \ - libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust0:armhf libstdc++6:armhf libssl-dev:armhf + libfontconfig1-dev:armhf libfreetype6-dev:armhf liblttng-ust1:armhf libstdc++6:armhf libssl-dev:armhf \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to build script RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.armhf /build.sh diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 index 08587aa7eb..358fb620ac 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -1,7 +1,11 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim +ARG DOTNET_VERSION=8.0 + +FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION}-bookworm-slim + # Docker build arguments ARG SOURCE_DIR=/jellyfin ARG ARTIFACT_DIR=/dist + # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -10,10 +14,13 @@ ENV IS_DOCKER=YES # Prepare Debian build environment RUN apt-get update -yqq \ - && apt-get install -yqq --no-install-recommends \ + && apt-get install --no-install-recommends -yqq \ debhelper gnupg devscripts unzip \ mmv libcurl4-openssl-dev libfontconfig1-dev \ - libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 zip + libfreetype6-dev libssl-dev libssl3 liblttng-ust1 zip \ + && apt-get clean autoclean -yqq \ + && apt-get autoremove -yqq \ + && rm -rf /var/lib/apt/lists/* # Link to docker-build script RUN ln -sf ${SOURCE_DIR}/deployment/build.windows.amd64 /build.sh diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64 index a0ab93e4e0..af73e31533 100755 --- a/deployment/build.centos.amd64 +++ b/deployment/build.centos.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= CentOS/RHEL 7+ amd64 .rpm +#= CentOS/RHEL 8+ amd64 .rpm set -o errexit set -o xtrace @@ -42,7 +42,7 @@ rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi rm -f fedora/jellyfin*.tar.gz @@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then pushd fedora cp -a /tmp/spec.orig jellyfin.spec - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" popd fi diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 1a59d02e91..85776ad6a0 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -37,7 +37,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index e1e30fab4a..d37cc5a64a 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index e3e8ae004e..f3505b1478 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64 index da345ec08a..21859cbf9f 100755 --- a/deployment/build.fedora.amd64 +++ b/deployment/build.fedora.amd64 @@ -42,7 +42,7 @@ rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm mv /root/rpmbuild/RPMS/x86_64/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi rm -f fedora/jellyfin*.tar.gz @@ -51,7 +51,7 @@ if [[ ${IS_DOCKER} == YES ]]; then pushd fedora cp -a /tmp/spec.orig jellyfin.spec - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" popd fi diff --git a/deployment/build.linux.amd64 b/deployment/build.linux.amd64 index c6baa61f6e..2998d2f9e3 100755 --- a/deployment/build.linux.amd64 +++ b/deployment/build.linux.amd64 @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_linux-amd64.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_linux-amd64.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.amd64-musl b/deployment/build.linux.amd64-musl index 6523f8319a..0fa1764650 100755 --- a/deployment/build.linux.amd64-musl +++ b/deployment/build.linux.amd64-musl @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_linux-amd64-musl.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_linux-amd64-musl.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.arm64 b/deployment/build.linux.arm64 index 6d6a8f803a..dc44ca330d 100755 --- a/deployment/build.linux.arm64 +++ b/deployment/build.linux.arm64 @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_linux-arm64.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_linux-arm64.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.armhf b/deployment/build.linux.armhf index 5167dfcb8d..f9de9ff0a3 100755 --- a/deployment/build.linux.armhf +++ b/deployment/build.linux.armhf @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_linux-armhf.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-arm --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_linux-armhf.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.linux.musl-linux-arm64 b/deployment/build.linux.musl-linux-arm64 index 57980314d4..ae9ab010f5 100755 --- a/deployment/build.linux.musl-linux-arm64 +++ b/deployment/build.linux.musl-linux-arm64 @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_linux-arm64-musl.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-musl-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_linux-arm64-musl.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.macos.amd64 b/deployment/build.macos.amd64 index c7711e82c7..81e0f43f62 100755 --- a/deployment/build.macos.amd64 +++ b/deployment/build.macos.amd64 @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_macos-amd64.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_macos-amd64.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.macos.arm64 b/deployment/build.macos.arm64 index b07eaad4e0..0a6f37edea 100755 --- a/deployment/build.macos.arm64 +++ b/deployment/build.macos.arm64 @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-arm64 --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true -tar -czf jellyfin-server_${version}_macos-arm64.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-arm64 --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +tar -czf jellyfin-server_"${version}"_macos-arm64.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.portable b/deployment/build.portable index ec151d2956..fad14fccfc 100755 --- a/deployment/build.portable +++ b/deployment/build.portable @@ -16,16 +16,16 @@ else fi # Build archives -dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_${version}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=false -tar -czf jellyfin-server_${version}_portable.tar.gz -C dist jellyfin-server_${version} -rm -rf dist/jellyfin-server_${version} +dotnet publish Jellyfin.Server --configuration Release --output dist/jellyfin-server_"${version}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=false +tar -czf jellyfin-server_"${version}"_portable.tar.gz -C dist jellyfin-server_"${version}" +rm -rf dist/jellyfin-server_"${version}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv jellyfin[-_]*.tar.gz "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 17968a6e93..3658676035 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -37,7 +37,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index ee7da9bb98..8cf24b955f 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 85c993282e..896486dcd4 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -38,7 +38,7 @@ mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then cp -a /tmp/control.orig debian/control - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/deployment/build.windows.amd64 b/deployment/build.windows.amd64 index 20f976365d..cd07f4e0b2 100755 --- a/deployment/build.windows.amd64 +++ b/deployment/build.windows.amd64 @@ -23,30 +23,30 @@ fi output_dir="dist/jellyfin-server_${version}" # Build binary -dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output ${output_dir}/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output "${output_dir}"/ -p:DebugSymbols=false -p:DebugType=none -p:UseAppHost=true # Prepare addins addin_build_dir="$( mktemp -d )" -wget ${NSSM_URL} -O ${addin_build_dir}/nssm.zip -wget ${FFMPEG_URL} -O ${addin_build_dir}/jellyfin-ffmpeg.zip -unzip ${addin_build_dir}/nssm.zip -d ${addin_build_dir} -cp ${addin_build_dir}/${NSSM_VERSION}/win64/nssm.exe ${output_dir}/nssm.exe -unzip ${addin_build_dir}/jellyfin-ffmpeg.zip -d ${addin_build_dir}/jellyfin-ffmpeg -cp ${addin_build_dir}/jellyfin-ffmpeg/* ${output_dir} -rm -rf ${addin_build_dir} +wget ${NSSM_URL} -O "${addin_build_dir}"/nssm.zip +wget ${FFMPEG_URL} -O "${addin_build_dir}"/jellyfin-ffmpeg.zip +unzip "${addin_build_dir}"/nssm.zip -d "${addin_build_dir}" +cp "${addin_build_dir}"/${NSSM_VERSION}/win64/nssm.exe "${output_dir}"/nssm.exe +unzip "${addin_build_dir}"/jellyfin-ffmpeg.zip -d "${addin_build_dir}"/jellyfin-ffmpeg +cp "${addin_build_dir}"/jellyfin-ffmpeg/* "${output_dir}" +rm -rf "${addin_build_dir}" # Create zip package pushd dist -zip -qr jellyfin-server_${version}.portable.zip jellyfin-server_${version} +zip -qr jellyfin-server_"${version}".portable.zip jellyfin-server_"${version}" popd -rm -rf ${output_dir} +rm -rf "${output_dir}" # Move the artifacts out mkdir -p "${ARTIFACT_DIR}/" mv dist/jellyfin[-_]*.zip "${ARTIFACT_DIR}/" if [[ ${IS_DOCKER} == YES ]]; then - chown -Rc $(stat -c %u:%g "${ARTIFACT_DIR}") "${ARTIFACT_DIR}" + chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi popd diff --git a/fedora/README.md b/fedora/README.md index d449b51c16..6ea87740f2 100644 --- a/fedora/README.md +++ b/fedora/README.md @@ -14,8 +14,10 @@ The RPM package for Fedora/CentOS requires some additional repositories as ffmpe # ffmpeg from RPMfusion free # Fedora $ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm -# CentOS 7 -$ sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm +# CentOS 8 +$ sudo dnf localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm +# CentOS 9 +$ sudo dnf localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-9.noarch.rpm ``` ## Building with dotnet @@ -26,8 +28,10 @@ Jellyfin is build with `--self-contained` so no dotnet required for runtime. # dotnet required for building the RPM # Fedora $ sudo dnf copr enable @dotnet-sig/dotnet -# CentOS -$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm +# CentOS 8 +$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm +# CentOS 9 +$ sudo rpm -Uvh https://packages.microsoft.com/config/rhel/9/packages-microsoft-prod.rpm ``` ## TODO diff --git a/fedora/jellyfin.spec b/fedora/jellyfin.spec index fb9fb2f7da..5327495ad3 100644 --- a/fedora/jellyfin.spec +++ b/fedora/jellyfin.spec @@ -1,10 +1,4 @@ %global debug_package %{nil} -# Set the dotnet runtime -%if 0%{?fedora} -%global dotnet_runtime fedora.%{fedora}-x64 -%else -%global dotnet_runtime centos-x64 -%endif Name: jellyfin Version: 10.9.0 @@ -29,12 +23,6 @@ BuildRequires: libcurl-devel, fontconfig-devel, freetype-devel, openssl-devel, BuildRequires: dotnet-runtime-8.0, dotnet-sdk-8.0 Requires: %{name}-server = %{version}-%{release}, %{name}-web = %{version}-%{release} -# Temporary (hopefully?) fix for https://github.com/jellyfin/jellyfin/issues/7471 -%if 0%{?fedora} >= 36 -%global __requires_exclude ^liblttng-ust\\.so\\.0.*$ -%endif - - %description Jellyfin is a free software media system that puts you in control of managing and streaming your media. @@ -66,14 +54,14 @@ the Jellyfin server to bind to ports 80 and/or 443 for example. export DOTNET_CLI_TELEMETRY_OPTOUT=1 export PATH=$PATH:/usr/local/bin # cannot use --output due to https://github.com/dotnet/sdk/issues/22220 -dotnet publish --configuration Release --self-contained --runtime %{dotnet_runtime} \ +dotnet publish --configuration Release --self-contained --runtime linux-x64 \ -p:DebugSymbols=false -p:DebugType=none Jellyfin.Server %install # Jellyfin files %{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir} -%{__cp} -r Jellyfin.Server/bin/Release/net8.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin +%{__cp} -r Jellyfin.Server/bin/Release/net8.0/linux-x64/publish/* %{buildroot}%{_libdir}/jellyfin %{__install} -D %{SOURCE10} %{buildroot}%{_bindir}/jellyfin sed -i -e 's|/usr/lib64|%{_libdir}|g' %{buildroot}%{_bindir}/jellyfin From 2fa50cceef6dede0b80fc9f528f057553a7928fc Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Wed, 17 Jan 2024 18:21:20 -0700 Subject: [PATCH 120/132] Use NuGetAuthenticate@1 --- .ci/azure-pipelines-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/azure-pipelines-package.yml b/.ci/azure-pipelines-package.yml index 39f98e063e..b0684c0d4c 100644 --- a/.ci/azure-pipelines-package.yml +++ b/.ci/azure-pipelines-package.yml @@ -259,7 +259,7 @@ jobs: publishFeedCredentials: 'NugetOrg' allowPackageConflicts: true # This ignores an error if the version already exists - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 displayName: 'Authenticate to unstable Nuget feed' condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master') From 879ac12d433f94f40b2630d2de39cc0896990374 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:00:58 +0000 Subject: [PATCH 121/132] chore(deps): update actions/upload-artifact action to v4.2.0 --- .github/workflows/ci-openapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 75ec82c5f1..63268001e0 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -25,7 +25,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 + uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 with: name: openapi-head retention-days: 14 @@ -59,7 +59,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@1eb3cb2b3e0f29609092a73eb033bb759a334595 # v4.1.0 + uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 with: name: openapi-base retention-days: 14 From 67b6c4f136fa5bcacb118475eacb140fe40b53d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 04:33:45 +0000 Subject: [PATCH 122/132] chore(deps): update dependency serilog.aspnetcore to v8.0.1 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index bc87f9fc96..687bc19f94 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -57,7 +57,7 @@ - + From 04a063aa8dc131f6e5f7f6bca2d339d3f6bc3c4a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:50:28 +0000 Subject: [PATCH 123/132] chore(deps): update dependency efcoresecondlevelcacheinterceptor to v4.1.2 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 687bc19f94..d6a9921586 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,7 +15,7 @@ - + From 9f945260094f177608e41f80a48e019e01ca1d91 Mon Sep 17 00:00:00 2001 From: jonathan1jansson Date: Thu, 18 Jan 2024 13:43:13 +0000 Subject: [PATCH 124/132] Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 97062deece..1fc3cdbaaa 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -43,7 +43,7 @@ "NameInstallFailed": "{0} installationen misslyckades", "NameSeasonNumber": "Säsong {0}", "NameSeasonUnknown": "Okänd säsong", - "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig att hämta.", + "NewVersionIsAvailable": "En ny version av Jellyfin Server är tillgänglig för nedladdning.", "NotificationOptionApplicationUpdateAvailable": "Ny programversion tillgänglig", "NotificationOptionApplicationUpdateInstalled": "Programuppdatering installerad", "NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats", @@ -74,7 +74,7 @@ "Songs": "Låtar", "StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.", "SubtitleDownloadFailureForItem": "Nerladdning av undertexter för {0} misslyckades", - "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} för {1}", + "SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} till {1}", "Sync": "Synk", "System": "System", "TvShows": "TV-serier", From 94e3fed3c9816012ebfaab6c8dadbef5d003eca9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:23:51 +0000 Subject: [PATCH 125/132] chore(deps): update dependency metabrainz.musicbrainz to v6.1.0 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 687bc19f94..24bed643b1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 1ff23692285597e5f751845674edff87bbe71391 Mon Sep 17 00:00:00 2001 From: hulkhaugen Date: Fri, 19 Jan 2024 19:12:59 +0000 Subject: [PATCH 126/132] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Localization/Core/nb.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 0362c24179..e64252d897 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -32,10 +32,10 @@ "LabelIpAddressValue": "IP-adresse: {0}", "LabelRunningTimeValue": "Spilletid {0}", "Latest": "Siste", - "MessageApplicationUpdated": "Jellyfin-tjeneren har blitt oppdatert", - "MessageApplicationUpdatedTo": "Jellyfin-tjeneren ble oppdatert til {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Tjenerkonfigurasjonsseksjon {0} har blitt oppdatert", - "MessageServerConfigurationUpdated": "Tjenerkonfigurasjon er oppdatert", + "MessageApplicationUpdated": "Jellyfin-serveren har blitt oppdatert", + "MessageApplicationUpdatedTo": "Jellyfin-serveren ble oppdatert til {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurasjonsseksjon {0} har blitt oppdatert", + "MessageServerConfigurationUpdated": "Serverkonfigurasjon har blitt oppdatert", "MixedContent": "Blandet innhold", "Movies": "Filmer", "Music": "Musikk", @@ -43,7 +43,7 @@ "NameInstallFailed": "Installasjonen av {0} mislyktes", "NameSeasonNumber": "Sesong {0}", "NameSeasonUnknown": "Ukjent sesong", - "NewVersionIsAvailable": "En ny versjon av Jellyfin-tjeneren er tilgjengelig for nedlasting.", + "NewVersionIsAvailable": "En ny versjon av Jellyfin Server er tilgjengelig for nedlasting.", "NotificationOptionApplicationUpdateAvailable": "En programvareoppdatering er tilgjengelig", "NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering installert", "NotificationOptionAudioPlayback": "Lydavspilling startet", @@ -55,7 +55,7 @@ "NotificationOptionPluginInstalled": "Programvareutvidelse installert", "NotificationOptionPluginUninstalled": "Programvareutvidelse avinstallert", "NotificationOptionPluginUpdateInstalled": "Programvareutvidelsesoppdatering installert", - "NotificationOptionServerRestartRequired": "Tjeneromstart er nødvendig", + "NotificationOptionServerRestartRequired": "Serveromstart er nødvendig", "NotificationOptionTaskFailed": "Feil under utføring av planlagt oppgave", "NotificationOptionUserLockedOut": "Bruker er utestengt", "NotificationOptionVideoPlayback": "Videoavspilling startet", @@ -70,9 +70,9 @@ "ScheduledTaskFailedWithName": "{0} mislykkes", "ScheduledTaskStartedWithName": "{0} startet", "ServerNameNeedsToBeRestarted": "{0} må startes på nytt", - "Shows": "Program", + "Shows": "Serier", "Songs": "Sanger", - "StartupEmbyServerIsLoading": "Jellyfin-tjener laster. Prøv igjen snart.", + "StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.", "SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}", "SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}", "Sync": "Synkroniser", From 5e375888fc19aebd1348d23b28287524f0f0f23b Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 19 Jan 2024 21:23:38 +0000 Subject: [PATCH 127/132] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emby.Server.Implementations/Localization/Core/nb.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index e64252d897..b6c15d871a 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -5,7 +5,7 @@ "Artists": "Artister", "AuthenticationSucceededWithUserName": "{0} har logget inn", "Books": "Bøker", - "CameraImageUploadedFrom": "Et nytt kamerabilde er lastet opp fra {0}", + "CameraImageUploadedFrom": "Et nytt kamerabilde har blitt lastet opp fra {0}", "Channels": "Kanaler", "ChapterNameValue": "Kapittel {0}", "Collections": "Samlinger", @@ -49,7 +49,7 @@ "NotificationOptionAudioPlayback": "Lydavspilling startet", "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet", "NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp", - "NotificationOptionInstallationFailed": "Installasjonen feilet", + "NotificationOptionInstallationFailed": "Installasjonsfeil", "NotificationOptionNewLibraryContent": "Nytt innhold lagt til", "NotificationOptionPluginError": "Programvareutvidelsesfeil", "NotificationOptionPluginInstalled": "Programvareutvidelse installert", From 30ab5d5e817fe4626cfe3e9e40e0b2443bae948c Mon Sep 17 00:00:00 2001 From: Gauvino <68083474+Gauvino@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:55:01 +0100 Subject: [PATCH 128/132] Fix action building (#10899) * Fix action building * Added required package --- deployment/Dockerfile.centos.amd64 | 6 +++--- deployment/build.centos.amd64 | 12 ++++++------ deployment/build.debian.amd64 | 8 +------- deployment/build.debian.arm64 | 8 +------- deployment/build.debian.armhf | 8 +------- deployment/build.fedora.amd64 | 2 +- deployment/build.ubuntu.amd64 | 8 +------- deployment/build.ubuntu.arm64 | 8 +------- deployment/build.ubuntu.armhf | 8 +------- 9 files changed, 16 insertions(+), 52 deletions(-) diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 3db184f494..af309b0831 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -11,11 +11,11 @@ ENV IS_DOCKER=YES # Prepare CentOS environment RUN dnf update -yq \ - && dnf install -yq epel-release \ && dnf install -yq \ - rpmdevtools libcurl-devel fontconfig-devel \ + @buildsys-build rpmdevtools git \ + dnf-plugins-core libcurl-devel fontconfig-devel \ freetype-devel openssl-devel glibc-devel \ - libicu-devel git wget dnf-plugins-core \ + libicu-devel systemd wget make \ && dnf clean all \ && rm -rf /var/cache/dnf diff --git a/deployment/build.centos.amd64 b/deployment/build.centos.amd64 index af73e31533..26be377f10 100755 --- a/deployment/build.centos.amd64 +++ b/deployment/build.centos.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= CentOS/RHEL 8+ amd64 .rpm +#= CentOS/RHEL 9+ amd64 .rpm set -o errexit set -o xtrace @@ -10,7 +10,7 @@ pushd "${SOURCE_DIR}" if [[ ${IS_DOCKER} == YES ]]; then # Remove BuildRequires for dotnet, since it's installed manually - pushd fedora + pushd centos cp -a jellyfin.spec /tmp/spec.orig sed -i 's/BuildRequires: dotnet/# BuildRequires: dotnet/' jellyfin.spec @@ -20,7 +20,7 @@ fi # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then - pushd fedora + pushd centos PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' ) @@ -35,7 +35,7 @@ EOF fi # Build RPM -make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS +make -f centos/Makefile srpm outdir=/root/rpmbuild/SRPMS rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm # Move the artifacts out @@ -45,10 +45,10 @@ if [[ ${IS_DOCKER} == YES ]]; then chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" fi -rm -f fedora/jellyfin*.tar.gz +rm -f centos/jellyfin*.tar.gz if [[ ${IS_DOCKER} == YES ]]; then - pushd fedora + pushd centos cp -a /tmp/spec.orig jellyfin.spec chown -Rc "$(stat -c %u:%g "${ARTIFACT_DIR}")" "${ARTIFACT_DIR}" diff --git a/deployment/build.debian.amd64 b/deployment/build.debian.amd64 index 85776ad6a0..350b22a854 100755 --- a/deployment/build.debian.amd64 +++ b/deployment/build.debian.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 10+ amd64 .deb +#= Debian 12+ amd64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian diff --git a/deployment/build.debian.arm64 b/deployment/build.debian.arm64 index d37cc5a64a..0dfca0ab49 100755 --- a/deployment/build.debian.arm64 +++ b/deployment/build.debian.arm64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 10+ arm64 .deb +#= Debian 12+ arm64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian diff --git a/deployment/build.debian.armhf b/deployment/build.debian.armhf index f3505b1478..0ab9e2f9a4 100755 --- a/deployment/build.debian.armhf +++ b/deployment/build.debian.armhf @@ -1,6 +1,6 @@ #!/bin/bash -#= Debian 10+ arm64 .deb +#= Debian 12+ arm64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian diff --git a/deployment/build.fedora.amd64 b/deployment/build.fedora.amd64 index 21859cbf9f..2b4ec2a9c5 100755 --- a/deployment/build.fedora.amd64 +++ b/deployment/build.fedora.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Fedora 29+ amd64 .rpm +#= Fedora 39+ amd64 .rpm set -o errexit set -o xtrace diff --git a/deployment/build.ubuntu.amd64 b/deployment/build.ubuntu.amd64 index 3658676035..6fd87a3aec 100755 --- a/deployment/build.ubuntu.amd64 +++ b/deployment/build.ubuntu.amd64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Ubuntu 18.04+ amd64 .deb +#= Ubuntu 22.04+ amd64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian diff --git a/deployment/build.ubuntu.arm64 b/deployment/build.ubuntu.arm64 index 8cf24b955f..f783941c73 100755 --- a/deployment/build.ubuntu.arm64 +++ b/deployment/build.ubuntu.arm64 @@ -1,6 +1,6 @@ #!/bin/bash -#= Ubuntu 18.04+ arm64 .deb +#= Ubuntu 22.04+ arm64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian diff --git a/deployment/build.ubuntu.armhf b/deployment/build.ubuntu.armhf index 896486dcd4..cde6708c5b 100755 --- a/deployment/build.ubuntu.armhf +++ b/deployment/build.ubuntu.armhf @@ -1,6 +1,6 @@ #!/bin/bash -#= Ubuntu 18.04+ arm64 .deb +#= Ubuntu 22.04+ arm64 .deb set -o errexit set -o xtrace @@ -8,12 +8,6 @@ set -o xtrace # Move to source directory pushd "${SOURCE_DIR}" -if [[ ${IS_DOCKER} == YES ]]; then - # Remove build-dep for dotnet-sdk-8.0, since it's installed manually - cp -a debian/control /tmp/control.orig - sed -i '/dotnet-sdk-8.0,/d' debian/control -fi - # Modify changelog to unstable configuration if IS_UNSTABLE if [[ ${IS_UNSTABLE} == 'yes' ]]; then pushd debian From 7027bc00fc06314929011735e425763779cb4076 Mon Sep 17 00:00:00 2001 From: Mustafa Date: Sun, 21 Jan 2024 15:49:15 -0500 Subject: [PATCH 129/132] Added translation using Weblate (Urdu) --- Emby.Server.Implementations/Localization/Core/ur.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/ur.json diff --git a/Emby.Server.Implementations/Localization/Core/ur.json b/Emby.Server.Implementations/Localization/Core/ur.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ur.json @@ -0,0 +1 @@ +{} From 0cf477b4fed45f80e0deb25dd2f3569c4da7ea50 Mon Sep 17 00:00:00 2001 From: Mustafa Date: Sun, 21 Jan 2024 20:50:26 +0000 Subject: [PATCH 130/132] Translated using Weblate (Urdu) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ur/ --- Emby.Server.Implementations/Localization/Core/ur.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ur.json b/Emby.Server.Implementations/Localization/Core/ur.json index 0967ef424b..3766830413 100644 --- a/Emby.Server.Implementations/Localization/Core/ur.json +++ b/Emby.Server.Implementations/Localization/Core/ur.json @@ -1 +1,3 @@ -{} +{ + "Books": "کتابیں" +} From e2b2399ea60c549fc8285bf8c8a9651a1e8ba635 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:05:50 +0000 Subject: [PATCH 131/132] chore(deps): update actions/upload-artifact action to v4.3.0 --- .github/workflows/ci-openapi.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-openapi.yml b/.github/workflows/ci-openapi.yml index 63268001e0..e43160562f 100644 --- a/.github/workflows/ci-openapi.yml +++ b/.github/workflows/ci-openapi.yml @@ -25,7 +25,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: openapi-head retention-days: 14 @@ -59,7 +59,7 @@ jobs: - name: Generate openapi.json run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests" - name: Upload openapi.json - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0 + uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 with: name: openapi-base retention-days: 14 From 989a80e05238d870f9d5b34d142a73e5845635cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:21:05 +0000 Subject: [PATCH 132/132] chore(deps): update dependency efcoresecondlevelcacheinterceptor to v4.2.0 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fe9280965c..dcf1834949 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,7 +15,7 @@ - +