using Microsoft.Playwright; using System.IO; namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public class PlaywrightInstance : IDisposable { IPlaywright _playwright; Dictionary _contexts = new Dictionary(); Dictionary> _pages = new Dictionary>(); /// /// ContextId and BrowserContext /// public Dictionary Contexts => _contexts; /// /// ContextId and Pages /// public Dictionary> Pages => _pages; public IPage GetPage(string id, string? pattern = null) { InitInstance(id).Wait(); return _contexts[id].Pages.LastOrDefault(); } public async Task InitInstance(string ctxId) { if (_playwright == null) { _playwright = await Playwright.CreateAsync(); } return await InitContext(ctxId); } public async Task InitContext(string ctxId) { if (_contexts.ContainsKey(ctxId)) return _contexts[ctxId]; string tempFolderPath = $"{Path.GetTempPath()}\\playwright\\{ctxId}"; _contexts[ctxId] = await _playwright.Chromium.LaunchPersistentContextAsync(tempFolderPath, new BrowserTypeLaunchPersistentContextOptions { #if DEBUG Headless = false, #else Headless = true, #endif Channel = "chrome", ViewportSize = new ViewportSize { Width = 1600, Height = 900 }, IgnoreDefaultArgs = [ "--enable-automation", ], Args = [ "--disable-infobars", "--test-type" // "--start-maximized" ] }); _pages[ctxId] = new List(); _contexts[ctxId].Page += async (sender, page) => { _pages[ctxId].Add(page); page.Close += async (sender, e) => { _pages[ctxId].Remove(e); Serilog.Log.Information($"Page is closed: {e.Url}"); }; Serilog.Log.Information($"New page is created: {page.Url}"); /*page.Response += async (sender, e) => { Serilog.Log.Information($"Response: {e.Url}"); if (e.Headers.ContainsKey("content-type") && e.Headers["content-type"].Contains("application/json")) { var json = await e.JsonAsync(); Serilog.Log.Information(json.ToString()); } };*/ }; _contexts[ctxId].Close += async (sender, e) => { Serilog.Log.Warning($"Playwright browser context is closed"); _pages.Remove(ctxId); _contexts.Remove(ctxId); }; return _contexts[ctxId]; } public async Task NewPage(string ctxId, DataFetched? fetched) { await InitContext(ctxId); var page = await _contexts[ctxId].NewPageAsync(); if (fetched != null) { page.Response += async (sender, e) => { if (e.Headers.ContainsKey("content-type") && e.Headers["content-type"].Contains("application/json") && e.Request.ResourceType == "fetch") { Serilog.Log.Information($"Response: {e.Url}"); var json = await e.JsonAsync(); fetched(e.Url.ToLower(), JsonSerializer.Serialize(json)); } }; } return page; } /// /// Wait page and network until timeout in seconds /// /// /// seconds /// public async Task Wait(string ctxId, int timeout = 60) { foreach (var page in _pages[ctxId]) { await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); await page.WaitForLoadStateAsync(LoadState.NetworkIdle, new PageWaitForLoadStateOptions { Timeout = 1000 * timeout }); } await Task.Delay(100); } public async Task Close(string ctxId) { if (_contexts.ContainsKey(ctxId)) { await _contexts[ctxId].CloseAsync(); } } public async Task CloseCurrentPage(string ctxId) { var pages = _pages[ctxId].ToArray(); for (var i = 0; i < pages.Length; i++) { var page = _pages[ctxId].FirstOrDefault(); if (page != null) { await page.CloseAsync(); } } } public void Dispose() { _contexts.Clear(); _playwright?.Dispose(); } }