mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-12-26 17:36:52 +00:00
Implement update endpoint
This commit is contained in:
parent
bff37ed13a
commit
c1dbb49315
|
@ -71,56 +71,56 @@ namespace Emby.Server.Implementations.Playlists
|
|||
return GetPlaylistsFolder(userId).GetChildren(user, true).OfType<Playlist>();
|
||||
}
|
||||
|
||||
public async Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options)
|
||||
public async Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest request)
|
||||
{
|
||||
var name = options.Name;
|
||||
var name = request.Name;
|
||||
var folderName = _fileSystem.GetValidFilename(name);
|
||||
var parentFolder = GetPlaylistsFolder(options.UserId);
|
||||
var parentFolder = GetPlaylistsFolder(request.UserId);
|
||||
if (parentFolder is null)
|
||||
{
|
||||
throw new ArgumentException(nameof(parentFolder));
|
||||
}
|
||||
|
||||
if (options.MediaType is null || options.MediaType == MediaType.Unknown)
|
||||
if (request.MediaType is null || request.MediaType == MediaType.Unknown)
|
||||
{
|
||||
foreach (var itemId in options.ItemIdList)
|
||||
foreach (var itemId in request.ItemIdList)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(itemId) ?? throw new ArgumentException("No item exists with the supplied Id");
|
||||
if (item.MediaType != MediaType.Unknown)
|
||||
{
|
||||
options.MediaType = item.MediaType;
|
||||
request.MediaType = item.MediaType;
|
||||
}
|
||||
else if (item is MusicArtist || item is MusicAlbum || item is MusicGenre)
|
||||
{
|
||||
options.MediaType = MediaType.Audio;
|
||||
request.MediaType = MediaType.Audio;
|
||||
}
|
||||
else if (item is Genre)
|
||||
{
|
||||
options.MediaType = MediaType.Video;
|
||||
request.MediaType = MediaType.Video;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item is Folder folder)
|
||||
{
|
||||
options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
|
||||
request.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
|
||||
.Select(i => i.MediaType)
|
||||
.FirstOrDefault(i => i != MediaType.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.MediaType is not null && options.MediaType != MediaType.Unknown)
|
||||
if (request.MediaType is not null && request.MediaType != MediaType.Unknown)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.MediaType is null || options.MediaType == MediaType.Unknown)
|
||||
if (request.MediaType is null || request.MediaType == MediaType.Unknown)
|
||||
{
|
||||
options.MediaType = MediaType.Audio;
|
||||
request.MediaType = MediaType.Audio;
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(options.UserId);
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
var path = Path.Combine(parentFolder.Path, folderName);
|
||||
path = GetTargetPath(path);
|
||||
|
||||
|
@ -133,20 +133,20 @@ namespace Emby.Server.Implementations.Playlists
|
|||
{
|
||||
Name = name,
|
||||
Path = path,
|
||||
OwnerUserId = options.UserId,
|
||||
Shares = options.Users ?? [],
|
||||
OpenAccess = options.Public ?? false
|
||||
OwnerUserId = request.UserId,
|
||||
Shares = request.Users ?? [],
|
||||
OpenAccess = request.Public ?? false
|
||||
};
|
||||
|
||||
playlist.SetMediaType(options.MediaType);
|
||||
playlist.SetMediaType(request.MediaType);
|
||||
parentFolder.AddChild(playlist);
|
||||
|
||||
await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (options.ItemIdList.Count > 0)
|
||||
if (request.ItemIdList.Count > 0)
|
||||
{
|
||||
await AddToPlaylistInternal(playlist.Id, options.ItemIdList, user, new DtoOptions(false)
|
||||
await AddToPlaylistInternal(playlist.Id, request.ItemIdList, user, new DtoOptions(false)
|
||||
{
|
||||
EnableImages = true
|
||||
}).ConfigureAwait(false);
|
||||
|
@ -233,7 +233,7 @@ namespace Emby.Server.Implementations.Playlists
|
|||
// Update the playlist in the repository
|
||||
playlist.LinkedChildren = newLinkedChildren;
|
||||
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
|
||||
// Refresh playlist metadata
|
||||
_providerManager.QueueRefresh(
|
||||
|
@ -262,7 +262,7 @@ namespace Emby.Server.Implementations.Playlists
|
|||
.Select(i => i.Item1)
|
||||
.ToArray();
|
||||
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
|
||||
_providerManager.QueueRefresh(
|
||||
playlist.Id,
|
||||
|
@ -306,7 +306,7 @@ namespace Emby.Server.Implementations.Playlists
|
|||
|
||||
playlist.LinkedChildren = [.. newList];
|
||||
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -530,7 +530,7 @@ namespace Emby.Server.Implementations.Playlists
|
|||
{
|
||||
playlist.OwnerUserId = rankedShares[0].UserId;
|
||||
playlist.Shares = rankedShares.Skip(1).ToArray();
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
}
|
||||
else if (!playlist.OpenAccess)
|
||||
{
|
||||
|
@ -548,12 +548,40 @@ namespace Emby.Server.Implementations.Playlists
|
|||
}
|
||||
}
|
||||
|
||||
public async Task ToggleOpenAccess(Guid playlistId, Guid userId)
|
||||
public async Task UpdatePlaylist(PlaylistUpdateRequest request)
|
||||
{
|
||||
var playlist = GetPlaylist(userId, playlistId);
|
||||
playlist.OpenAccess = !playlist.OpenAccess;
|
||||
var playlist = GetPlaylist(request.UserId, request.Id);
|
||||
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
if (request.Ids is not null)
|
||||
{
|
||||
playlist.LinkedChildren = [];
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
await AddToPlaylistInternal(request.Id, request.Ids, user, new DtoOptions(false)
|
||||
{
|
||||
EnableImages = true
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
playlist = GetPlaylist(request.UserId, request.Id);
|
||||
}
|
||||
|
||||
if (request.Name is not null)
|
||||
{
|
||||
playlist.Name = request.Name;
|
||||
}
|
||||
|
||||
if (request.Users is not null)
|
||||
{
|
||||
playlist.Shares = request.Users;
|
||||
}
|
||||
|
||||
if (request.Public is not null)
|
||||
{
|
||||
playlist.OpenAccess = request.Public.Value;
|
||||
}
|
||||
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task AddToShares(Guid playlistId, Guid userId, PlaylistUserPermissions share)
|
||||
|
@ -568,7 +596,7 @@ namespace Emby.Server.Implementations.Playlists
|
|||
|
||||
shares.Add(share);
|
||||
playlist.Shares = shares;
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task RemoveFromShares(Guid playlistId, Guid userId, PlaylistUserPermissions share)
|
||||
|
@ -577,10 +605,10 @@ namespace Emby.Server.Implementations.Playlists
|
|||
var shares = playlist.Shares.ToList();
|
||||
shares.Remove(share);
|
||||
playlist.Shares = shares;
|
||||
await UpdatePlaylist(playlist).ConfigureAwait(false);
|
||||
await UpdatePlaylistInternal(playlist).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UpdatePlaylist(Playlist playlist)
|
||||
private async Task UpdatePlaylistInternal(Playlist playlist)
|
||||
{
|
||||
await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
|
|
|
@ -100,6 +100,54 @@ public class PlaylistsController : BaseJellyfinApiController
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a playlist.
|
||||
/// </summary>
|
||||
/// <param name="playlistId">The playlist id.</param>
|
||||
/// <param name="updatePlaylistRequest">The <see cref="UpdatePlaylistDto"/> id.</param>
|
||||
/// <response code="204">Playlist updated.</response>
|
||||
/// <response code="401">Unauthorized access.</response>
|
||||
/// <response code="404">Playlist not found.</response>
|
||||
/// <returns>
|
||||
/// A <see cref="Task" /> that represents the asynchronous operation to update a playlist.
|
||||
/// The task result contains an <see cref="OkResult"/> indicating success.
|
||||
/// </returns>
|
||||
[HttpPost("{playlistId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult> UpdatePlaylist(
|
||||
[FromRoute, Required] Guid playlistId,
|
||||
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Disallow)] UpdatePlaylistDto updatePlaylistRequest)
|
||||
{
|
||||
var callingUserId = User.GetUserId();
|
||||
|
||||
var playlist = _playlistManager.GetPlaylist(callingUserId, playlistId);
|
||||
if (playlist is null)
|
||||
{
|
||||
return NotFound("Playlist not found");
|
||||
}
|
||||
|
||||
var isPermitted = playlist.OwnerUserId.Equals(callingUserId)
|
||||
|| playlist.Shares.Any(s => s.CanEdit && s.UserId.Equals(callingUserId));
|
||||
|
||||
if (!isPermitted)
|
||||
{
|
||||
return Unauthorized("Unauthorized access");
|
||||
}
|
||||
|
||||
await _playlistManager.UpdatePlaylist(new PlaylistUpdateRequest
|
||||
{
|
||||
UserId = callingUserId,
|
||||
Id = playlistId,
|
||||
Name = updatePlaylistRequest.Name,
|
||||
Ids = updatePlaylistRequest.Ids,
|
||||
Users = updatePlaylistRequest.Users,
|
||||
Public = updatePlaylistRequest.Public
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a playlist's users.
|
||||
/// </summary>
|
||||
|
@ -131,44 +179,6 @@ public class PlaylistsController : BaseJellyfinApiController
|
|||
return isPermitted ? playlist.Shares.ToList() : Unauthorized("Unauthorized Access");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles public access of a playlist.
|
||||
/// </summary>
|
||||
/// <param name="playlistId">The playlist id.</param>
|
||||
/// <response code="204">Public access toggled.</response>
|
||||
/// <response code="401">Unauthorized access.</response>
|
||||
/// <response code="404">Playlist not found.</response>
|
||||
/// <returns>
|
||||
/// A <see cref="Task" /> that represents the asynchronous operation to toggle public access of a playlist.
|
||||
/// The task result contains an <see cref="OkResult"/> indicating success.
|
||||
/// </returns>
|
||||
[HttpPost("{playlistId}/TogglePublic")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult> TogglePublicAccess(
|
||||
[FromRoute, Required] Guid playlistId)
|
||||
{
|
||||
var callingUserId = User.GetUserId();
|
||||
|
||||
var playlist = _playlistManager.GetPlaylist(callingUserId, playlistId);
|
||||
if (playlist is null)
|
||||
{
|
||||
return NotFound("Playlist not found");
|
||||
}
|
||||
|
||||
var isPermitted = playlist.OwnerUserId.Equals(callingUserId)
|
||||
|| playlist.Shares.Any(s => s.CanEdit && s.UserId.Equals(callingUserId));
|
||||
|
||||
if (!isPermitted)
|
||||
{
|
||||
return Unauthorized("Unauthorized access");
|
||||
}
|
||||
|
||||
await _playlistManager.ToggleOpenAccess(playlistId, callingUserId).ConfigureAwait(false);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify a user to a playlist's users.
|
||||
/// </summary>
|
||||
|
@ -206,7 +216,7 @@ public class PlaylistsController : BaseJellyfinApiController
|
|||
return Unauthorized("Unauthorized access");
|
||||
}
|
||||
|
||||
await _playlistManager.AddToShares(playlistId, callingUserId, new PlaylistUserPermissions(userId.ToString(), canEdit)).ConfigureAwait(false);
|
||||
await _playlistManager.AddToShares(playlistId, callingUserId, new PlaylistUserPermissions(userId, canEdit)).ConfigureAwait(false);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
|
34
Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs
Normal file
34
Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Extensions.Json.Converters;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace Jellyfin.Api.Models.PlaylistDtos;
|
||||
|
||||
/// <summary>
|
||||
/// Updateexisting playlist dto.
|
||||
/// </summary>
|
||||
public class UpdatePlaylistDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the new playlist.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item ids of the playlist.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))]
|
||||
public IReadOnlyList<Guid>? Ids { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playlist users.
|
||||
/// </summary>
|
||||
public IReadOnlyList<PlaylistUserPermissions>? Users { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the playlist is public.
|
||||
/// </summary>
|
||||
public bool? Public { get; set; }
|
||||
}
|
|
@ -57,9 +57,9 @@ internal class FixPlaylistOwner : IMigrationRoutine
|
|||
if (shares.Count > 0)
|
||||
{
|
||||
var firstEditShare = shares.First(x => x.CanEdit);
|
||||
if (firstEditShare is not null && Guid.TryParse(firstEditShare.UserId, out var guid))
|
||||
if (firstEditShare is not null)
|
||||
{
|
||||
playlist.OwnerUserId = guid;
|
||||
playlist.OwnerUserId = firstEditShare.UserId;
|
||||
playlist.Shares = shares.Where(x => x != firstEditShare).ToArray();
|
||||
playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
|
||||
_playlistManager.SavePlaylistFile(playlist);
|
||||
|
|
|
@ -19,6 +19,20 @@ namespace MediaBrowser.Controller.Playlists
|
|||
/// <returns>Playlist.</returns>
|
||||
Playlist GetPlaylist(Guid userId, Guid playlistId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the playlist.
|
||||
/// </summary>
|
||||
/// <param name="request">The <see cref="PlaylistCreationRequest"/>.</param>
|
||||
/// <returns>Task<Playlist>.</returns>
|
||||
Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a playlist.
|
||||
/// </summary>
|
||||
/// <param name="request">The <see cref="PlaylistUpdateRequest"/>.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdatePlaylist(PlaylistUpdateRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the playlists.
|
||||
/// </summary>
|
||||
|
@ -26,14 +40,6 @@ namespace MediaBrowser.Controller.Playlists
|
|||
/// <returns>IEnumerable<Playlist>.</returns>
|
||||
IEnumerable<Playlist> GetPlaylists(Guid userId);
|
||||
|
||||
/// <summary>
|
||||
/// Toggle OpenAccess policy of the playlist.
|
||||
/// </summary>
|
||||
/// <param name="playlistId">The playlist identifier.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task ToggleOpenAccess(Guid playlistId, Guid userId);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a share to the playlist.
|
||||
/// </summary>
|
||||
|
@ -52,13 +58,6 @@ namespace MediaBrowser.Controller.Playlists
|
|||
/// <returns>Task.</returns>
|
||||
Task RemoveFromShares(Guid playlistId, Guid userId, PlaylistUserPermissions share);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the playlist.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task<Playlist>.</returns>
|
||||
Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options);
|
||||
|
||||
/// <summary>
|
||||
/// Adds to playlist.
|
||||
/// </summary>
|
||||
|
|
|
@ -252,7 +252,7 @@ namespace MediaBrowser.Controller.Playlists
|
|||
return false;
|
||||
}
|
||||
|
||||
return shares.Any(share => Guid.TryParse(share.UserId, out var id) && id.Equals(userId));
|
||||
return shares.Any(s => s.UserId.Equals(userId));
|
||||
}
|
||||
|
||||
public override bool IsVisibleStandalone(User user)
|
||||
|
|
|
@ -862,9 +862,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|||
}
|
||||
|
||||
// This is valid
|
||||
if (!string.IsNullOrWhiteSpace(userId))
|
||||
if (!string.IsNullOrWhiteSpace(userId) && Guid.TryParse(userId, out var guid))
|
||||
{
|
||||
return new PlaylistUserPermissions(userId, canEdit);
|
||||
return new PlaylistUserPermissions(guid, canEdit);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -420,19 +420,16 @@ namespace MediaBrowser.LocalMetadata.Savers
|
|||
|
||||
foreach (var share in item.Shares)
|
||||
{
|
||||
if (share.UserId is not null)
|
||||
{
|
||||
await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false);
|
||||
await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false);
|
||||
|
||||
await writer.WriteElementStringAsync(null, "UserId", null, share.UserId).ConfigureAwait(false);
|
||||
await writer.WriteElementStringAsync(
|
||||
null,
|
||||
"CanEdit",
|
||||
null,
|
||||
share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false);
|
||||
await writer.WriteElementStringAsync(null, "UserId", null, share.UserId.ToString()).ConfigureAwait(false);
|
||||
await writer.WriteElementStringAsync(
|
||||
null,
|
||||
"CanEdit",
|
||||
null,
|
||||
share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false);
|
||||
|
||||
await writer.WriteEndElementAsync().ConfigureAwait(false);
|
||||
}
|
||||
await writer.WriteEndElementAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await writer.WriteEndElementAsync().ConfigureAwait(false);
|
||||
|
|
41
MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs
Normal file
41
MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Model.Playlists;
|
||||
|
||||
/// <summary>
|
||||
/// A playlist creation request.
|
||||
/// </summary>
|
||||
public class PlaylistUpdateRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id of the playlist.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id of the user updating the playlist.
|
||||
/// </summary>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the playlist.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item ids to add to the playlist.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Guid>? Ids { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the playlist users.
|
||||
/// </summary>
|
||||
public IReadOnlyList<PlaylistUserPermissions>? Users { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the playlist is public.
|
||||
/// </summary>
|
||||
public bool? Public { get; set; }
|
||||
}
|
Loading…
Reference in a new issue