aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers/LibraryStructureController.cs
diff options
context:
space:
mode:
authorDominik <git@secnd.me>2023-06-15 19:38:42 +0200
committerGitHub <noreply@github.com>2023-06-15 19:38:42 +0200
commit17f1e8d19b1fd693893d66d2275ed8ae2476344e (patch)
tree7f48be975faa92042769870957587b3c7864f631 /Jellyfin.Api/Controllers/LibraryStructureController.cs
parente8ae7e5c38e28f13fa8de295e26c930cb46d9b79 (diff)
parent6771b5cabe96b4b3cbd1cd0c998d564f3dd17ed4 (diff)
Merge branch 'master' into segment-deletion
Diffstat (limited to 'Jellyfin.Api/Controllers/LibraryStructureController.cs')
-rw-r--r--Jellyfin.Api/Controllers/LibraryStructureController.cs509
1 files changed, 254 insertions, 255 deletions
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index ec1170411..b012ff42e 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -20,308 +20,307 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-namespace Jellyfin.Api.Controllers
+namespace Jellyfin.Api.Controllers;
+
+/// <summary>
+/// The library structure controller.
+/// </summary>
+[Route("Library/VirtualFolders")]
+[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
+public class LibraryStructureController : BaseJellyfinApiController
{
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly ILibraryManager _libraryManager;
+ private readonly ILibraryMonitor _libraryMonitor;
+
/// <summary>
- /// The library structure controller.
+ /// Initializes a new instance of the <see cref="LibraryStructureController"/> class.
/// </summary>
- [Route("Library/VirtualFolders")]
- [Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
- public class LibraryStructureController : BaseJellyfinApiController
+ /// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
+ /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
+ /// <param name="libraryMonitor">Instance of <see cref="ILibraryMonitor"/> interface.</param>
+ public LibraryStructureController(
+ IServerConfigurationManager serverConfigurationManager,
+ ILibraryManager libraryManager,
+ ILibraryMonitor libraryMonitor)
{
- private readonly IServerApplicationPaths _appPaths;
- private readonly ILibraryManager _libraryManager;
- private readonly ILibraryMonitor _libraryMonitor;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LibraryStructureController"/> class.
- /// </summary>
- /// <param name="serverConfigurationManager">Instance of <see cref="IServerConfigurationManager"/> interface.</param>
- /// <param name="libraryManager">Instance of <see cref="ILibraryManager"/> interface.</param>
- /// <param name="libraryMonitor">Instance of <see cref="ILibraryMonitor"/> interface.</param>
- public LibraryStructureController(
- IServerConfigurationManager serverConfigurationManager,
- ILibraryManager libraryManager,
- ILibraryMonitor libraryMonitor)
- {
- _appPaths = serverConfigurationManager.ApplicationPaths;
- _libraryManager = libraryManager;
- _libraryMonitor = libraryMonitor;
- }
+ _appPaths = serverConfigurationManager.ApplicationPaths;
+ _libraryManager = libraryManager;
+ _libraryMonitor = libraryMonitor;
+ }
+
+ /// <summary>
+ /// Gets all virtual folders.
+ /// </summary>
+ /// <response code="200">Virtual folders retrieved.</response>
+ /// <returns>An <see cref="IEnumerable{VirtualFolderInfo}"/> with the virtual folders.</returns>
+ [HttpGet]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult<IEnumerable<VirtualFolderInfo>> GetVirtualFolders()
+ {
+ return _libraryManager.GetVirtualFolders(true);
+ }
+
+ /// <summary>
+ /// Adds a virtual folder.
+ /// </summary>
+ /// <param name="name">The name of the virtual folder.</param>
+ /// <param name="collectionType">The type of the collection.</param>
+ /// <param name="paths">The paths of the virtual folder.</param>
+ /// <param name="libraryOptionsDto">The library options.</param>
+ /// <param name="refreshLibrary">Whether to refresh the library.</param>
+ /// <response code="204">Folder added.</response>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ [HttpPost]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public async Task<ActionResult> AddVirtualFolder(
+ [FromQuery] string? name,
+ [FromQuery] CollectionTypeOptions? collectionType,
+ [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] paths,
+ [FromBody] AddVirtualFolderDto? libraryOptionsDto,
+ [FromQuery] bool refreshLibrary = false)
+ {
+ var libraryOptions = libraryOptionsDto?.LibraryOptions ?? new LibraryOptions();
- /// <summary>
- /// Gets all virtual folders.
- /// </summary>
- /// <response code="200">Virtual folders retrieved.</response>
- /// <returns>An <see cref="IEnumerable{VirtualFolderInfo}"/> with the virtual folders.</returns>
- [HttpGet]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult<IEnumerable<VirtualFolderInfo>> GetVirtualFolders()
+ if (paths is not null && paths.Length > 0)
{
- return _libraryManager.GetVirtualFolders(true);
+ libraryOptions.PathInfos = paths.Select(i => new MediaPathInfo(i)).ToArray();
}
- /// <summary>
- /// Adds a virtual folder.
- /// </summary>
- /// <param name="name">The name of the virtual folder.</param>
- /// <param name="collectionType">The type of the collection.</param>
- /// <param name="paths">The paths of the virtual folder.</param>
- /// <param name="libraryOptionsDto">The library options.</param>
- /// <param name="refreshLibrary">Whether to refresh the library.</param>
- /// <response code="204">Folder added.</response>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task<ActionResult> AddVirtualFolder(
- [FromQuery] string? name,
- [FromQuery] CollectionTypeOptions? collectionType,
- [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] paths,
- [FromBody] AddVirtualFolderDto? libraryOptionsDto,
- [FromQuery] bool refreshLibrary = false)
- {
- var libraryOptions = libraryOptionsDto?.LibraryOptions ?? new LibraryOptions();
+ await _libraryManager.AddVirtualFolder(name, collectionType, libraryOptions, refreshLibrary).ConfigureAwait(false);
- if (paths != null && paths.Length > 0)
- {
- libraryOptions.PathInfos = paths.Select(i => new MediaPathInfo(i)).ToArray();
- }
+ return NoContent();
+ }
- await _libraryManager.AddVirtualFolder(name, collectionType, libraryOptions, refreshLibrary).ConfigureAwait(false);
+ /// <summary>
+ /// Removes a virtual folder.
+ /// </summary>
+ /// <param name="name">The name of the folder.</param>
+ /// <param name="refreshLibrary">Whether to refresh the library.</param>
+ /// <response code="204">Folder removed.</response>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ [HttpDelete]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public async Task<ActionResult> RemoveVirtualFolder(
+ [FromQuery] string? name,
+ [FromQuery] bool refreshLibrary = false)
+ {
+ await _libraryManager.RemoveVirtualFolder(name, refreshLibrary).ConfigureAwait(false);
+ return NoContent();
+ }
- return NoContent();
+ /// <summary>
+ /// Renames a virtual folder.
+ /// </summary>
+ /// <param name="name">The name of the virtual folder.</param>
+ /// <param name="newName">The new name.</param>
+ /// <param name="refreshLibrary">Whether to refresh the library.</param>
+ /// <response code="204">Folder renamed.</response>
+ /// <response code="404">Library doesn't exist.</response>
+ /// <response code="409">Library already exists.</response>
+ /// <returns>A <see cref="NoContentResult"/> on success, a <see cref="NotFoundResult"/> if the library doesn't exist, a <see cref="ConflictResult"/> if the new name is already taken.</returns>
+ /// <exception cref="ArgumentNullException">The new name may not be null.</exception>
+ [HttpPost("Name")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status409Conflict)]
+ public ActionResult RenameVirtualFolder(
+ [FromQuery] string? name,
+ [FromQuery] string? newName,
+ [FromQuery] bool refreshLibrary = false)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException(nameof(name));
}
- /// <summary>
- /// Removes a virtual folder.
- /// </summary>
- /// <param name="name">The name of the folder.</param>
- /// <param name="refreshLibrary">Whether to refresh the library.</param>
- /// <response code="204">Folder removed.</response>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpDelete]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public async Task<ActionResult> RemoveVirtualFolder(
- [FromQuery] string? name,
- [FromQuery] bool refreshLibrary = false)
+ if (string.IsNullOrWhiteSpace(newName))
{
- await _libraryManager.RemoveVirtualFolder(name, refreshLibrary).ConfigureAwait(false);
- return NoContent();
+ throw new ArgumentNullException(nameof(newName));
}
- /// <summary>
- /// Renames a virtual folder.
- /// </summary>
- /// <param name="name">The name of the virtual folder.</param>
- /// <param name="newName">The new name.</param>
- /// <param name="refreshLibrary">Whether to refresh the library.</param>
- /// <response code="204">Folder renamed.</response>
- /// <response code="404">Library doesn't exist.</response>
- /// <response code="409">Library already exists.</response>
- /// <returns>A <see cref="NoContentResult"/> on success, a <see cref="NotFoundResult"/> if the library doesn't exist, a <see cref="ConflictResult"/> if the new name is already taken.</returns>
- /// <exception cref="ArgumentNullException">The new name may not be null.</exception>
- [HttpPost("Name")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- [ProducesResponseType(StatusCodes.Status409Conflict)]
- public ActionResult RenameVirtualFolder(
- [FromQuery] string? name,
- [FromQuery] string? newName,
- [FromQuery] bool refreshLibrary = false)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
+ var rootFolderPath = _appPaths.DefaultUserViewsPath;
- if (string.IsNullOrWhiteSpace(newName))
- {
- throw new ArgumentNullException(nameof(newName));
- }
+ var currentPath = Path.Combine(rootFolderPath, name);
+ var newPath = Path.Combine(rootFolderPath, newName);
- var rootFolderPath = _appPaths.DefaultUserViewsPath;
+ if (!Directory.Exists(currentPath))
+ {
+ return NotFound("The media collection does not exist.");
+ }
- var currentPath = Path.Combine(rootFolderPath, name);
- var newPath = Path.Combine(rootFolderPath, newName);
+ if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath))
+ {
+ return Conflict($"The media library already exists at {newPath}.");
+ }
- if (!Directory.Exists(currentPath))
- {
- return NotFound("The media collection does not exist.");
- }
+ _libraryMonitor.Stop();
- if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath))
+ try
+ {
+ // Changing capitalization. Handle windows case insensitivity
+ if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase))
{
- return Conflict($"The media library already exists at {newPath}.");
+ var tempPath = Path.Combine(
+ rootFolderPath,
+ Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
+ Directory.Move(currentPath, tempPath);
+ currentPath = tempPath;
}
- _libraryMonitor.Stop();
+ Directory.Move(currentPath, newPath);
+ }
+ finally
+ {
+ CollectionFolder.OnCollectionFolderChange();
- try
+ Task.Run(async () =>
{
- // Changing capitalization. Handle windows case insensitivity
- if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase))
+ // No need to start if scanning the library because it will handle it
+ if (refreshLibrary)
{
- var tempPath = Path.Combine(
- rootFolderPath,
- Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
- Directory.Move(currentPath, tempPath);
- currentPath = tempPath;
+ await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
}
-
- Directory.Move(currentPath, newPath);
- }
- finally
- {
- CollectionFolder.OnCollectionFolderChange();
-
- Task.Run(async () =>
+ else
{
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- // Have to block here to allow exceptions to bubble
- await Task.Delay(1000).ConfigureAwait(false);
- _libraryMonitor.Start();
- }
- });
- }
-
- return NoContent();
+ // Need to add a delay here or directory watchers may still pick up the changes
+ // Have to block here to allow exceptions to bubble
+ await Task.Delay(1000).ConfigureAwait(false);
+ _libraryMonitor.Start();
+ }
+ });
}
- /// <summary>
- /// Add a media path to a library.
- /// </summary>
- /// <param name="mediaPathDto">The media path dto.</param>
- /// <param name="refreshLibrary">Whether to refresh the library.</param>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- /// <response code="204">Media path added.</response>
- /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
- [HttpPost("Paths")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult AddMediaPath(
- [FromBody, Required] MediaPathDto mediaPathDto,
- [FromQuery] bool refreshLibrary = false)
- {
- _libraryMonitor.Stop();
+ return NoContent();
+ }
- try
- {
- var mediaPath = mediaPathDto.PathInfo ?? new MediaPathInfo(mediaPathDto.Path ?? throw new ArgumentException("PathInfo and Path can't both be null."));
+ /// <summary>
+ /// Add a media path to a library.
+ /// </summary>
+ /// <param name="mediaPathDto">The media path dto.</param>
+ /// <param name="refreshLibrary">Whether to refresh the library.</param>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ /// <response code="204">Media path added.</response>
+ /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
+ [HttpPost("Paths")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public ActionResult AddMediaPath(
+ [FromBody, Required] MediaPathDto mediaPathDto,
+ [FromQuery] bool refreshLibrary = false)
+ {
+ _libraryMonitor.Stop();
- _libraryManager.AddMediaPath(mediaPathDto.Name, mediaPath);
- }
- finally
- {
- Task.Run(async () =>
- {
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- // Have to block here to allow exceptions to bubble
- await Task.Delay(1000).ConfigureAwait(false);
- _libraryMonitor.Start();
- }
- });
- }
+ try
+ {
+ var mediaPath = mediaPathDto.PathInfo ?? new MediaPathInfo(mediaPathDto.Path ?? throw new ArgumentException("PathInfo and Path can't both be null."));
- return NoContent();
+ _libraryManager.AddMediaPath(mediaPathDto.Name, mediaPath);
}
-
- /// <summary>
- /// Updates a media path.
- /// </summary>
- /// <param name="mediaPathRequestDto">The name of the library and path infos.</param>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- /// <response code="204">Media path updated.</response>
- /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
- [HttpPost("Paths/Update")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult UpdateMediaPath([FromBody, Required] UpdateMediaPathRequestDto mediaPathRequestDto)
+ finally
{
- if (string.IsNullOrWhiteSpace(mediaPathRequestDto.Name))
+ Task.Run(async () =>
{
- throw new ArgumentNullException(nameof(mediaPathRequestDto), "Name must not be null or empty");
- }
+ // No need to start if scanning the library because it will handle it
+ if (refreshLibrary)
+ {
+ await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ }
+ else
+ {
+ // Need to add a delay here or directory watchers may still pick up the changes
+ // Have to block here to allow exceptions to bubble
+ await Task.Delay(1000).ConfigureAwait(false);
+ _libraryMonitor.Start();
+ }
+ });
+ }
+
+ return NoContent();
+ }
- _libraryManager.UpdateMediaPath(mediaPathRequestDto.Name, mediaPathRequestDto.PathInfo);
- return NoContent();
+ /// <summary>
+ /// Updates a media path.
+ /// </summary>
+ /// <param name="mediaPathRequestDto">The name of the library and path infos.</param>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ /// <response code="204">Media path updated.</response>
+ /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
+ [HttpPost("Paths/Update")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public ActionResult UpdateMediaPath([FromBody, Required] UpdateMediaPathRequestDto mediaPathRequestDto)
+ {
+ if (string.IsNullOrWhiteSpace(mediaPathRequestDto.Name))
+ {
+ throw new ArgumentNullException(nameof(mediaPathRequestDto), "Name must not be null or empty");
}
- /// <summary>
- /// Remove a media path.
- /// </summary>
- /// <param name="name">The name of the library.</param>
- /// <param name="path">The path to remove.</param>
- /// <param name="refreshLibrary">Whether to refresh the library.</param>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- /// <response code="204">Media path removed.</response>
- /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
- [HttpDelete("Paths")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult RemoveMediaPath(
- [FromQuery] string? name,
- [FromQuery] string? path,
- [FromQuery] bool refreshLibrary = false)
+ _libraryManager.UpdateMediaPath(mediaPathRequestDto.Name, mediaPathRequestDto.PathInfo);
+ return NoContent();
+ }
+
+ /// <summary>
+ /// Remove a media path.
+ /// </summary>
+ /// <param name="name">The name of the library.</param>
+ /// <param name="path">The path to remove.</param>
+ /// <param name="refreshLibrary">Whether to refresh the library.</param>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ /// <response code="204">Media path removed.</response>
+ /// <exception cref="ArgumentNullException">The name of the library may not be empty.</exception>
+ [HttpDelete("Paths")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public ActionResult RemoveMediaPath(
+ [FromQuery] string? name,
+ [FromQuery] string? path,
+ [FromQuery] bool refreshLibrary = false)
+ {
+ if (string.IsNullOrWhiteSpace(name))
{
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException(nameof(name));
- }
+ throw new ArgumentNullException(nameof(name));
+ }
- _libraryMonitor.Stop();
+ _libraryMonitor.Stop();
- try
- {
- _libraryManager.RemoveMediaPath(name, path);
- }
- finally
+ try
+ {
+ _libraryManager.RemoveMediaPath(name, path);
+ }
+ finally
+ {
+ Task.Run(async () =>
{
- Task.Run(async () =>
+ // No need to start if scanning the library because it will handle it
+ if (refreshLibrary)
{
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- // Have to block here to allow exceptions to bubble
- await Task.Delay(1000).ConfigureAwait(false);
- _libraryMonitor.Start();
- }
- });
- }
-
- return NoContent();
+ await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ }
+ else
+ {
+ // Need to add a delay here or directory watchers may still pick up the changes
+ // Have to block here to allow exceptions to bubble
+ await Task.Delay(1000).ConfigureAwait(false);
+ _libraryMonitor.Start();
+ }
+ });
}
- /// <summary>
- /// Update library options.
- /// </summary>
- /// <param name="request">The library name and options.</param>
- /// <response code="204">Library updated.</response>
- /// <returns>A <see cref="NoContentResult"/>.</returns>
- [HttpPost("LibraryOptions")]
- [ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult UpdateLibraryOptions(
- [FromBody] UpdateLibraryOptionsDto request)
- {
- var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
+ return NoContent();
+ }
- collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
- return NoContent();
- }
+ /// <summary>
+ /// Update library options.
+ /// </summary>
+ /// <param name="request">The library name and options.</param>
+ /// <response code="204">Library updated.</response>
+ /// <returns>A <see cref="NoContentResult"/>.</returns>
+ [HttpPost("LibraryOptions")]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ public ActionResult UpdateLibraryOptions(
+ [FromBody] UpdateLibraryOptionsDto request)
+ {
+ var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
+
+ collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
+ return NoContent();
}
}