Merge pull request #9253 from Bond-009/nullref
This commit is contained in:
commit
5e074ac945
|
@ -2,6 +2,7 @@
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.SyncPlay;
|
using MediaBrowser.Controller.SyncPlay;
|
||||||
|
@ -47,6 +48,10 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy
|
||||||
|
|
||||||
var userId = context.User.GetUserId();
|
var userId = context.User.GetUserId();
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
|
if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess)
|
||||||
{
|
{
|
||||||
|
|
|
@ -99,12 +99,17 @@ public class ImageController : BaseJellyfinApiController
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromQuery] int? index = null)
|
[FromQuery] int? index = null)
|
||||||
{
|
{
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
||||||
{
|
{
|
||||||
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = _userManager.GetUserById(userId);
|
|
||||||
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
await using (memoryStream.ConfigureAwait(false))
|
await using (memoryStream.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
@ -148,12 +153,17 @@ public class ImageController : BaseJellyfinApiController
|
||||||
[FromRoute, Required] ImageType imageType,
|
[FromRoute, Required] ImageType imageType,
|
||||||
[FromRoute] int index)
|
[FromRoute] int index)
|
||||||
{
|
{
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
||||||
{
|
{
|
||||||
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = _userManager.GetUserById(userId);
|
|
||||||
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||||
await using (memoryStream.ConfigureAwait(false))
|
await using (memoryStream.ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -241,7 +242,7 @@ public class ItemsController : BaseJellyfinApiController
|
||||||
var isApiKey = User.GetIsApiKey();
|
var isApiKey = User.GetIsApiKey();
|
||||||
// if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
|
// if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
|
||||||
var user = !isApiKey && userId.HasValue && !userId.Value.Equals(default)
|
var user = !isApiKey && userId.HasValue && !userId.Value.Equals(default)
|
||||||
? _userManager.GetUserById(userId.Value)
|
? _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException()
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// beyond this point, we're either using an api key or we have a valid user
|
// beyond this point, we're either using an api key or we have a valid user
|
||||||
|
@ -815,6 +816,11 @@ public class ItemsController : BaseJellyfinApiController
|
||||||
[FromQuery] bool excludeActiveSessions = false)
|
[FromQuery] bool excludeActiveSessions = false)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var parentIdGuid = parentId ?? Guid.Empty;
|
var parentIdGuid = parentId ?? Guid.Empty;
|
||||||
var dtoOptions = new DtoOptions { Fields = fields }
|
var dtoOptions = new DtoOptions { Fields = fields }
|
||||||
.AddClientFields(User)
|
.AddClientFields(User)
|
||||||
|
|
|
@ -283,6 +283,11 @@ public class LibraryController : BaseJellyfinApiController
|
||||||
userId,
|
userId,
|
||||||
inheritFromParent);
|
inheritFromParent);
|
||||||
|
|
||||||
|
if (themeSongs.Result is NotFoundObjectResult || themeVideos.Result is NotFoundObjectResult)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
return new AllThemeMediaResult
|
return new AllThemeMediaResult
|
||||||
{
|
{
|
||||||
ThemeSongsResult = themeSongs?.Value,
|
ThemeSongsResult = themeSongs?.Value,
|
||||||
|
@ -452,6 +457,10 @@ public class LibraryController : BaseJellyfinApiController
|
||||||
if (user is not null)
|
if (user is not null)
|
||||||
{
|
{
|
||||||
parent = TranslateParentItem(parent, user);
|
parent = TranslateParentItem(parent, user);
|
||||||
|
if (parent is null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
|
baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
|
||||||
|
@ -672,6 +681,11 @@ public class LibraryController : BaseJellyfinApiController
|
||||||
: _libraryManager.GetUserRootFolder())
|
: _libraryManager.GetUserRootFolder())
|
||||||
: _libraryManager.GetItemById(itemId);
|
: _libraryManager.GetItemById(itemId);
|
||||||
|
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (item is Episode || (item is IItemByName && item is not MusicArtist))
|
if (item is Episode || (item is IItemByName && item is not MusicArtist))
|
||||||
{
|
{
|
||||||
return new QueryResult<BaseItemDto>();
|
return new QueryResult<BaseItemDto>();
|
||||||
|
|
|
@ -1211,7 +1211,7 @@ public class LiveTvController : BaseJellyfinApiController
|
||||||
|
|
||||||
private async Task AssertUserCanManageLiveTv()
|
private async Task AssertUserCanManageLiveTv()
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(User.GetUserId());
|
var user = _userManager.GetUserById(User.GetUserId()) ?? throw new ResourceNotFoundException();
|
||||||
var session = await _sessionManager.LogSessionActivity(
|
var session = await _sessionManager.LogSessionActivity(
|
||||||
User.GetClient(),
|
User.GetClient(),
|
||||||
User.GetVersion(),
|
User.GetVersion(),
|
||||||
|
|
|
@ -158,6 +158,11 @@ public class MusicGenresController : BaseJellyfinApiController
|
||||||
item = _libraryManager.GetMusicGenre(genreName);
|
item = _libraryManager.GetMusicGenre(genreName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (userId.HasValue && !userId.Value.Equals(default))
|
if (userId.HasValue && !userId.Value.Equals(default))
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId.Value);
|
var user = _userManager.GetUserById(userId.Value);
|
||||||
|
|
|
@ -77,6 +77,11 @@ public class PlaystateController : BaseJellyfinApiController
|
||||||
[FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed)
|
[FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||||
|
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
@ -89,6 +94,11 @@ public class PlaystateController : BaseJellyfinApiController
|
||||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||||
{
|
{
|
||||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||||
|
if (additionalUser is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
UpdatePlayedStatus(additionalUser, item, true, datePlayed);
|
UpdatePlayedStatus(additionalUser, item, true, datePlayed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +119,11 @@ public class PlaystateController : BaseJellyfinApiController
|
||||||
public async Task<ActionResult<UserItemDataDto>> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
public async Task<ActionResult<UserItemDataDto>> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
|
||||||
|
@ -121,6 +136,11 @@ public class PlaystateController : BaseJellyfinApiController
|
||||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||||
{
|
{
|
||||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||||
|
if (additionalUser is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
UpdatePlayedStatus(additionalUser, item, false, null);
|
UpdatePlayedStatus(additionalUser, item, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,10 @@ public class SessionController : BaseJellyfinApiController
|
||||||
result = result.Where(i => i.SupportsRemoteControl);
|
result = result.Where(i => i.SupportsRemoteControl);
|
||||||
|
|
||||||
var user = _userManager.GetUserById(controllableByUserId.Value);
|
var user = _userManager.GetUserById(controllableByUserId.Value);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
|
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
|
||||||
{
|
{
|
||||||
|
|
|
@ -147,6 +147,11 @@ public class UserController : BaseJellyfinApiController
|
||||||
public async Task<ActionResult> DeleteUser([FromRoute, Required] Guid userId)
|
public async Task<ActionResult> DeleteUser([FromRoute, Required] Guid userId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false);
|
await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false);
|
||||||
await _userManager.DeleteUserAsync(userId).ConfigureAwait(false);
|
await _userManager.DeleteUserAsync(userId).ConfigureAwait(false);
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
@ -281,8 +286,8 @@ public class UserController : BaseJellyfinApiController
|
||||||
{
|
{
|
||||||
var success = await _userManager.AuthenticateUser(
|
var success = await _userManager.AuthenticateUser(
|
||||||
user.Username,
|
user.Username,
|
||||||
request.CurrentPw,
|
request.CurrentPw ?? string.Empty,
|
||||||
request.CurrentPw,
|
request.CurrentPw ?? string.Empty,
|
||||||
HttpContext.GetNormalizedRemoteIp().ToString(),
|
HttpContext.GetNormalizedRemoteIp().ToString(),
|
||||||
false).ConfigureAwait(false);
|
false).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -292,7 +297,7 @@ public class UserController : BaseJellyfinApiController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
|
await _userManager.ChangePassword(user, request.NewPw ?? string.Empty).ConfigureAwait(false);
|
||||||
|
|
||||||
var currentToken = User.GetToken();
|
var currentToken = User.GetToken();
|
||||||
|
|
||||||
|
@ -338,7 +343,7 @@ public class UserController : BaseJellyfinApiController
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _userManager.ChangeEasyPassword(user, request.NewPw, request.NewPassword).ConfigureAwait(false);
|
await _userManager.ChangeEasyPassword(user, request.NewPw ?? string.Empty, request.NewPassword ?? string.Empty).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
|
@ -362,13 +367,17 @@ public class UserController : BaseJellyfinApiController
|
||||||
[FromRoute, Required] Guid userId,
|
[FromRoute, Required] Guid userId,
|
||||||
[FromBody, Required] UserDto updateUser)
|
[FromBody, Required] UserDto updateUser)
|
||||||
{
|
{
|
||||||
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
|
if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
|
||||||
{
|
{
|
||||||
return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
|
return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = _userManager.GetUserById(userId);
|
|
||||||
|
|
||||||
if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal))
|
if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
|
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
|
||||||
|
@ -398,6 +407,10 @@ public class UserController : BaseJellyfinApiController
|
||||||
[FromBody, Required] UserPolicy newPolicy)
|
[FromBody, Required] UserPolicy newPolicy)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
// If removing admin access
|
// If removing admin access
|
||||||
if (!newPolicy.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
|
if (!newPolicy.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
|
||||||
|
|
|
@ -79,10 +79,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
public async Task<ActionResult<BaseItemDto>> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
public async Task<ActionResult<BaseItemDto>> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var item = itemId.Equals(default)
|
var item = itemId.Equals(default)
|
||||||
? _libraryManager.GetUserRootFolder()
|
? _libraryManager.GetUserRootFolder()
|
||||||
: _libraryManager.GetItemById(itemId);
|
: _libraryManager.GetItemById(itemId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
|
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -102,6 +110,11 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
public ActionResult<BaseItemDto> GetRootFolder([FromRoute, Required] Guid userId)
|
public ActionResult<BaseItemDto> GetRootFolder([FromRoute, Required] Guid userId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var item = _libraryManager.GetUserRootFolder();
|
var item = _libraryManager.GetUserRootFolder();
|
||||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||||
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||||
|
@ -119,10 +132,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetIntros([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetIntros([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var item = itemId.Equals(default)
|
var item = itemId.Equals(default)
|
||||||
? _libraryManager.GetUserRootFolder()
|
? _libraryManager.GetUserRootFolder()
|
||||||
: _libraryManager.GetItemById(itemId);
|
: _libraryManager.GetItemById(itemId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
|
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
|
||||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||||
|
@ -200,10 +221,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
public ActionResult<IEnumerable<BaseItemDto>> GetLocalTrailers([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
public ActionResult<IEnumerable<BaseItemDto>> GetLocalTrailers([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var item = itemId.Equals(default)
|
var item = itemId.Equals(default)
|
||||||
? _libraryManager.GetUserRootFolder()
|
? _libraryManager.GetUserRootFolder()
|
||||||
: _libraryManager.GetItemById(itemId);
|
: _libraryManager.GetItemById(itemId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||||
|
|
||||||
|
@ -230,10 +259,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
public ActionResult<IEnumerable<BaseItemDto>> GetSpecialFeatures([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
public ActionResult<IEnumerable<BaseItemDto>> GetSpecialFeatures([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var item = itemId.Equals(default)
|
var item = itemId.Equals(default)
|
||||||
? _libraryManager.GetUserRootFolder()
|
? _libraryManager.GetUserRootFolder()
|
||||||
: _libraryManager.GetItemById(itemId);
|
: _libraryManager.GetItemById(itemId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||||
|
|
||||||
|
@ -275,6 +312,10 @@ public class UserLibraryController : BaseJellyfinApiController
|
||||||
[FromQuery] bool groupItems = true)
|
[FromQuery] bool groupItems = true)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
if (!isPlayed.HasValue)
|
if (!isPlayed.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
@ -155,7 +155,12 @@ public class VideosController : BaseJellyfinApiController
|
||||||
|
|
||||||
if (video.LinkedAlternateVersions.Length == 0)
|
if (video.LinkedAlternateVersions.Length == 0)
|
||||||
{
|
{
|
||||||
video = (Video)_libraryManager.GetItemById(video.PrimaryVersionId);
|
video = (Video?)_libraryManager.GetItemById(video.PrimaryVersionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (video is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var link in video.GetLinkedAlternateVersions())
|
foreach (var link in video.GetLinkedAlternateVersions())
|
||||||
|
|
|
@ -200,7 +200,7 @@ public class MediaInfoHelper
|
||||||
options.SubtitleStreamIndex = subtitleStreamIndex;
|
options.SubtitleStreamIndex = subtitleStreamIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = _userManager.GetUserById(userId);
|
var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException();
|
||||||
|
|
||||||
if (!enableDirectPlay)
|
if (!enableDirectPlay)
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,6 +81,11 @@ public static class RequestHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = userManager.GetUserById(userId);
|
var user = userManager.GetUserById(userId);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
return user.EnableUserPreferenceAccess;
|
return user.EnableUserPreferenceAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +103,7 @@ public static class RequestHelpers
|
||||||
|
|
||||||
if (session is null)
|
if (session is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Session not found.");
|
throw new ResourceNotFoundException("Session not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
namespace Jellyfin.Api.Models.UserDtos;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Models.UserDtos;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The create user by name request body.
|
/// The create user by name request body.
|
||||||
|
@ -8,7 +10,8 @@ public class CreateUserByName
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the username.
|
/// Gets or sets the username.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Name { get; set; }
|
[Required]
|
||||||
|
required public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the password.
|
/// Gets or sets the password.
|
||||||
|
|
|
@ -11,5 +11,5 @@ public class ForgotPasswordDto
|
||||||
/// Gets or sets the entered username to have its password reset.
|
/// Gets or sets the entered username to have its password reset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public string? EnteredUsername { get; set; }
|
required public string EnteredUsername { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ public class ForgotPasswordPinDto
|
||||||
/// Gets or sets the entered pin to have the password reset.
|
/// Gets or sets the entered pin to have the password reset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public string? Pin { get; set; }
|
required public string Pin { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Data.Events;
|
using Jellyfin.Data.Events;
|
||||||
using Jellyfin.Data.Queries;
|
using Jellyfin.Data.Queries;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Devices;
|
using MediaBrowser.Model.Devices;
|
||||||
|
@ -185,6 +186,10 @@ namespace Jellyfin.Server.Implementations.Devices
|
||||||
if (userId.HasValue)
|
if (userId.HasValue)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(userId.Value);
|
var user = _userManager.GetUserById(userId.Value);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
|
sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
#pragma warning disable CA1002
|
#pragma warning disable CA1002
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -28,7 +27,7 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="owner">The owner.</param>
|
/// <param name="owner">The owner.</param>
|
||||||
/// <returns>BaseItemDto.</returns>
|
/// <returns>BaseItemDto.</returns>
|
||||||
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
|
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the base item dtos.
|
/// Gets the base item dtos.
|
||||||
|
@ -38,7 +37,7 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="owner">The owner.</param>
|
/// <param name="owner">The owner.</param>
|
||||||
/// <returns>The <see cref="IReadOnlyList{T}"/> of <see cref="BaseItemDto"/>.</returns>
|
/// <returns>The <see cref="IReadOnlyList{T}"/> of <see cref="BaseItemDto"/>.</returns>
|
||||||
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
|
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User? user = null, BaseItem? owner = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the item by name dto.
|
/// Gets the item by name dto.
|
||||||
|
@ -48,6 +47,6 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <param name="taggedItems">The list of tagged items.</param>
|
/// <param name="taggedItems">The list of tagged items.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <returns>The item dto.</returns>
|
/// <returns>The item dto.</returns>
|
||||||
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null);
|
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem>? taggedItems, User? user = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -47,14 +45,14 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="id">The id.</param>
|
/// <param name="id">The id.</param>
|
||||||
/// <returns>The user with the specified Id, or <c>null</c> if the user doesn't exist.</returns>
|
/// <returns>The user with the specified Id, or <c>null</c> if the user doesn't exist.</returns>
|
||||||
/// <exception cref="ArgumentException"><c>id</c> is an empty Guid.</exception>
|
/// <exception cref="ArgumentException"><c>id</c> is an empty Guid.</exception>
|
||||||
User GetUserById(Guid id);
|
User? GetUserById(Guid id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the user by.
|
/// Gets the name of the user by.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <returns>User.</returns>
|
/// <returns>User.</returns>
|
||||||
User GetUserByName(string name);
|
User? GetUserByName(string name);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renames the user.
|
/// Renames the user.
|
||||||
|
@ -128,7 +126,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="remoteEndPoint">The remote end point.</param>
|
/// <param name="remoteEndPoint">The remote end point.</param>
|
||||||
/// <returns>UserDto.</returns>
|
/// <returns>UserDto.</returns>
|
||||||
UserDto GetUserDto(User user, string remoteEndPoint = null);
|
UserDto GetUserDto(User user, string? remoteEndPoint = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticates the user.
|
/// Authenticates the user.
|
||||||
|
@ -139,7 +137,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <param name="remoteEndPoint">Remove endpoint to use.</param>
|
/// <param name="remoteEndPoint">Remove endpoint to use.</param>
|
||||||
/// <param name="isUserSession">Specifies if a user session.</param>
|
/// <param name="isUserSession">Specifies if a user session.</param>
|
||||||
/// <returns>User wrapped in awaitable task.</returns>
|
/// <returns>User wrapped in awaitable task.</returns>
|
||||||
Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
|
Task<User?> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts the forgot password process.
|
/// Starts the forgot password process.
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -9,7 +7,7 @@ namespace MediaBrowser.Controller.Library
|
||||||
{
|
{
|
||||||
public static class LibraryManagerExtensions
|
public static class LibraryManagerExtensions
|
||||||
{
|
{
|
||||||
public static BaseItem GetItemById(this ILibraryManager manager, string id)
|
public static BaseItem? GetItemById(this ILibraryManager manager, string id)
|
||||||
{
|
{
|
||||||
return manager.GetItemById(new Guid(id));
|
return manager.GetItemById(new Guid(id));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Models.StartupDtos;
|
using Jellyfin.Api.Models.StartupDtos;
|
||||||
using Jellyfin.Api.Models.UserDtos;
|
using Jellyfin.Api.Models.UserDtos;
|
||||||
using Jellyfin.Extensions.Json;
|
using Jellyfin.Extensions.Json;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Integration.Tests
|
namespace Jellyfin.Server.Integration.Tests
|
||||||
|
@ -43,6 +44,33 @@ namespace Jellyfin.Server.Integration.Tests
|
||||||
return auth!.AccessToken;
|
return auth!.AccessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<UserDto> GetUserDtoAsync(HttpClient client)
|
||||||
|
{
|
||||||
|
using var response = await client.GetAsync("Users/Me").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var userDto = await JsonSerializer.DeserializeAsync<UserDto>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false), JsonDefaults.Options).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(userDto);
|
||||||
|
return userDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<BaseItemDto> GetRootFolderDtoAsync(HttpClient client, Guid userId = default)
|
||||||
|
{
|
||||||
|
if (userId.Equals(default))
|
||||||
|
{
|
||||||
|
var userDto = await GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
userId = userDto.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await client.GetAsync($"Users/{userId}/Items/Root").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||||
|
JsonDefaults.Options).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(rootDto);
|
||||||
|
return rootDto;
|
||||||
|
}
|
||||||
|
|
||||||
public static void AddAuthHeader(this HttpHeaders headers, string accessToken)
|
public static void AddAuthHeader(this HttpHeaders headers, string accessToken)
|
||||||
{
|
{
|
||||||
headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}");
|
headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}");
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Extensions.Json;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public sealed class ItemsControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public ItemsControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetItems_NoApiKeyOrUserId_BadRequest()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.GetAsync("Items").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Users/{0}/Items")]
|
||||||
|
[InlineData("Users/{0}/Items/Resume")]
|
||||||
|
public async Task GetUserItems_NonExistentUserId_NotFound(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Items?userId={0}")]
|
||||||
|
[InlineData("Users/{0}/Items")]
|
||||||
|
[InlineData("Users/{0}/Items/Resume")]
|
||||||
|
public async Task GetItems_UserId_Ok(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id)).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var items = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||||
|
_jsonOptions).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(items);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public sealed class LibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public LibraryControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Items/{0}/File")]
|
||||||
|
[InlineData("Items/{0}/ThemeSongs")]
|
||||||
|
[InlineData("Items/{0}/ThemeVideos")]
|
||||||
|
[InlineData("Items/{0}/ThemeMedia")]
|
||||||
|
[InlineData("Items/{0}/Ancestors")]
|
||||||
|
[InlineData("Items/{0}/Download")]
|
||||||
|
[InlineData("Artists/{0}/Similar")]
|
||||||
|
[InlineData("Items/{0}/Similar")]
|
||||||
|
[InlineData("Albums/{0}/Similar")]
|
||||||
|
[InlineData("Shows/{0}/Similar")]
|
||||||
|
[InlineData("Movies/{0}/Similar")]
|
||||||
|
[InlineData("Trailers/{0}/Similar")]
|
||||||
|
public async Task Get_NonExistentItemId_NotFound(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public sealed class MusicGenreControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public MusicGenreControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MusicGenres_FakeMusicGenre_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.GetAsync("MusicGenres/Fake-MusicGenre").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Priority;
|
|
||||||
|
|
||||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
|
||||||
public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory>
|
public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
{
|
{
|
||||||
private readonly JellyfinApplicationFactory _factory;
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
private static readonly Guid _testUserId = Guid.NewGuid();
|
|
||||||
private static readonly Guid _testItemId = Guid.NewGuid();
|
|
||||||
private static string? _accessToken;
|
private static string? _accessToken;
|
||||||
|
|
||||||
public PlaystateControllerTests(JellyfinApplicationFactory factory)
|
public PlaystateControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
@ -20,31 +15,47 @@ public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<HttpResponseMessage> DeleteUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
|
|
||||||
=> httpClient.DeleteAsync($"Users/{userId}/PlayedItems/{itemId}");
|
|
||||||
|
|
||||||
private Task<HttpResponseMessage> PostUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
|
|
||||||
=> httpClient.PostAsync($"Users/{userId}/PlayedItems/{itemId}", null);
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(0)]
|
public async Task DeleteMarkUnplayedItem_NonExistentUserId_NotFound()
|
||||||
public async Task DeleteMarkUnplayedItem_DoesNotExist_NotFound()
|
|
||||||
{
|
{
|
||||||
var client = _factory.CreateClient();
|
var client = _factory.CreateClient();
|
||||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
using var response = await DeleteUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
|
using var response = await client.DeleteAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(0)]
|
public async Task PostMarkPlayedItem_NonExistentUserId_NotFound()
|
||||||
public async Task PostMarkPlayedItem_DoesNotExist_NotFound()
|
|
||||||
{
|
{
|
||||||
var client = _factory.CreateClient();
|
var client = _factory.CreateClient();
|
||||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
using var response = await PostUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
|
using var response = await client.PostAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeleteMarkUnplayedItem_NonExistentItemId_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
using var response = await client.DeleteAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PostMarkPlayedItem_NonExistentItemId_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
using var response = await client.PostAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
|
||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public class SessionControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public SessionControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSessions_NonExistentUserId_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
using var response = await client.GetAsync($"Session/Sessions?userId={Guid.NewGuid()}").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,16 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||||
Assert.False(users![0].HasConfiguredPassword);
|
Assert.False(users![0].HasConfiguredPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Priority(-1)]
|
||||||
|
public async Task Me_Valid_Success()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
_ = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task New_Valid_Success()
|
public async Task New_Valid_Success()
|
||||||
|
@ -108,13 +118,26 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
||||||
|
|
||||||
var createRequest = new CreateUserByName()
|
var createRequest = new CreateUserByName()
|
||||||
{
|
{
|
||||||
Name = username
|
Name = username!
|
||||||
};
|
};
|
||||||
|
|
||||||
using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false);
|
using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false);
|
||||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task Delete_DoesntExist_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
|
||||||
|
// access token can't be null here as the previous test populated it
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken!);
|
||||||
|
|
||||||
|
using var response = await client.DeleteAsync($"User/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task UpdateUserPassword_Valid_Success()
|
public async Task UpdateUserPassword_Valid_Success()
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Extensions.Json;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
|
using MediaBrowser.Model.Querying;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public sealed class UserLibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public UserLibraryControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetRootFolder_NonExistenUserId_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.GetAsync($"Users/{Guid.NewGuid()}/Items/Root").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetRootFolder_UserId_Valid()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
_ = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Users/{0}/Items/{1}")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/Intros")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/Lyrics")]
|
||||||
|
public async Task GetItem_NonExistenUserId_NotFound(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid(), rootFolderDto.Id)).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Users/{0}/Items/{1}")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/Intros")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/Lyrics")]
|
||||||
|
public async Task GetItem_NonExistentItemId_NotFound(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, Guid.NewGuid())).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetItem_UserIdAndItemId_Valid()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||||
|
_jsonOptions).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(rootDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetIntros_UserIdAndItemId_Valid()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}/Intros").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var rootDto = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||||
|
_jsonOptions).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(rootDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||||
|
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||||
|
public async Task LocalTrailersAndSpecialFeatures_UserIdAndItemId_Valid(string format)
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||||
|
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, rootFolderDto.Id)).ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto[]>(
|
||||||
|
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||||
|
_jsonOptions).ConfigureAwait(false);
|
||||||
|
Assert.NotNull(rootDto);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||||
|
|
||||||
|
public sealed class VideosControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||||
|
{
|
||||||
|
private readonly JellyfinApplicationFactory _factory;
|
||||||
|
private static string? _accessToken;
|
||||||
|
|
||||||
|
public VideosControllerTests(JellyfinApplicationFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeleteAlternateSources_NonExistentItemId_NotFound()
|
||||||
|
{
|
||||||
|
var client = _factory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||||
|
|
||||||
|
var response = await client.DeleteAsync($"Videos/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user