2024-06-15 04:38:38 +00:00
|
|
|
using Microsoft.Playwright;
|
2024-02-04 22:55:46 +00:00
|
|
|
using System.IO;
|
|
|
|
|
|
2024-01-03 19:40:41 +00:00
|
|
|
namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver;
|
2024-01-03 04:43:37 +00:00
|
|
|
|
|
|
|
|
public class PlaywrightInstance : IDisposable
|
|
|
|
|
{
|
|
|
|
|
IPlaywright _playwright;
|
2024-02-12 17:11:05 +00:00
|
|
|
Dictionary<string, IBrowserContext> _contexts = new Dictionary<string, IBrowserContext>();
|
2024-06-15 04:38:38 +00:00
|
|
|
Dictionary<string, List<IPage>> _pages = new Dictionary<string, List<IPage>>();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ContextId and BrowserContext
|
|
|
|
|
/// </summary>
|
2024-04-09 03:38:56 +00:00
|
|
|
public Dictionary<string, IBrowserContext> Contexts => _contexts;
|
2024-01-03 04:43:37 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// ContextId and Pages
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Dictionary<string, List<IPage>> Pages => _pages;
|
|
|
|
|
|
|
|
|
|
public IPage GetPage(string id, string? pattern = null)
|
2024-02-06 05:05:52 +00:00
|
|
|
{
|
2024-02-12 17:11:05 +00:00
|
|
|
InitInstance(id).Wait();
|
|
|
|
|
return _contexts[id].Pages.LastOrDefault();
|
2024-02-06 05:05:52 +00:00
|
|
|
}
|
2024-01-03 04:43:37 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
public async Task<IBrowserContext> InitInstance(string ctxId)
|
2024-01-06 03:24:13 +00:00
|
|
|
{
|
|
|
|
|
if (_playwright == null)
|
|
|
|
|
{
|
|
|
|
|
_playwright = await Playwright.CreateAsync();
|
2024-02-04 22:55:46 +00:00
|
|
|
}
|
2024-06-15 04:38:38 +00:00
|
|
|
return await InitContext(ctxId);
|
2024-02-12 17:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
public async Task<IBrowserContext> InitContext(string ctxId)
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
if (_contexts.ContainsKey(ctxId))
|
|
|
|
|
return _contexts[ctxId];
|
2024-04-09 03:38:56 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
string tempFolderPath = $"{Path.GetTempPath()}\\playwright\\{ctxId}";
|
2024-04-09 03:38:56 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
_contexts[ctxId] = await _playwright.Chromium.LaunchPersistentContextAsync(tempFolderPath, new BrowserTypeLaunchPersistentContextOptions
|
2024-02-04 22:55:46 +00:00
|
|
|
{
|
2024-02-24 16:15:26 +00:00
|
|
|
#if DEBUG
|
|
|
|
|
Headless = false,
|
|
|
|
|
#else
|
2024-02-15 21:01:22 +00:00
|
|
|
Headless = true,
|
2024-02-24 16:15:26 +00:00
|
|
|
#endif
|
2024-02-12 17:11:05 +00:00
|
|
|
Channel = "chrome",
|
2024-06-26 12:48:15 +00:00
|
|
|
ViewportSize = new ViewportSize
|
|
|
|
|
{
|
|
|
|
|
Width = 1600,
|
|
|
|
|
Height = 900
|
|
|
|
|
},
|
2024-06-10 15:51:14 +00:00
|
|
|
IgnoreDefaultArgs =
|
|
|
|
|
[
|
2024-05-17 23:45:18 +00:00
|
|
|
"--enable-automation",
|
2024-06-10 15:51:14 +00:00
|
|
|
],
|
2024-06-15 04:38:38 +00:00
|
|
|
Args =
|
2024-06-10 15:51:14 +00:00
|
|
|
[
|
2024-04-16 04:48:22 +00:00
|
|
|
"--disable-infobars",
|
2024-06-10 15:51:14 +00:00
|
|
|
"--test-type"
|
2024-04-16 04:48:22 +00:00
|
|
|
// "--start-maximized"
|
2024-06-10 15:51:14 +00:00
|
|
|
]
|
2024-02-12 17:11:05 +00:00
|
|
|
});
|
2024-06-15 04:38:38 +00:00
|
|
|
_pages[ctxId] = new List<IPage>();
|
2024-02-02 04:16:57 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
_contexts[ctxId].Page += async (sender, page) =>
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
_pages[ctxId].Add(page);
|
|
|
|
|
page.Close += async (sender, e) =>
|
2024-02-04 22:55:46 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
_pages[ctxId].Remove(e);
|
2024-02-12 17:11:05 +00:00
|
|
|
Serilog.Log.Information($"Page is closed: {e.Url}");
|
2024-02-04 22:55:46 +00:00
|
|
|
};
|
2024-06-15 04:38:38 +00:00
|
|
|
Serilog.Log.Information($"New page is created: {page.Url}");
|
2024-06-21 18:23:18 +00:00
|
|
|
|
|
|
|
|
/*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());
|
|
|
|
|
}
|
|
|
|
|
};*/
|
2024-02-12 17:11:05 +00:00
|
|
|
};
|
2024-02-04 22:55:46 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
_contexts[ctxId].Close += async (sender, e) =>
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
|
|
|
|
Serilog.Log.Warning($"Playwright browser context is closed");
|
2024-06-15 04:38:38 +00:00
|
|
|
_pages.Remove(ctxId);
|
|
|
|
|
_contexts.Remove(ctxId);
|
2024-02-12 17:11:05 +00:00
|
|
|
};
|
2024-04-09 03:38:56 +00:00
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
return _contexts[ctxId];
|
2024-02-12 17:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-21 18:23:18 +00:00
|
|
|
public async Task<IPage> NewPage(string ctxId, DataFetched? fetched)
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
await InitContext(ctxId);
|
|
|
|
|
var page = await _contexts[ctxId].NewPageAsync();
|
2024-06-21 18:23:18 +00:00
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
return page;
|
2024-02-12 17:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Wait page and network until timeout in seconds
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ctxId"></param>
|
|
|
|
|
/// <param name="timeout">seconds</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task Wait(string ctxId, int timeout = 60)
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
foreach (var page in _pages[ctxId])
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-02-15 21:55:50 +00:00
|
|
|
await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
|
2024-06-15 04:38:38 +00:00
|
|
|
await page.WaitForLoadStateAsync(LoadState.NetworkIdle, new PageWaitForLoadStateOptions
|
|
|
|
|
{
|
|
|
|
|
Timeout = 1000 * timeout
|
|
|
|
|
});
|
2024-02-12 17:11:05 +00:00
|
|
|
}
|
2024-02-12 21:49:18 +00:00
|
|
|
await Task.Delay(100);
|
2024-02-12 17:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
public async Task Close(string ctxId)
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
if (_contexts.ContainsKey(ctxId))
|
2024-02-12 17:11:05 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
await _contexts[ctxId].CloseAsync();
|
2024-01-06 03:24:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 04:38:38 +00:00
|
|
|
public async Task CloseCurrentPage(string ctxId)
|
2024-04-09 03:38:56 +00:00
|
|
|
{
|
2024-06-15 04:38:38 +00:00
|
|
|
var pages = _pages[ctxId].ToArray();
|
|
|
|
|
for (var i = 0; i < pages.Length; i++)
|
2024-04-09 03:38:56 +00:00
|
|
|
{
|
2024-06-22 22:04:03 +00:00
|
|
|
var page = _pages[ctxId].FirstOrDefault();
|
|
|
|
|
if (page != null)
|
|
|
|
|
{
|
|
|
|
|
await page.CloseAsync();
|
|
|
|
|
}
|
2024-04-09 03:38:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-03 04:43:37 +00:00
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2024-02-12 17:11:05 +00:00
|
|
|
_contexts.Clear();
|
2024-05-17 23:45:18 +00:00
|
|
|
_playwright?.Dispose();
|
2024-01-03 04:43:37 +00:00
|
|
|
}
|
|
|
|
|
}
|