Bladeren bron

Fix playlist creation and removal

Shadowghost 2 jaren geleden
bovenliggende
commit
76ae599bd3

+ 36 - 10
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -135,16 +135,8 @@ namespace Emby.Server.Implementations.Playlists
                 {
                 {
                     Name = name,
                     Name = name,
                     Path = path,
                     Path = path,
-                    Shares = new[]
-                    {
-                        new Share
-                        {
-                            UserId = options.UserId.Equals(default)
-                                ? null
-                                : options.UserId.ToString("N", CultureInfo.InvariantCulture),
-                            CanEdit = true
-                        }
-                    }
+                    OwnerUserId = options.UserId,
+                    Shares = options.Shares
                 };
                 };
 
 
                 playlist.SetMediaType(options.MediaType);
                 playlist.SetMediaType(options.MediaType);
@@ -537,5 +529,39 @@ namespace Emby.Server.Implementations.Playlists
             return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)) ??
             return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)) ??
                 _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal));
                 _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal));
         }
         }
+
+        public async Task RemovePlaylists(Guid userId)
+        {
+            var playlists = GetPlaylists(userId);
+            foreach (var playlist in playlists)
+            {
+                // Update owner if shared
+                var rankedShares = playlist.Shares.OrderByDescending(x => x.CanEdit).ToArray();
+                if (rankedShares.Length > 0 && Guid.TryParse(rankedShares[0].UserId, out var guid))
+                {
+                    playlist.OwnerUserId = guid;
+                    playlist.Shares = rankedShares.Skip(1).ToArray();
+                    await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+
+                    if (playlist.IsFile)
+                    {
+                        SavePlaylistFile(playlist);
+                    }
+                }
+                else
+                {
+                    // Remove playlist if not shared
+                    _libraryManager.DeleteItem(
+                        playlist,
+                        new DeleteOptions
+                        {
+                            DeleteFileLocation = false,
+                            DeleteFromExternalProvider = false
+                        },
+                        playlist.GetParent(),
+                        false);
+                }
+            }
+        }
     }
     }
 }
 }

+ 7 - 1
Jellyfin.Api/Controllers/UserController.cs

@@ -15,6 +15,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Controller.QuickConnect;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
@@ -41,6 +42,7 @@ public class UserController : BaseJellyfinApiController
     private readonly IServerConfigurationManager _config;
     private readonly IServerConfigurationManager _config;
     private readonly ILogger _logger;
     private readonly ILogger _logger;
     private readonly IQuickConnect _quickConnectManager;
     private readonly IQuickConnect _quickConnectManager;
+    private readonly IPlaylistManager _playlistManager;
 
 
     /// <summary>
     /// <summary>
     /// Initializes a new instance of the <see cref="UserController"/> class.
     /// Initializes a new instance of the <see cref="UserController"/> class.
@@ -53,6 +55,7 @@ public class UserController : BaseJellyfinApiController
     /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
     /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
     /// <param name="quickConnectManager">Instance of the <see cref="IQuickConnect"/> interface.</param>
     /// <param name="quickConnectManager">Instance of the <see cref="IQuickConnect"/> interface.</param>
+    /// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param>
     public UserController(
     public UserController(
         IUserManager userManager,
         IUserManager userManager,
         ISessionManager sessionManager,
         ISessionManager sessionManager,
@@ -61,7 +64,8 @@ public class UserController : BaseJellyfinApiController
         IAuthorizationContext authContext,
         IAuthorizationContext authContext,
         IServerConfigurationManager config,
         IServerConfigurationManager config,
         ILogger<UserController> logger,
         ILogger<UserController> logger,
-        IQuickConnect quickConnectManager)
+        IQuickConnect quickConnectManager,
+        IPlaylistManager playlistManager)
     {
     {
         _userManager = userManager;
         _userManager = userManager;
         _sessionManager = sessionManager;
         _sessionManager = sessionManager;
@@ -71,6 +75,7 @@ public class UserController : BaseJellyfinApiController
         _config = config;
         _config = config;
         _logger = logger;
         _logger = logger;
         _quickConnectManager = quickConnectManager;
         _quickConnectManager = quickConnectManager;
+        _playlistManager = playlistManager;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -153,6 +158,7 @@ public class UserController : BaseJellyfinApiController
         }
         }
 
 
         await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false);
         await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false);
+        await _playlistManager.RemovePlaylists(userId).ConfigureAwait(false);
         await _userManager.DeleteUserAsync(userId).ConfigureAwait(false);
         await _userManager.DeleteUserAsync(userId).ConfigureAwait(false);
         return NoContent();
         return NoContent();
     }
     }

+ 0 - 11
MediaBrowser.Controller/Entities/IHasShares.cs

@@ -1,11 +0,0 @@
-#nullable disable
-
-#pragma warning disable CA1819, CS1591
-
-namespace MediaBrowser.Controller.Entities
-{
-    public interface IHasShares
-    {
-        Share[] Shares { get; set; }
-    }
-}

+ 0 - 13
MediaBrowser.Controller/Entities/Share.cs

@@ -1,13 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Controller.Entities
-{
-    public class Share
-    {
-        public string UserId { get; set; }
-
-        public bool CanEdit { get; set; }
-    }
-}

+ 8 - 0
MediaBrowser.Controller/Playlists/IPlaylistManager.cs

@@ -56,5 +56,13 @@ namespace MediaBrowser.Controller.Playlists
         /// <param name="newIndex">The new index.</param>
         /// <param name="newIndex">The new index.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task MoveItemAsync(string playlistId, string entryId, int newIndex);
         Task MoveItemAsync(string playlistId, string entryId, int newIndex);
+
+        /// <summary>
+        /// Removed all playlists of a user.
+        /// If the playlist is shared, ownership is transferred.
+        /// </summary>
+        /// <param name="userId">The user id.</param>
+        /// <returns>Task.</returns>
+        Task RemovePlaylists(Guid userId);
     }
     }
 }
 }

+ 1 - 0
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -15,6 +15,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Controller.Playlists
 namespace MediaBrowser.Controller.Playlists

+ 16 - 0
MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs

@@ -8,6 +8,7 @@ using System.Threading;
 using System.Xml;
 using System.Xml;
 using Jellyfin.Extensions;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
@@ -636,6 +637,21 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     break;
                     break;
                 }
                 }
 
 
+                case "OwnerUserId":
+                {
+                    var val = reader.ReadElementContentAsString();
+
+                    if (Guid.TryParse(val, out var guid) && !guid.Equals(Guid.Empty))
+                    {
+                        if (item is Playlist playlist)
+                        {
+                            playlist.OwnerUserId = guid;
+                        }
+                    }
+
+                    break;
+                }
+
                 case "Format3D":
                 case "Format3D":
                 {
                 {
                     var val = reader.ReadElementContentAsString();
                     var val = reader.ReadElementContentAsString();

+ 12 - 8
MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs

@@ -395,6 +395,7 @@ namespace MediaBrowser.LocalMetadata.Savers
 
 
             if (item is Playlist playlist && !Playlist.IsPlaylistFile(playlist.Path))
             if (item is Playlist playlist && !Playlist.IsPlaylistFile(playlist.Path))
             {
             {
+                await writer.WriteElementStringAsync(null, "OwnerUserId", null, playlist.OwnerUserId.ToString("N")).ConfigureAwait(false);
                 await AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem").ConfigureAwait(false);
                 await AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem").ConfigureAwait(false);
             }
             }
 
 
@@ -418,16 +419,19 @@ namespace MediaBrowser.LocalMetadata.Savers
 
 
             foreach (var share in item.Shares)
             foreach (var share in item.Shares)
             {
             {
-                await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false);
+                if (share.UserId is not null)
+                {
+                    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).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);
             await writer.WriteEndElementAsync().ConfigureAwait(false);

+ 12 - 0
MediaBrowser.Model/Entities/IHasShares.cs

@@ -0,0 +1,12 @@
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// Interface for access to shares.
+/// </summary>
+public interface IHasShares
+{
+    /// <summary>
+    /// Gets or sets the shares.
+    /// </summary>
+    Share[] Shares { get; set; }
+}

+ 17 - 0
MediaBrowser.Model/Entities/Share.cs

@@ -0,0 +1,17 @@
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// Class to hold data on sharing permissions.
+/// </summary>
+public class Share
+{
+    /// <summary>
+    /// Gets or sets the user id.
+    /// </summary>
+    public string? UserId { get; set; }
+
+    /// <summary>
+    /// Gets or sets a value indicating whether the user has edit permissions.
+    /// </summary>
+    public bool CanEdit { get; set; }
+}

+ 28 - 11
MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs

@@ -1,19 +1,36 @@
-#nullable disable
-#pragma warning disable CS1591
-
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Model.Playlists;
 
 
-namespace MediaBrowser.Model.Playlists
+/// <summary>
+/// A playlist creation request.
+/// </summary>
+public class PlaylistCreationRequest
 {
 {
-    public class PlaylistCreationRequest
-    {
-        public string Name { get; set; }
+    /// <summary>
+    /// Gets or sets the name.
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// Gets or sets the list of items.
+    /// </summary>
+    public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>();
 
 
-        public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>();
+    /// <summary>
+    /// Gets or sets the media type.
+    /// </summary>
+    public string? MediaType { get; set; }
 
 
-        public string MediaType { get; set; }
+    /// <summary>
+    /// Gets or sets the user id.
+    /// </summary>
+    public Guid UserId { get; set; }
 
 
-        public Guid UserId { get; set; }
-    }
+    /// <summary>
+    /// Gets or sets the shares.
+    /// </summary>
+    public Share[]? Shares { get; set; }
 }
 }