add: 元数据提取

This commit is contained in:
jackfiled 2024-01-19 20:33:41 +08:00
parent 2d75c5c9a7
commit cc3e7f1e4b
7 changed files with 77 additions and 9 deletions

View File

@ -5,6 +5,7 @@ using Microsoft.Extensions.Diagnostics.Metrics;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using YaeBlog.Core.Extensions; using YaeBlog.Core.Extensions;
using YamlDotNet.Serialization;
namespace YaeBlog.Core.Builder; namespace YaeBlog.Core.Builder;
@ -12,12 +13,15 @@ public sealed class BlogApplicationBuilder : IHostApplicationBuilder
{ {
private readonly HostApplicationBuilder _hostApplicationBuilder; private readonly HostApplicationBuilder _hostApplicationBuilder;
public MarkdownPipelineBuilder MarkdigPipelineBuilder { get; set; } public MarkdownPipelineBuilder MarkdigPipelineBuilder { get; }
public DeserializerBuilder YamlDeserializerBuilder { get; }
internal BlogApplicationBuilder(BlogApplicationOptions options) internal BlogApplicationBuilder(BlogApplicationOptions options)
{ {
ConfigurationManager configuration = new(); ConfigurationManager configuration = new();
MarkdigPipelineBuilder = new MarkdownPipelineBuilder(); MarkdigPipelineBuilder = new MarkdownPipelineBuilder();
YamlDeserializerBuilder = new DeserializerBuilder();
_hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
{ {

View File

@ -4,6 +4,8 @@ using Microsoft.Extensions.DependencyInjection;
using YaeBlog.Core.Builder; using YaeBlog.Core.Builder;
using YaeBlog.Core.Models; using YaeBlog.Core.Models;
using YaeBlog.Core.Services; using YaeBlog.Core.Services;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace YaeBlog.Core.Extensions; namespace YaeBlog.Core.Extensions;
@ -19,11 +21,17 @@ internal static class BlogApplicationExtension
builder.Services.Configure<BlogOptions>( builder.Services.Configure<BlogOptions>(
builder.Configuration.GetSection(BlogOptions.OptionName)); builder.Configuration.GetSection(BlogOptions.OptionName));
builder.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance);
builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties();
builder.Services.AddSingleton<MarkdownPipeline>(
_ => builder.MarkdigPipelineBuilder.Build());
builder.Services.AddSingleton<IDeserializer>(
_ => builder.YamlDeserializerBuilder.Build());
builder.Services.AddHostedService<BlogHostedService>(); builder.Services.AddHostedService<BlogHostedService>();
builder.Services.AddSingleton<EssayScanService>(); builder.Services.AddSingleton<EssayScanService>();
builder.Services.AddSingleton<RendererService>(); builder.Services.AddSingleton<RendererService>();
builder.Services.AddSingleton<MarkdownPipeline>(
_ => builder.MarkdigPipelineBuilder.Build());
builder.Services.AddSingleton<EssayContentService>(); builder.Services.AddSingleton<EssayContentService>();
return builder; return builder;

View File

@ -4,5 +4,5 @@ public class BlogContent
{ {
public required string FileName { get; init; } public required string FileName { get; init; }
public required string FileContent { get; init; } public required string FileContent { get; set; }
} }

View File

@ -6,5 +6,12 @@ public class BlogEssay
public required DateTime PublishTime { get; init; } public required DateTime PublishTime { get; init; }
public List<string> Tags { get; } = [];
public required string HtmlContent { get; init; } public required string HtmlContent { get; init; }
public override string ToString()
{
return $"{Title}-{PublishTime}";
}
} }

View File

@ -0,0 +1,10 @@
namespace YaeBlog.Core.Models;
public class MarkdownMetadata
{
public string? Title { get; set; }
public DateTime? Date { get; set; }
public List<string> Tags { get; set; } = [];
}

View File

@ -1,14 +1,15 @@
using System.Collections.Concurrent; using Markdig;
using Markdig;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using YaeBlog.Core.Exceptions; using YaeBlog.Core.Exceptions;
using YaeBlog.Core.Models; using YaeBlog.Core.Models;
using YamlDotNet.Serialization;
namespace YaeBlog.Core.Services; namespace YaeBlog.Core.Services;
public class RendererService(ILogger<RendererService> logger, public class RendererService(ILogger<RendererService> logger,
EssayScanService essayScanService, EssayScanService essayScanService,
MarkdownPipeline markdownPipeline, MarkdownPipeline markdownPipeline,
IDeserializer yamlDeserializer,
EssayContentService essayContentService) EssayContentService essayContentService)
{ {
public async Task RenderAsync() public async Task RenderAsync()
@ -17,19 +18,56 @@ public class RendererService(ILogger<RendererService> logger,
Parallel.ForEach(contents, content => Parallel.ForEach(contents, content =>
{ {
logger.LogDebug("Render markdown file {}.", content.FileName); MarkdownMetadata? metadata = TryParseMetadata(content);
BlogEssay essay = new() BlogEssay essay = new()
{ {
Title = content.FileName, Title = metadata?.Title ?? content.FileName,
PublishTime = DateTime.Now, PublishTime = metadata?.Date ?? DateTime.Now,
HtmlContent = Markdown.ToHtml(content.FileContent, markdownPipeline) HtmlContent = Markdown.ToHtml(content.FileContent, markdownPipeline)
}; };
if (metadata is not null)
{
essay.Tags.AddRange(essay.Tags);
}
if (!essayContentService.TryAdd(essay.Title, essay)) if (!essayContentService.TryAdd(essay.Title, essay))
{ {
throw new BlogFileException( throw new BlogFileException(
$"There are two essays with the same name: '{content.FileName}'."); $"There are two essays with the same name: '{content.FileName}'.");
} }
logger.LogDebug("Render markdown file {}.", essay);
logger.LogDebug("{}", essay.HtmlContent);
}); });
} }
private MarkdownMetadata? TryParseMetadata(BlogContent content)
{
string fileContent = content.FileContent.Trim();
if (!fileContent.StartsWith("---"))
{
return null;
}
// 移除起始的---
fileContent = fileContent[3..];
int lastPos = fileContent.IndexOf("---", StringComparison.Ordinal);
if (lastPos is -1 or 0)
{
return null;
}
string yamlContent = fileContent[..lastPos];
MarkdownMetadata metadata = yamlDeserializer.Deserialize<MarkdownMetadata>(yamlContent);
logger.LogDebug("Title: {}, Publish Date: {}.",
metadata.Title, metadata.Date);
// 返回去掉元数据之后的文本
lastPos += 3;
content.FileContent = fileContent[lastPos..];
return null;
}
} }

View File

@ -15,6 +15,7 @@
<PackageReference Include="Markdig" Version="0.34.0" /> <PackageReference Include="Markdig" Version="0.34.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="YamlDotNet" Version="13.7.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>