* Updated dependencies

* Updated the default key to be 256 bits to meet security requirements.

* Added basic implementation of web link resolving favicon. Needs lots more work and testing on all OSes.

* Implemented ability to see links and click on them for an individual chapter.

* Hooked up the ability to set Series web links.

* Render out the web link

* Refactored out the favicon so there is a backup in case it fails. Refactored the baseline image placeholders to be dark mode since that is the default.

* Added Robbie's nice error weblink fallbacks.
This commit is contained in:
Joe Milazzo 2023-05-11 16:27:04 -05:00 committed by GitHub
parent 23fde65a7b
commit bd8a1821a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 4272 additions and 80 deletions

View file

@ -22,6 +22,7 @@ public interface IDirectoryService
string TempDirectory { get; }
string ConfigDirectory { get; }
string SiteThemeDirectory { get; }
string FaviconDirectory { get; }
/// <summary>
/// Original BookmarkDirectory. Only used for resetting directory. Use <see cref="ServerSettingKey.BackupDirectory"/> for actual path.
/// </summary>
@ -75,6 +76,7 @@ public class DirectoryService : IDirectoryService
public string ConfigDirectory { get; }
public string BookmarkDirectory { get; }
public string SiteThemeDirectory { get; }
public string FaviconDirectory { get; }
private readonly ILogger<DirectoryService> _logger;
private const RegexOptions MatchOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase;
@ -98,6 +100,7 @@ public class DirectoryService : IDirectoryService
ConfigDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config");
BookmarkDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "bookmarks");
SiteThemeDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "themes");
FaviconDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "favicons");
ExistOrCreate(SiteThemeDirectory);
ExistOrCreate(CoverImageDirectory);
@ -105,6 +108,7 @@ public class DirectoryService : IDirectoryService
ExistOrCreate(LogDirectory);
ExistOrCreate(TempDirectory);
ExistOrCreate(BookmarkDirectory);
ExistOrCreate(FaviconDirectory);
}
/// <summary>

View file

@ -1,10 +1,18 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
using Flurl;
using Flurl.Http;
using Microsoft.Extensions.Logging;
using NetVips;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using Image = NetVips.Image;
using Size = SixLabors.ImageSharp.Size;
namespace API.Services;
@ -49,6 +57,7 @@ public interface IImageService
Task<string> ConvertToWebP(string filePath, string outputPath);
Task<bool> IsImage(string filePath);
Task<string> DownloadFaviconAsync(string url);
}
public class ImageService : IImageService
@ -177,6 +186,36 @@ public class ImageService : IImageService
return false;
}
public async Task<string> DownloadFaviconAsync(string url)
{
// Parse the URL to get the domain (including subdomain)
var uri = new Uri(url);
var domain = uri.Host;
var baseUrl = uri.Scheme + "://" + uri.Host;
try
{
// Download the favicon.ico file using Flurl
var faviconStream = await baseUrl
.AppendPathSegment("favicon.ico")
.AllowHttpStatus("2xx")
.GetStreamAsync();
// Create the destination file path
var filename = $"{domain}.png";
using var icon = new Icon(faviconStream);
using var bitmap = icon.ToBitmap();
bitmap.Save(Path.Combine(_directoryService.FaviconDirectory, filename), ImageFormat.Png);
_logger.LogDebug("Favicon.png for {Domain} downloaded and saved successfully", domain);
return filename;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error downloading favicon.png for ${Domain}", domain);
throw;
}
}
/// <inheritdoc />
public string CreateThumbnailFromBase64(string encodedImage, string fileName, bool saveAsWebP = false, int thumbnailWidth = ThumbnailWidth)
@ -257,6 +296,11 @@ public class ImageService : IImageService
return $"thumbnail{chapterId}";
}
public static string GetWebLinkFormat(string url)
{
return $"{new Uri(url).Host}.png";
}
public static string CreateMergedImage(List<string> coverImages, string dest)
{

View file

@ -120,6 +120,16 @@ public class SeriesService : ISeriesService
series.Metadata.LanguageLocked = true;
}
if (!string.IsNullOrEmpty(updateSeriesMetadataDto.SeriesMetadata?.WebLinks))
{
series.Metadata.WebLinks = string.Join(",", updateSeriesMetadataDto.SeriesMetadata?.WebLinks
.Split(",")
.Where(s => !string.IsNullOrEmpty(s))
.Select(s => s.Trim())!
);
}
series.Metadata.CollectionTags ??= new List<CollectionTag>();
UpdateCollectionsList(updateSeriesMetadataDto.CollectionTags, series, allCollectionTags, (tag) =>
{

View file

@ -120,6 +120,8 @@ public class BackupService : IBackupService
await SendProgress(0.75F, "Copying themes");
CopyThemesToBackupDirectory(tempDirectory);
await SendProgress(0.85F, "Copying favicons");
CopyFaviconsToBackupDirectory(tempDirectory);
try
{
@ -141,6 +143,11 @@ public class BackupService : IBackupService
_directoryService.CopyFilesToDirectory(files, _directoryService.FileSystem.Path.Join(tempDirectory, "logs"));
}
private void CopyFaviconsToBackupDirectory(string tempDirectory)
{
_directoryService.CopyDirectoryToDirectory(_directoryService.FaviconDirectory, tempDirectory);
}
private async Task CopyCoverImagesToBackupDirectory(string tempDirectory)
{
var outputTempDir = Path.Join(tempDirectory, "covers");

View file

@ -708,12 +708,19 @@ public class ProcessSeries : IProcessSeries
chapter.StoryArcNumber = comicInfo.StoryArcNumber;
}
if (comicInfo.AlternateCount > 0)
{
chapter.AlternateCount = comicInfo.AlternateCount;
}
if (!string.IsNullOrEmpty(comicInfo.Web))
{
chapter.WebLinks = string.Join(",", comicInfo.Web
.Split(",")
.Where(s => !string.IsNullOrEmpty(s))
.Select(s => s.Trim())
);
}
if (comicInfo.Count > 0)
{

View file

@ -49,13 +49,12 @@ public class TokenService : ITokenService
claims.AddRange(roles.Select(role => new Claim(Role, role)));
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var credentials = new SigningCredentials(_key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor()
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddDays(14),
SigningCredentials = creds
SigningCredentials = credentials
};
var tokenHandler = new JwtSecurityTokenHandler();