Skip to content

Instantly share code, notes, and snippets.

@tcortega
Created October 30, 2023 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcortega/227bfcf7e33a7565002d0b7332c0174d to your computer and use it in GitHub Desktop.
Save tcortega/227bfcf7e33a7565002d0b7332c0174d to your computer and use it in GitHub Desktop.
[RegisterSingleton]
public class LabelEngine
{
private readonly ILogger _logger;
private readonly Timer _timer;
private readonly AsyncReaderWriterLock _lock = new();
private IBrowser _browser = null!;
private readonly string _browserPath;
public LabelEngine(ILogger logger, IOptions<LabelEngineOptions> options)
{
_logger = logger;
_browserPath = options.Value.BrowserPath;
_timer = new Timer(RestartBrowser, null, TimeSpan.FromMinutes(20), TimeSpan.FromMinutes(20));
}
public Task InitAsync() => StartBrowser();
public async Task<LabelResult> GenerateLabel(LabelFieldsModel inputData, string templateFile)
{
using (await _lock.ReaderLockAsync())
{
Guard.IsNotNullOrEmpty(templateFile, nameof(templateFile));
var templatePath = FileManager.GetTemplatePath(templateFile);
if (!File.Exists(templatePath))
{
_logger.Error("Template file not found: {TemplatePath}", templatePath);
throw new FileNotFoundException($"Template file not found: {templatePath}");
}
var content = await File.ReadAllTextAsync(templatePath);
var template = Template.ParseLiquid(content);
var result = await template.RenderAsync(inputData, member => member.Name);
if (result is null)
{
throw new InvalidOperationException("An error happened when attempting to build the render the template.");
}
var labelId = Guid.NewGuid();
using var htmlLabel = FileManager.CreateTempFileEntry("html", labelId);
await File.WriteAllTextAsync(htmlLabel.FilePath, result);
await using var page = await _browser.NewPageAsync();
await page.GoToAsync($"file://{htmlLabel.FilePath}");
var labelOutputPath = FileManager.GetLabelPath(labelId);
await page.PdfAsync(labelOutputPath, new()
{
Format = PaperFormat.A4
});
return new()
{
LabelId = labelId, TrackingNumber = inputData.TrackingNumber
};
}
}
private async Task StartBrowser()
{
var launchOptions = new LaunchOptions
{
ExecutablePath = _browserPath, Headless = true, Args = new[]
{
"--disable-gpu", "--no-sandbox", "--disable-software-rasterizer", "--disable-dev-shm-usage", "--disable-extensions", "--disable-background-networking", "--disable-sync", "--metrics-recording-only", "--disable-default-apps", "--mute-audio"
}
};
_browser = await Puppeteer.LaunchAsync(launchOptions);
}
private async void RestartBrowser(object? state)
{
using (await _lock.WriterLockAsync())
{
try
{
await _browser.CloseAsync();
await StartBrowser();
}
catch (Exception ex)
{
_logger.Error(ex, "Error restarting browser");
}
}
}
}
@remixtedi
Copy link

@tcortega Looks good. Which library are you using for AsyncReaderWriterLock?

@tcortega
Copy link
Author

tcortega commented Nov 1, 2023

@tcortega Looks good. Which library are you using for AsyncReaderWriterLock?

https://github.com/StephenCleary/AsyncEx

Nito.AsyncEx.Coordination more specifically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment