aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2024-04-01 20:43:05 +0200
committerShadowghost <Ghost_of_Stone@web.de>2024-04-01 20:43:05 +0200
commitc1dbb49315f90bf03445a960eb8eace86f1ea6f2 (patch)
tree393fc3f0dd870ee16dfa4506aaff466807d7f200
parentbff37ed13aa9ee0267ee5e1248339c6044fa1b0c (diff)
Implement update endpoint
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs88
-rw-r--r--Jellyfin.Api/Controllers/PlaylistsController.cs74
-rw-r--r--Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs34
-rw-r--r--Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs4
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs29
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs19
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs41
9 files changed, 202 insertions, 93 deletions
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 0e5add635..517ec68dc 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -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);
+
+ 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 UpdatePlaylist(playlist).ConfigureAwait(false);
+ 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);
diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs
index 862e5235e..de4c542d9 100644
--- a/Jellyfin.Api/Controllers/PlaylistsController.cs
+++ b/Jellyfin.Api/Controllers/PlaylistsController.cs
@@ -101,72 +101,82 @@ public class PlaylistsController : BaseJellyfinApiController
}
/// <summary>
- /// Get a playlist's users.
+ /// Updates a playlist.
/// </summary>
/// <param name="playlistId">The playlist id.</param>
- /// <response code="200">Found shares.</response>
+ /// <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 list of <see cref="PlaylistUserPermissions"/> objects.
+ /// A <see cref="Task" /> that represents the asynchronous operation to update a playlist.
+ /// The task result contains an <see cref="OkResult"/> indicating success.
/// </returns>
- [HttpGet("{playlistId}/User")]
+ [HttpPost("{playlistId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public ActionResult<IReadOnlyList<PlaylistUserPermissions>> GetPlaylistUsers(
- [FromRoute, Required] Guid playlistId)
+ public async Task<ActionResult> UpdatePlaylist(
+ [FromRoute, Required] Guid playlistId,
+ [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Disallow)] UpdatePlaylistDto updatePlaylistRequest)
{
- var userId = User.GetUserId();
+ var callingUserId = User.GetUserId();
- var playlist = _playlistManager.GetPlaylist(userId, playlistId);
+ var playlist = _playlistManager.GetPlaylist(callingUserId, playlistId);
if (playlist is null)
{
return NotFound("Playlist not found");
}
- var isPermitted = playlist.OwnerUserId.Equals(userId)
- || playlist.Shares.Any(s => s.CanEdit && s.UserId.Equals(userId));
+ var isPermitted = playlist.OwnerUserId.Equals(callingUserId)
+ || playlist.Shares.Any(s => s.CanEdit && s.UserId.Equals(callingUserId));
- return isPermitted ? playlist.Shares.ToList() : Unauthorized("Unauthorized Access");
+ 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>
- /// Toggles public access of a playlist.
+ /// Get a playlist's users.
/// </summary>
/// <param name="playlistId">The playlist id.</param>
- /// <response code="204">Public access toggled.</response>
+ /// <response code="200">Found shares.</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.
+ /// A list of <see cref="PlaylistUserPermissions"/> objects.
/// </returns>
- [HttpPost("{playlistId}/TogglePublic")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [HttpGet("{playlistId}/User")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task<ActionResult> TogglePublicAccess(
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public ActionResult<IReadOnlyList<PlaylistUserPermissions>> GetPlaylistUsers(
[FromRoute, Required] Guid playlistId)
{
- var callingUserId = User.GetUserId();
+ var userId = User.GetUserId();
- var playlist = _playlistManager.GetPlaylist(callingUserId, playlistId);
+ var playlist = _playlistManager.GetPlaylist(userId, 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);
+ var isPermitted = playlist.OwnerUserId.Equals(userId)
+ || playlist.Shares.Any(s => s.CanEdit && s.UserId.Equals(userId));
- return NoContent();
+ return isPermitted ? playlist.Shares.ToList() : Unauthorized("Unauthorized Access");
}
/// <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();
}
diff --git a/Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs b/Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs
new file mode 100644
index 000000000..93e544eed
--- /dev/null
+++ b/Jellyfin.Api/Models/PlaylistDtos/UpdatePlaylistDto.cs
@@ -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; }
+}
diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs
index 06596c171..3655a610d 100644
--- a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs
+++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs
@@ -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);
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index 1750be619..464620427 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -20,19 +20,25 @@ namespace MediaBrowser.Controller.Playlists
Playlist GetPlaylist(Guid userId, Guid playlistId);
/// <summary>
- /// Gets the playlists.
+ /// Creates the playlist.
/// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <returns>IEnumerable&lt;Playlist&gt;.</returns>
- IEnumerable<Playlist> GetPlaylists(Guid userId);
+ /// <param name="request">The <see cref="PlaylistCreationRequest"/>.</param>
+ /// <returns>Task&lt;Playlist&gt;.</returns>
+ Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest request);
/// <summary>
- /// Toggle OpenAccess policy of the playlist.
+ /// Updates a playlist.
/// </summary>
- /// <param name="playlistId">The playlist identifier.</param>
- /// <param name="userId">The user identifier.</param>
+ /// <param name="request">The <see cref="PlaylistUpdateRequest"/>.</param>
/// <returns>Task.</returns>
- Task ToggleOpenAccess(Guid playlistId, Guid userId);
+ Task UpdatePlaylist(PlaylistUpdateRequest request);
+
+ /// <summary>
+ /// Gets the playlists.
+ /// </summary>
+ /// <param name="userId">The user identifier.</param>
+ /// <returns>IEnumerable&lt;Playlist&gt;.</returns>
+ IEnumerable<Playlist> GetPlaylists(Guid userId);
/// <summary>
/// Adds a share to the playlist.
@@ -53,13 +59,6 @@ namespace MediaBrowser.Controller.Playlists
Task RemoveFromShares(Guid playlistId, Guid userId, PlaylistUserPermissions share);
/// <summary>
- /// Creates the playlist.
- /// </summary>
- /// <param name="options">The options.</param>
- /// <returns>Task&lt;Playlist&gt;.</returns>
- Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options);
-
- /// <summary>
/// Adds to playlist.
/// </summary>
/// <param name="playlistId">The playlist identifier.</param>
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index b948d2e18..747dd9f63 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -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)
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 22ae3f12b..a7e027d94 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -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;
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
index 5a7193079..ee0d10bea 100644
--- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -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);
diff --git a/MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs b/MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs
new file mode 100644
index 000000000..f574e679c
--- /dev/null
+++ b/MediaBrowser.Model/Playlists/PlaylistUpdateRequest.cs
@@ -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; }
+}