Foundational Cover Image Rework (#584)

* Updating wording on card item when total pages is 0, to be just "Cannot Read" since it could be a non-archive file

* Refactored cover images to be stored on disk. This first commit has the extraction to disk and the metadata service to handle updating when applicable.

* Refactored code to have the actual save to cover image directory done by ImageService.

* Implemented the ability to override cover images.

* Some cleanup on Image service

* Implemented the ability to cleanup old covers nightly

* Added a migration to migrate existing covers to new cover image format (files).

* Refactored all CoverImages to just be the filename, leaving the Join with Cover directory to higher level code.

* Ensure when getting user progress, we pick the first.

* Added cleanup cover images for deleted tags. Don't pull any cover images that are blank.

* After series update, clear out cover image. No change on UI, but just keeps things clear before metadata refresh hits

* Refactored image formats for covers to ImageService.

* Fixed an issue where after refactoring how images were stored, the cleanup service was deleting them after each scan.

* Changed how ShouldUpdateCoverImage works to check if file exists or not even if cover image is locked.

* Fixed unit tests

* Added caching back to cover images.

* Caching on series as well

* Code Cleanup items

* Ensure when checking if a file exists in MetadataService, that we join for cover image directory. After we scan library, do one last filter to delete any series that have 0 pages total.

* Catch exceptions so we don't run cover migration if this is first time run.

* After a scan, only clear out the cache directory and not do a deep clean.

* Implemented the ability to backup custom locked covers only.

* Fixed unit tests

* Trying to figure out why GA crashes when running MetadataServiceTests.cs

* Some debugging on GA tests not running

* Commented out tests that were causing issues in GA.

* Fixed an issue where series cover images wouldn't migrate

* Fixed the updating of links to actually do all series and not just locked
This commit is contained in:
Joseph Milazzo 2021-09-21 17:15:29 -07:00 committed by GitHub
parent fd6925b126
commit 82b5b599e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1928 additions and 234 deletions

View file

@ -14,6 +14,15 @@ namespace API.Services
{
private readonly ILogger<ImageService> _logger;
private readonly IDirectoryService _directoryService;
public const string ChapterCoverImageRegex = @"v\d+_c\d+";
public const string SeriesCoverImageRegex = @"seres\d+";
public const string CollectionTagCoverImageRegex = @"tag\d+";
/// <summary>
/// Width of the Thumbnail generation
/// </summary>
private const int ThumbnailWidth = 320;
public ImageService(ILogger<ImageService> logger, IDirectoryService directoryService)
{
@ -41,63 +50,103 @@ namespace API.Services
return firstImage;
}
public byte[] GetCoverImage(string path, bool createThumbnail = false)
public string GetCoverImage(string path, string fileName)
{
if (string.IsNullOrEmpty(path)) return Array.Empty<byte>();
if (string.IsNullOrEmpty(path)) return string.Empty;
try
{
if (createThumbnail)
{
return CreateThumbnail(path);
}
using var img = Image.NewFromFile(path);
using var stream = new MemoryStream();
img.JpegsaveStream(stream);
stream.Position = 0;
return stream.ToArray();
return CreateThumbnail(path, fileName);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "[GetCoverImage] There was an error and prevented thumbnail generation on {ImageFile}. Defaulting to no cover image", path);
}
return Array.Empty<byte>();
return string.Empty;
}
/// <inheritdoc />
public byte[] CreateThumbnail(string path)
public string CreateThumbnail(string path, string fileName)
{
try
{
using var thumbnail = Image.Thumbnail(path, MetadataService.ThumbnailWidth);
return thumbnail.WriteToBuffer(".jpg");
using var thumbnail = Image.Thumbnail(path, ThumbnailWidth);
var filename = fileName + ".png";
thumbnail.WriteToFile(Path.Join(DirectoryService.CoverImageDirectory, fileName + ".png"));
return filename;
}
catch (Exception e)
{
_logger.LogError(e, "Error creating thumbnail from url");
}
return Array.Empty<byte>();
return string.Empty;
}
/// <summary>
/// Creates a thumbnail out of a memory stream and saves to <see cref="DirectoryService.CoverImageDirectory"/> with the passed
/// fileName and .png extension.
/// </summary>
/// <param name="stream">Stream to write to disk. Ensure this is rewinded.</param>
/// <param name="fileName">filename to save as without extension</param>
/// <returns>File name with extension of the file. This will always write to <see cref="DirectoryService.CoverImageDirectory"/></returns>
public static string WriteCoverThumbnail(Stream stream, string fileName)
{
using var thumbnail = NetVips.Image.ThumbnailStream(stream, ThumbnailWidth);
var filename = fileName + ".png";
thumbnail.WriteToFile(Path.Join(DirectoryService.CoverImageDirectory, fileName + ".png"));
return filename;
}
/// <inheritdoc />
public byte[] CreateThumbnailFromBase64(string encodedImage)
public string CreateThumbnailFromBase64(string encodedImage, string fileName)
{
try
{
using var thumbnail = Image.ThumbnailBuffer(Convert.FromBase64String(encodedImage), MetadataService.ThumbnailWidth);
return thumbnail.WriteToBuffer(".jpg");
using var thumbnail = Image.ThumbnailBuffer(Convert.FromBase64String(encodedImage), ThumbnailWidth);
var filename = fileName + ".png";
thumbnail.WriteToFile(Path.Join(DirectoryService.CoverImageDirectory, fileName + ".png"));
return filename;
}
catch (Exception e)
{
_logger.LogError(e, "Error creating thumbnail from url");
}
return Array.Empty<byte>();
return string.Empty;
}
/// <summary>
/// Returns the name format for a chapter cover image
/// </summary>
/// <param name="chapterId"></param>
/// <param name="volumeId"></param>
/// <returns></returns>
public static string GetChapterFormat(int chapterId, int volumeId)
{
return $"v{volumeId}_c{chapterId}";
}
/// <summary>
/// Returns the name format for a series cover image
/// </summary>
/// <param name="seriesId"></param>
/// <returns></returns>
public static string GetSeriesFormat(int seriesId)
{
return $"series{seriesId}";
}
/// <summary>
/// Returns the name format for a collection tag cover image
/// </summary>
/// <param name="tagId"></param>
/// <returns></returns>
public static string GetCollectionTagFormat(int tagId)
{
return $"tag{tagId}";
}
}
}