mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 13:10:13 -07:00
MediaCover Pulls and Fixes
This commit is contained in:
parent
44f8022fa2
commit
498368e8c5
11 changed files with 175 additions and 24 deletions
|
@ -11,10 +11,12 @@ namespace Lidarr.Api.V1.Albums
|
||||||
public class AlbumLookupController : Controller
|
public class AlbumLookupController : Controller
|
||||||
{
|
{
|
||||||
private readonly ISearchForNewAlbum _searchProxy;
|
private readonly ISearchForNewAlbum _searchProxy;
|
||||||
|
private readonly IMapCoversToLocal _coverMapper;
|
||||||
|
|
||||||
public AlbumLookupController(ISearchForNewAlbum searchProxy)
|
public AlbumLookupController(ISearchForNewAlbum searchProxy, IMapCoversToLocal coverMapper)
|
||||||
{
|
{
|
||||||
_searchProxy = searchProxy;
|
_searchProxy = searchProxy;
|
||||||
|
_coverMapper = coverMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
@ -24,15 +26,18 @@ namespace Lidarr.Api.V1.Albums
|
||||||
return MapToResource(searchResults).ToList();
|
return MapToResource(searchResults).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<AlbumResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Album> albums)
|
private IEnumerable<AlbumResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Album> albums)
|
||||||
{
|
{
|
||||||
foreach (var currentAlbum in albums)
|
foreach (var currentAlbum in albums)
|
||||||
{
|
{
|
||||||
var resource = currentAlbum.ToResource();
|
var resource = currentAlbum.ToResource();
|
||||||
|
|
||||||
|
_coverMapper.ConvertToLocalUrls(resource.Id, MediaCoverEntity.Album, resource.Images);
|
||||||
|
|
||||||
var cover = currentAlbum.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
|
var cover = currentAlbum.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
|
||||||
if (cover != null)
|
if (cover != null)
|
||||||
{
|
{
|
||||||
resource.RemoteCover = cover.Url;
|
resource.RemoteCover = cover.RemoteUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return resource;
|
yield return resource;
|
||||||
|
|
|
@ -11,10 +11,12 @@ namespace Lidarr.Api.V1.Artist
|
||||||
public class ArtistLookupController : Controller
|
public class ArtistLookupController : Controller
|
||||||
{
|
{
|
||||||
private readonly ISearchForNewArtist _searchProxy;
|
private readonly ISearchForNewArtist _searchProxy;
|
||||||
|
private readonly IMapCoversToLocal _coverMapper;
|
||||||
|
|
||||||
public ArtistLookupController(ISearchForNewArtist searchProxy)
|
public ArtistLookupController(ISearchForNewArtist searchProxy, IMapCoversToLocal coverMapper)
|
||||||
{
|
{
|
||||||
_searchProxy = searchProxy;
|
_searchProxy = searchProxy;
|
||||||
|
_coverMapper = coverMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
@ -24,15 +26,18 @@ namespace Lidarr.Api.V1.Artist
|
||||||
return MapToResource(searchResults).ToList();
|
return MapToResource(searchResults).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<ArtistResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Artist> artist)
|
private IEnumerable<ArtistResource> MapToResource(IEnumerable<NzbDrone.Core.Music.Artist> artist)
|
||||||
{
|
{
|
||||||
foreach (var currentArtist in artist)
|
foreach (var currentArtist in artist)
|
||||||
{
|
{
|
||||||
var resource = currentArtist.ToResource();
|
var resource = currentArtist.ToResource();
|
||||||
|
|
||||||
|
_coverMapper.ConvertToLocalUrls(resource.Id, MediaCoverEntity.Artist, resource.Images);
|
||||||
|
|
||||||
var poster = currentArtist.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
var poster = currentArtist.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
||||||
if (poster != null)
|
if (poster != null)
|
||||||
{
|
{
|
||||||
resource.RemotePoster = poster.Url;
|
resource.RemotePoster = poster.RemoteUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return resource;
|
yield return resource;
|
||||||
|
|
|
@ -6,6 +6,6 @@ namespace Lidarr.Http.Frontend.Mappers
|
||||||
{
|
{
|
||||||
string Map(string resourceUrl);
|
string Map(string resourceUrl);
|
||||||
bool CanHandle(string resourceUrl);
|
bool CanHandle(string resourceUrl);
|
||||||
FileStreamResult GetResponse(string resourceUrl);
|
IActionResult GetResponse(string resourceUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace Lidarr.Http.Frontend.Mappers
|
||||||
|
|
||||||
public override bool CanHandle(string resourceUrl)
|
public override bool CanHandle(string resourceUrl)
|
||||||
{
|
{
|
||||||
return resourceUrl.StartsWith("/MediaCover", StringComparison.InvariantCultureIgnoreCase);
|
return resourceUrl.StartsWith("/MediaCover/", StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
55
src/Lidarr.Http/Frontend/Mappers/MediaCoverProxyMapper.cs
Normal file
55
src/Lidarr.Http/Frontend/Mappers/MediaCoverProxyMapper.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
|
using NzbDrone.Core.MediaCover;
|
||||||
|
|
||||||
|
namespace Lidarr.Http.Frontend.Mappers
|
||||||
|
{
|
||||||
|
public class MediaCoverProxyMapper : IMapHttpRequestsToDisk
|
||||||
|
{
|
||||||
|
private readonly Regex _regex = new Regex(@"/MediaCoverProxy/(?<hash>\w+)/(?<filename>(.+)\.(jpg|png|gif))");
|
||||||
|
|
||||||
|
private readonly IMediaCoverProxy _mediaCoverProxy;
|
||||||
|
private readonly IContentTypeProvider _mimeTypeProvider;
|
||||||
|
|
||||||
|
public MediaCoverProxyMapper(IMediaCoverProxy mediaCoverProxy)
|
||||||
|
{
|
||||||
|
_mediaCoverProxy = mediaCoverProxy;
|
||||||
|
_mimeTypeProvider = new FileExtensionContentTypeProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Map(string resourceUrl)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHandle(string resourceUrl)
|
||||||
|
{
|
||||||
|
return resourceUrl.StartsWith("/MediaCoverProxy/", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult GetResponse(string resourceUrl)
|
||||||
|
{
|
||||||
|
var match = _regex.Match(resourceUrl);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
return new StatusCodeResult((int)HttpStatusCode.NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = match.Groups["hash"].Value;
|
||||||
|
var filename = match.Groups["filename"].Value;
|
||||||
|
|
||||||
|
var imageData = _mediaCoverProxy.GetImage(hash);
|
||||||
|
|
||||||
|
if (!_mimeTypeProvider.TryGetContentType(filename, out var contentType))
|
||||||
|
{
|
||||||
|
contentType = "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileContentResult(imageData, contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ namespace Lidarr.Http.Frontend.Mappers
|
||||||
|
|
||||||
public abstract bool CanHandle(string resourceUrl);
|
public abstract bool CanHandle(string resourceUrl);
|
||||||
|
|
||||||
public FileStreamResult GetResponse(string resourceUrl)
|
public IActionResult GetResponse(string resourceUrl)
|
||||||
{
|
{
|
||||||
var filePath = Map(resourceUrl);
|
var filePath = Map(resourceUrl);
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace Lidarr.Http.Frontend
|
||||||
|
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
if (result.ContentType == "text/html")
|
if ((result as FileResult)?.ContentType == "text/html")
|
||||||
{
|
{
|
||||||
Response.Headers.DisableCache();
|
Response.Headers.DisableCache();
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace NzbDrone.Core.MediaCover
|
||||||
|
|
||||||
public MediaCoverTypes CoverType { get; set; }
|
public MediaCoverTypes CoverType { get; set; }
|
||||||
public string Extension { get; private set; }
|
public string Extension { get; private set; }
|
||||||
|
public string RemoteUrl { get; set; }
|
||||||
|
|
||||||
public MediaCover()
|
public MediaCover()
|
||||||
{
|
{
|
||||||
|
|
64
src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs
Normal file
64
src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaCover
|
||||||
|
{
|
||||||
|
public interface IMediaCoverProxy
|
||||||
|
{
|
||||||
|
string RegisterUrl(string url);
|
||||||
|
|
||||||
|
string GetUrl(string hash);
|
||||||
|
byte[] GetImage(string hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MediaCoverProxy : IMediaCoverProxy
|
||||||
|
{
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
private readonly ICached<string> _cache;
|
||||||
|
|
||||||
|
public MediaCoverProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, ICacheManager cacheManager)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_configFileProvider = configFileProvider;
|
||||||
|
_cache = cacheManager.GetCache<string>(GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RegisterUrl(string url)
|
||||||
|
{
|
||||||
|
var hash = url.SHA256Hash();
|
||||||
|
|
||||||
|
_cache.Set(hash, url, TimeSpan.FromHours(24));
|
||||||
|
|
||||||
|
_cache.ClearExpired();
|
||||||
|
|
||||||
|
var fileName = Path.GetFileName(url);
|
||||||
|
return _configFileProvider.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUrl(string hash)
|
||||||
|
{
|
||||||
|
var result = _cache.Find(hash);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException("Url no longer in cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetImage(string hash)
|
||||||
|
{
|
||||||
|
var url = GetUrl(hash);
|
||||||
|
|
||||||
|
var request = new HttpRequest(url);
|
||||||
|
|
||||||
|
return _httpClient.Get(request).ResponseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.MediaCover
|
||||||
IHandleAsync<AlbumDeletedEvent>,
|
IHandleAsync<AlbumDeletedEvent>,
|
||||||
IMapCoversToLocal
|
IMapCoversToLocal
|
||||||
{
|
{
|
||||||
|
private readonly IMediaCoverProxy _mediaCoverProxy;
|
||||||
private readonly IImageResizer _resizer;
|
private readonly IImageResizer _resizer;
|
||||||
private readonly IAlbumService _albumService;
|
private readonly IAlbumService _albumService;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
@ -45,7 +46,8 @@ namespace NzbDrone.Core.MediaCover
|
||||||
// So limit the number of concurrent resizing tasks
|
// So limit the number of concurrent resizing tasks
|
||||||
private static SemaphoreSlim _semaphore = new SemaphoreSlim((int)Math.Ceiling(Environment.ProcessorCount / 2.0));
|
private static SemaphoreSlim _semaphore = new SemaphoreSlim((int)Math.Ceiling(Environment.ProcessorCount / 2.0));
|
||||||
|
|
||||||
public MediaCoverService(IImageResizer resizer,
|
public MediaCoverService(IMediaCoverProxy mediaCoverProxy,
|
||||||
|
IImageResizer resizer,
|
||||||
IAlbumService albumService,
|
IAlbumService albumService,
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
|
@ -55,6 +57,7 @@ namespace NzbDrone.Core.MediaCover
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
|
_mediaCoverProxy = mediaCoverProxy;
|
||||||
_resizer = resizer;
|
_resizer = resizer;
|
||||||
_albumService = albumService;
|
_albumService = albumService;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
@ -83,23 +86,37 @@ namespace NzbDrone.Core.MediaCover
|
||||||
|
|
||||||
public void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable<MediaCover> covers)
|
public void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable<MediaCover> covers)
|
||||||
{
|
{
|
||||||
foreach (var mediaCover in covers)
|
if (entityId == 0)
|
||||||
{
|
{
|
||||||
var filePath = GetCoverPath(entityId, coverEntity, mediaCover.CoverType, mediaCover.Extension, null);
|
// Artist isn't in Lidarr yet, map via a proxy to circument referrer issues
|
||||||
|
foreach (var mediaCover in covers)
|
||||||
if (coverEntity == MediaCoverEntity.Album)
|
|
||||||
{
|
{
|
||||||
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/Albums/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + mediaCover.Extension;
|
mediaCover.RemoteUrl = mediaCover.Url;
|
||||||
|
mediaCover.Url = _mediaCoverProxy.RegisterUrl(mediaCover.RemoteUrl);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var mediaCover in covers)
|
||||||
{
|
{
|
||||||
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + mediaCover.Extension;
|
var filePath = GetCoverPath(entityId, coverEntity, mediaCover.CoverType, mediaCover.Extension, null);
|
||||||
}
|
|
||||||
|
|
||||||
if (_diskProvider.FileExists(filePath))
|
mediaCover.RemoteUrl = mediaCover.Url;
|
||||||
{
|
|
||||||
var lastWrite = _diskProvider.FileGetLastWrite(filePath);
|
if (coverEntity == MediaCoverEntity.Album)
|
||||||
mediaCover.Url += "?lastWrite=" + lastWrite.Ticks;
|
{
|
||||||
|
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/Albums/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + mediaCover.Extension;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + mediaCover.Extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_diskProvider.FileExists(filePath))
|
||||||
|
{
|
||||||
|
var lastWrite = _diskProvider.FileGetLastWrite(filePath);
|
||||||
|
mediaCover.Url += "?lastWrite=" + lastWrite.Ticks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +153,10 @@ namespace NzbDrone.Core.MediaCover
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (HttpException e)
|
||||||
|
{
|
||||||
|
_logger.Warn("Couldn't download media cover for {0}. {1}", artist, e.Message);
|
||||||
|
}
|
||||||
catch (WebException e)
|
catch (WebException e)
|
||||||
{
|
{
|
||||||
_logger.Warn("Couldn't download media cover for {0}. {1}", artist, e.Message);
|
_logger.Warn("Couldn't download media cover for {0}. {1}", artist, e.Message);
|
||||||
|
|
|
@ -291,7 +291,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
||||||
foreach (var albumImport in albumImports)
|
foreach (var albumImport in albumImports)
|
||||||
{
|
{
|
||||||
var release = albumImport.First().ImportDecision.Item.Release;
|
var release = albumImport.First().ImportDecision.Item.Release;
|
||||||
var album = albumImport.First().ImportDecision.Item.Album;
|
var album = _albumService.GetAlbum(albumImport.First().ImportDecision.Item.Album.Id);
|
||||||
var artist = albumImport.First().ImportDecision.Item.Artist;
|
var artist = albumImport.First().ImportDecision.Item.Artist;
|
||||||
|
|
||||||
if (albumImport.Where(e => e.Errors.Count == 0).ToList().Count > 0 && artist != null && album != null)
|
if (albumImport.Where(e => e.Errors.Count == 0).ToList().Count > 0 && artist != null && album != null)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue