< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.LyricsController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/LyricsController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 12
Coverable lines: 12
Total lines: 245
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/LyricsController.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel.DataAnnotations;
 4using System.IO;
 5using System.Net.Mime;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Jellyfin.Api.Attributes;
 9using Jellyfin.Api.Extensions;
 10using Jellyfin.Api.Helpers;
 11using Jellyfin.Extensions;
 12using MediaBrowser.Common.Api;
 13using MediaBrowser.Controller.Entities.Audio;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Controller.Lyrics;
 16using MediaBrowser.Controller.Providers;
 17using MediaBrowser.Model.IO;
 18using MediaBrowser.Model.Lyrics;
 19using MediaBrowser.Model.Providers;
 20using Microsoft.AspNetCore.Authorization;
 21using Microsoft.AspNetCore.Http;
 22using Microsoft.AspNetCore.Mvc;
 23
 24namespace Jellyfin.Api.Controllers;
 25
 26/// <summary>
 27/// Lyrics controller.
 28/// </summary>
 29[Route("")]
 30public class LyricsController : BaseJellyfinApiController
 31{
 32    private readonly ILibraryManager _libraryManager;
 33    private readonly ILyricManager _lyricManager;
 34    private readonly IProviderManager _providerManager;
 35    private readonly IFileSystem _fileSystem;
 36    private readonly IUserManager _userManager;
 37
 38    /// <summary>
 39    /// Initializes a new instance of the <see cref="LyricsController"/> class.
 40    /// </summary>
 41    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 42    /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
 43    /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
 44    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
 45    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 046    public LyricsController(
 047        ILibraryManager libraryManager,
 048        ILyricManager lyricManager,
 049        IProviderManager providerManager,
 050        IFileSystem fileSystem,
 051        IUserManager userManager)
 52    {
 053        _libraryManager = libraryManager;
 054        _lyricManager = lyricManager;
 055        _providerManager = providerManager;
 056        _fileSystem = fileSystem;
 057        _userManager = userManager;
 058    }
 59
 60    /// <summary>
 61    /// Gets an item's lyrics.
 62    /// </summary>
 63    /// <param name="itemId">Item id.</param>
 64    /// <response code="200">Lyrics returned.</response>
 65    /// <response code="404">Something went wrong. No Lyrics will be returned.</response>
 66    /// <returns>An <see cref="OkResult"/> containing the item's lyrics.</returns>
 67    [HttpGet("Audio/{itemId}/Lyrics")]
 68    [Authorize]
 69    [ProducesResponseType(StatusCodes.Status200OK)]
 70    [ProducesResponseType(StatusCodes.Status404NotFound)]
 71    public async Task<ActionResult<LyricDto>> GetLyrics([FromRoute, Required] Guid itemId)
 72    {
 73        var item = _libraryManager.GetItemById<Audio>(itemId, User.GetUserId());
 74        if (item is null)
 75        {
 76            return NotFound();
 77        }
 78
 79        var result = await _lyricManager.GetLyricsAsync(item, CancellationToken.None).ConfigureAwait(false);
 80        if (result is not null)
 81        {
 82            return Ok(result);
 83        }
 84
 85        return NotFound();
 86    }
 87
 88    /// <summary>
 89    /// Upload an external lyric file.
 90    /// </summary>
 91    /// <param name="itemId">The item the lyric belongs to.</param>
 92    /// <param name="fileName">Name of the file being uploaded.</param>
 93    /// <response code="200">Lyrics uploaded.</response>
 94    /// <response code="400">Error processing upload.</response>
 95    /// <response code="404">Item not found.</response>
 96    /// <returns>The uploaded lyric.</returns>
 97    [HttpPost("Audio/{itemId}/Lyrics")]
 98    [Authorize(Policy = Policies.LyricManagement)]
 99    [AcceptsFile(MediaTypeNames.Text.Plain)]
 100    [ProducesResponseType(StatusCodes.Status200OK)]
 101    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 102    [ProducesResponseType(StatusCodes.Status404NotFound)]
 103    public async Task<ActionResult<LyricDto>> UploadLyrics(
 104        [FromRoute, Required] Guid itemId,
 105        [FromQuery, Required] string fileName)
 106    {
 107        var item = _libraryManager.GetItemById<Audio>(itemId, User.GetUserId());
 108        if (item is null)
 109        {
 110            return NotFound();
 111        }
 112
 113        if (Request.ContentLength.GetValueOrDefault(0) == 0)
 114        {
 115            return BadRequest("No lyrics uploaded");
 116        }
 117
 118        // Utilize Path.GetExtension as it provides extra path validation.
 119        var format = Path.GetExtension(fileName.AsSpan()).RightPart('.').ToString();
 120        if (string.IsNullOrEmpty(format))
 121        {
 122            return BadRequest("Extension is required on filename");
 123        }
 124
 125        var stream = new MemoryStream();
 126        await using (stream.ConfigureAwait(false))
 127        {
 128            await Request.Body.CopyToAsync(stream).ConfigureAwait(false);
 129            var uploadedLyric = await _lyricManager.SaveLyricAsync(
 130                    item,
 131                    format,
 132                    stream)
 133                .ConfigureAwait(false);
 134
 135            if (uploadedLyric is null)
 136            {
 137                return BadRequest();
 138            }
 139
 140            _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), Refres
 141            return Ok(uploadedLyric);
 142        }
 143    }
 144
 145    /// <summary>
 146    /// Deletes an external lyric file.
 147    /// </summary>
 148    /// <param name="itemId">The item id.</param>
 149    /// <response code="204">Lyric deleted.</response>
 150    /// <response code="404">Item not found.</response>
 151    /// <returns>A <see cref="NoContentResult"/>.</returns>
 152    [HttpDelete("Audio/{itemId}/Lyrics")]
 153    [Authorize(Policy = Policies.LyricManagement)]
 154    [ProducesResponseType(StatusCodes.Status204NoContent)]
 155    [ProducesResponseType(StatusCodes.Status404NotFound)]
 156    public async Task<ActionResult> DeleteLyrics(
 157        [FromRoute, Required] Guid itemId)
 158    {
 159        var item = _libraryManager.GetItemById<Audio>(itemId, User.GetUserId());
 160        if (item is null)
 161        {
 162            return NotFound();
 163        }
 164
 165        await _lyricManager.DeleteLyricsAsync(item).ConfigureAwait(false);
 166        return NoContent();
 167    }
 168
 169    /// <summary>
 170    /// Search remote lyrics.
 171    /// </summary>
 172    /// <param name="itemId">The item id.</param>
 173    /// <response code="200">Lyrics retrieved.</response>
 174    /// <response code="404">Item not found.</response>
 175    /// <returns>An array of <see cref="RemoteLyricInfo"/>.</returns>
 176    [HttpGet("Audio/{itemId}/RemoteSearch/Lyrics")]
 177    [Authorize(Policy = Policies.LyricManagement)]
 178    [ProducesResponseType(StatusCodes.Status200OK)]
 179    [ProducesResponseType(StatusCodes.Status404NotFound)]
 180    public async Task<ActionResult<IReadOnlyList<RemoteLyricInfoDto>>> SearchRemoteLyrics([FromRoute, Required] Guid ite
 181    {
 182        var item = _libraryManager.GetItemById<Audio>(itemId, User.GetUserId());
 183        if (item is null)
 184        {
 185            return NotFound();
 186        }
 187
 188        var results = await _lyricManager.SearchLyricsAsync(item, false, CancellationToken.None).ConfigureAwait(false);
 189        return Ok(results);
 190    }
 191
 192    /// <summary>
 193    /// Downloads a remote lyric.
 194    /// </summary>
 195    /// <param name="itemId">The item id.</param>
 196    /// <param name="lyricId">The lyric id.</param>
 197    /// <response code="200">Lyric downloaded.</response>
 198    /// <response code="404">Item not found.</response>
 199    /// <returns>A <see cref="NoContentResult"/>.</returns>
 200    [HttpPost("Audio/{itemId}/RemoteSearch/Lyrics/{lyricId}")]
 201    [Authorize(Policy = Policies.LyricManagement)]
 202    [ProducesResponseType(StatusCodes.Status200OK)]
 203    [ProducesResponseType(StatusCodes.Status404NotFound)]
 204    public async Task<ActionResult<LyricDto>> DownloadRemoteLyrics(
 205        [FromRoute, Required] Guid itemId,
 206        [FromRoute, Required] string lyricId)
 207    {
 208        var item = _libraryManager.GetItemById<Audio>(itemId, User.GetUserId());
 209        if (item is null)
 210        {
 211            return NotFound();
 212        }
 213
 214        var downloadedLyrics = await _lyricManager.DownloadLyricsAsync(item, lyricId, CancellationToken.None).ConfigureA
 215        if (downloadedLyrics is null)
 216        {
 217            return NotFound();
 218        }
 219
 220        _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPri
 221        return Ok(downloadedLyrics);
 222    }
 223
 224    /// <summary>
 225    /// Gets the remote lyrics.
 226    /// </summary>
 227    /// <param name="lyricId">The remote provider item id.</param>
 228    /// <response code="200">File returned.</response>
 229    /// <response code="404">Lyric not found.</response>
 230    /// <returns>A <see cref="FileStreamResult"/> with the lyric file.</returns>
 231    [HttpGet("Providers/Lyrics/{lyricId}")]
 232    [Authorize(Policy = Policies.LyricManagement)]
 233    [ProducesResponseType(StatusCodes.Status200OK)]
 234    [ProducesResponseType(StatusCodes.Status404NotFound)]
 235    public async Task<ActionResult<LyricDto>> GetRemoteLyrics([FromRoute, Required] string lyricId)
 236    {
 237        var result = await _lyricManager.GetRemoteLyricsAsync(lyricId, CancellationToken.None).ConfigureAwait(false);
 238        if (result is null)
 239        {
 240            return NotFound();
 241        }
 242
 243        return Ok(result);
 244    }
 245}