MediaCover Pulls and Fixes

pull/2763/head
Qstick 2 years ago
parent 44f8022fa2
commit 498368e8c5

@ -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);
} }
} }
} }

@ -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()
{ {

@ -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…
Cancel
Save