mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-15 09:59:06 -07:00
Fix playlists library and migration (#9770)
This commit is contained in:
parent
603fce59df
commit
eb52af4e6a
@ -46,10 +46,9 @@ namespace Emby.Server.Implementations.Library
|
||||
public Folder[] GetUserViews(UserViewQuery query)
|
||||
{
|
||||
var user = _userManager.GetUserById(query.UserId);
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
throw new ArgumentException("User Id specified in the query does not exist.", nameof(query));
|
||||
throw new ArgumentException("User id specified in the query does not exist.", nameof(query));
|
||||
}
|
||||
|
||||
var folders = _libraryManager.GetUserRootFolder()
|
||||
@ -58,7 +57,6 @@ namespace Emby.Server.Implementations.Library
|
||||
.ToList();
|
||||
|
||||
var groupedFolders = new List<ICollectionFolder>();
|
||||
|
||||
var list = new List<Folder>();
|
||||
|
||||
foreach (var folder in folders)
|
||||
@ -66,6 +64,20 @@ namespace Emby.Server.Implementations.Library
|
||||
var collectionFolder = folder as ICollectionFolder;
|
||||
var folderViewType = collectionFolder?.CollectionType;
|
||||
|
||||
// Playlist library requires special handling because the folder only refrences user playlists
|
||||
if (string.Equals(folderViewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var items = folder.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
ParentId = folder.ParentId
|
||||
});
|
||||
|
||||
if (!items.Any(item => item.IsVisible(user)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (UserView.IsUserSpecific(folder))
|
||||
{
|
||||
list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id, folderViewType, null));
|
||||
@ -132,14 +144,12 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
|
||||
|
||||
var orders = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews);
|
||||
|
||||
return list
|
||||
.OrderBy(i =>
|
||||
{
|
||||
var index = Array.IndexOf(orders, i.Id);
|
||||
|
||||
if (index == -1
|
||||
&& i is UserView view
|
||||
&& !view.DisplayParentId.Equals(default))
|
||||
|
@ -67,9 +67,8 @@ namespace Emby.Server.Implementations.Playlists
|
||||
public async Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options)
|
||||
{
|
||||
var name = options.Name;
|
||||
|
||||
var folderName = _fileSystem.GetValidFilename(name);
|
||||
var parentFolder = GetPlaylistsFolder(Guid.Empty);
|
||||
var parentFolder = GetPlaylistsFolder(options.UserId);
|
||||
if (parentFolder is null)
|
||||
{
|
||||
throw new ArgumentException(nameof(parentFolder));
|
||||
@ -80,7 +79,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
foreach (var itemId in options.ItemIdList)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(itemId);
|
||||
|
||||
if (item is null)
|
||||
{
|
||||
throw new ArgumentException("No item exists with the supplied Id");
|
||||
@ -121,7 +119,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(options.UserId);
|
||||
|
||||
var path = Path.Combine(parentFolder.Path, folderName);
|
||||
path = GetTargetPath(path);
|
||||
|
||||
@ -130,7 +127,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
var playlist = new Playlist
|
||||
{
|
||||
Name = name,
|
||||
@ -140,7 +136,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
};
|
||||
|
||||
playlist.SetMediaType(options.MediaType);
|
||||
|
||||
parentFolder.AddChild(playlist);
|
||||
|
||||
await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
|
||||
@ -326,7 +321,8 @@ namespace Emby.Server.Implementations.Playlists
|
||||
}
|
||||
}
|
||||
|
||||
private void SavePlaylistFile(Playlist item)
|
||||
/// <inheritdoc />
|
||||
public void SavePlaylistFile(Playlist item)
|
||||
{
|
||||
// this is probably best done as a metadata provider
|
||||
// saving a file over itself will require some work to prevent this from happening when not needed
|
||||
@ -564,20 +560,5 @@ namespace Emby.Server.Implementations.Playlists
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task UpdatePlaylistAsync(Playlist playlist)
|
||||
{
|
||||
var currentPlaylist = (Playlist)_libraryManager.GetItemById(playlist.Id);
|
||||
currentPlaylist.OwnerUserId = playlist.OwnerUserId;
|
||||
currentPlaylist.Shares = playlist.Shares;
|
||||
|
||||
await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
if (currentPlaylist.IsFile)
|
||||
{
|
||||
SavePlaylistFile(currentPlaylist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
[JsonIgnore]
|
||||
public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists;
|
||||
|
||||
public override bool IsVisible(User user)
|
||||
{
|
||||
return base.IsVisible(user) && GetChildren(user, true).Any();
|
||||
}
|
||||
|
||||
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
||||
{
|
||||
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
|
||||
@ -47,7 +42,6 @@ namespace Emby.Server.Implementations.Playlists
|
||||
|
||||
query.Recursive = true;
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
|
||||
query.Parent = null;
|
||||
return LibraryManager.GetItemsResult(query);
|
||||
}
|
||||
|
||||
|
@ -503,6 +503,7 @@ public class ItemsController : BaseJellyfinApiController
|
||||
}
|
||||
}
|
||||
|
||||
query.Parent = null;
|
||||
result = folder.GetItems(query);
|
||||
}
|
||||
else
|
||||
@ -511,10 +512,12 @@ public class ItemsController : BaseJellyfinApiController
|
||||
result = new QueryResult<BaseItem>(itemsArray);
|
||||
}
|
||||
|
||||
// result might include items not accessible by the user, DtoService will remove them
|
||||
var accessibleItems = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user);
|
||||
return new QueryResult<BaseItemDto>(
|
||||
startIndex,
|
||||
result.TotalRecordCount,
|
||||
_dtoService.GetBaseItemDtos(result.Items, dtoOptions, user));
|
||||
accessibleItems.Count,
|
||||
accessibleItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -65,14 +65,12 @@ public class PlaylistsController : BaseJellyfinApiController
|
||||
/// <param name="mediaType">The media type.</param>
|
||||
/// <param name="createPlaylistRequest">The create playlist payload.</param>
|
||||
/// <response code="200">Playlist created.</response>
|
||||
/// <response code="403">User does not have permission to create playlists.</response>
|
||||
/// <returns>
|
||||
/// A <see cref="Task" /> that represents the asynchronous operation to create a playlist.
|
||||
/// The task result contains an <see cref="OkResult"/> indicating success.
|
||||
/// </returns>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
|
||||
[FromQuery, ParameterObsolete] string? name,
|
||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids,
|
||||
@ -105,11 +103,9 @@ public class PlaylistsController : BaseJellyfinApiController
|
||||
/// <param name="ids">Item id, comma delimited.</param>
|
||||
/// <param name="userId">The userId.</param>
|
||||
/// <response code="204">Items added to playlist.</response>
|
||||
/// <response code="403">User does not have permission to add items to playlist.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
|
||||
[HttpPost("{playlistId}/Items")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> AddToPlaylist(
|
||||
[FromRoute, Required] Guid playlistId,
|
||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids,
|
||||
@ -127,11 +123,9 @@ public class PlaylistsController : BaseJellyfinApiController
|
||||
/// <param name="itemId">The item id.</param>
|
||||
/// <param name="newIndex">The new index.</param>
|
||||
/// <response code="204">Item moved to new index.</response>
|
||||
/// <response code="403">User does not have permission to move item.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
|
||||
[HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> MoveItem(
|
||||
[FromRoute, Required] string playlistId,
|
||||
[FromRoute, Required] string itemId,
|
||||
@ -147,11 +141,9 @@ public class PlaylistsController : BaseJellyfinApiController
|
||||
/// <param name="playlistId">The playlist id.</param>
|
||||
/// <param name="entryIds">The item ids, comma delimited.</param>
|
||||
/// <response code="204">Items removed.</response>
|
||||
/// <response code="403">User does not have permission to get playlist.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success.</returns>
|
||||
[HttpDelete("{playlistId}/Items")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> RemoveFromPlaylist(
|
||||
[FromRoute, Required] string playlistId,
|
||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] entryIds)
|
||||
@ -173,12 +165,10 @@ public class PlaylistsController : BaseJellyfinApiController
|
||||
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
|
||||
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
|
||||
/// <response code="200">Original playlist returned.</response>
|
||||
/// <response code="403">User does not have permission to get playlist items.</response>
|
||||
/// <response code="404">Playlist not found.</response>
|
||||
/// <returns>The original playlist items.</returns>
|
||||
[HttpGet("{playlistId}/Items")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult<QueryResult<BaseItemDto>> GetPlaylistItems(
|
||||
[FromRoute, Required] Guid playlistId,
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
@ -60,13 +61,14 @@ internal class FixPlaylistOwner : IMigrationRoutine
|
||||
{
|
||||
playlist.OwnerUserId = guid;
|
||||
playlist.Shares = shares.Where(x => x != firstEditShare).ToArray();
|
||||
_playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult();
|
||||
playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
|
||||
_playlistManager.SavePlaylistFile(playlist);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
playlist.OpenAccess = true;
|
||||
_playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult();
|
||||
playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,10 +66,9 @@ namespace MediaBrowser.Controller.Playlists
|
||||
Task RemovePlaylistsAsync(Guid userId);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a playlist.
|
||||
/// Saves a playlist.
|
||||
/// </summary>
|
||||
/// <param name="playlist">The updated playlist.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdatePlaylistAsync(Playlist playlist);
|
||||
/// <param name="item">The playlist.</param>
|
||||
void SavePlaylistFile(Playlist item);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user