mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
code style
This commit is contained in:
41
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs
Normal file
41
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareContext.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Core.IO.DataTransfer;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
internal sealed class BridgeShareContext
|
||||
{
|
||||
private readonly CoreWebView2 coreWebView2;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly IClipboardProvider clipboardProvider;
|
||||
private readonly JsonSerializerOptions jsonSerializerOptions;
|
||||
|
||||
public BridgeShareContext(CoreWebView2 coreWebView2, ITaskContext taskContext, HttpClient httpClient, IInfoBarService infoBarService, IClipboardProvider clipboardProvider, JsonSerializerOptions jsonSerializerOptions)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.taskContext = taskContext;
|
||||
this.infoBarService = infoBarService;
|
||||
this.clipboardProvider = clipboardProvider;
|
||||
this.coreWebView2 = coreWebView2;
|
||||
this.jsonSerializerOptions = jsonSerializerOptions;
|
||||
}
|
||||
|
||||
public CoreWebView2 CoreWebView2 { get => coreWebView2; }
|
||||
|
||||
public ITaskContext TaskContext { get => taskContext; }
|
||||
|
||||
public HttpClient HttpClient { get => httpClient; }
|
||||
|
||||
public IInfoBarService InfoBarService { get => infoBarService; }
|
||||
|
||||
public IClipboardProvider ClipboardProvider { get => clipboardProvider; }
|
||||
|
||||
public JsonSerializerOptions JsonSerializerOptions { get => jsonSerializerOptions; }
|
||||
}
|
||||
108
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs
Normal file
108
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeShareImplmentation.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.Web.Bridge.Model;
|
||||
using System.IO;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
internal sealed partial class BridgeShareImplmentation
|
||||
{
|
||||
public static async ValueTask<IJsBridgeResult?> ShareAsync(JsParam<SharePayload> param, BridgeShareContext context)
|
||||
{
|
||||
SharePayload payload = param.Payload;
|
||||
switch (payload.Type)
|
||||
{
|
||||
case "image":
|
||||
{
|
||||
ShareContent content = payload.Content;
|
||||
if (content.ImageUrl is { Length: > 0 } imageUrl)
|
||||
{
|
||||
await ShareFromImageUrlAsync(context, imageUrl).ConfigureAwait(false);
|
||||
}
|
||||
else if (content.ImageBase64 is { } imageBase64)
|
||||
{
|
||||
await ShareFromImageBase64Async(context, imageBase64).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "screenshot":
|
||||
{
|
||||
await context.TaskContext.SwitchToMainThreadAsync();
|
||||
|
||||
// https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot
|
||||
string jsonParameters = """{ "format": "png", "captureBeyondViewport": true }""";
|
||||
string resultJson = await context.CoreWebView2.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", jsonParameters);
|
||||
|
||||
CaptureScreenshotResult? result = JsonSerializer.Deserialize<CaptureScreenshotResult>(resultJson, context.JsonSerializerOptions);
|
||||
ArgumentNullException.ThrowIfNull(result);
|
||||
|
||||
await ShareFromRawPixelDataAsync(context, result.Data).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new JsResult<Dictionary<string, string>>()
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
["type"] = param.Payload.Type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private static async ValueTask ShareFromImageUrlAsync(BridgeShareContext context, string imageUrl)
|
||||
{
|
||||
using (Stream stream = await context.HttpClient.GetStreamAsync(imageUrl).ConfigureAwait(false))
|
||||
{
|
||||
await ShareCoreAsync(context, stream, static (stream, web) => web.CopyToAsync(stream.AsStreamForWrite())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ValueTask ShareFromImageBase64Async(BridgeShareContext context, string base64ImageData)
|
||||
{
|
||||
return ShareFromRawPixelDataAsync(context, Convert.FromBase64String(base64ImageData));
|
||||
}
|
||||
|
||||
private static ValueTask ShareFromRawPixelDataAsync(BridgeShareContext context, byte[] rawPixelData)
|
||||
{
|
||||
return ShareCoreAsync(context, rawPixelData, static (stream, bytes) => stream.AsStreamForWrite().WriteAsync(bytes).AsTask());
|
||||
}
|
||||
|
||||
private static async ValueTask ShareCoreAsync<TData>(BridgeShareContext context, TData data, Func<InMemoryRandomAccessStream, TData, Task> asyncWriteData)
|
||||
{
|
||||
using (InMemoryRandomAccessStream rawPixelDataStream = new())
|
||||
{
|
||||
await asyncWriteData(rawPixelDataStream, data).ConfigureAwait(false);
|
||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(rawPixelDataStream);
|
||||
|
||||
using (InMemoryRandomAccessStream stream = new())
|
||||
{
|
||||
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
|
||||
encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync());
|
||||
await encoder.FlushAsync();
|
||||
|
||||
await context.TaskContext.SwitchToMainThreadAsync();
|
||||
if (context.ClipboardProvider.SetBitmap(stream))
|
||||
{
|
||||
context.InfoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.InfoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CaptureScreenshotResult
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public byte[] Data { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
@@ -84,15 +84,12 @@ internal class MiHoYoJSBridge
|
||||
""";
|
||||
|
||||
private readonly SemaphoreSlim webMessageSemaphore = new(1);
|
||||
private readonly Guid interfaceId = Guid.NewGuid();
|
||||
private readonly Guid bridgeId = Guid.NewGuid();
|
||||
private readonly UserAndUid userAndUid;
|
||||
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly ILogger<MiHoYoJSBridge> logger;
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly IClipboardProvider clipboardProvider;
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2WebMessageReceivedEventArgs> webMessageReceivedEventHandler;
|
||||
private readonly TypedEventHandler<CoreWebView2, CoreWebView2DOMContentLoadedEventArgs> domContentLoadedEventHandler;
|
||||
@@ -109,9 +106,6 @@ internal class MiHoYoJSBridge
|
||||
|
||||
taskContext = serviceProvider.GetRequiredService<ITaskContext>();
|
||||
logger = serviceProvider.GetRequiredService<ILogger<MiHoYoJSBridge>>();
|
||||
infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
|
||||
clipboardProvider = serviceProvider.GetRequiredService<IClipboardProvider>();
|
||||
httpClient = serviceProvider.GetRequiredService<HttpClient>();
|
||||
|
||||
webMessageReceivedEventHandler = OnWebMessageReceived;
|
||||
domContentLoadedEventHandler = OnDOMContentLoaded;
|
||||
@@ -132,11 +126,6 @@ internal class MiHoYoJSBridge
|
||||
coreWebView2 = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual async ValueTask<IJsBridgeResult?> ClosePageAsync(JsParam param)
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
@@ -152,21 +141,11 @@ internal class MiHoYoJSBridge
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调整分享设置
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual IJsBridgeResult? ConfigureShare(JsParam param)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取ActionTicket
|
||||
/// </summary>
|
||||
/// <param name="jsParam">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual async ValueTask<IJsBridgeResult?> GetActionTicketAsync(JsParam<ActionTypePayload> jsParam)
|
||||
{
|
||||
return await serviceProvider
|
||||
@@ -175,11 +154,6 @@ internal class MiHoYoJSBridge
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取账户信息
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual JsResult<Dictionary<string, string>> GetCookieInfo(JsParam param)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(userAndUid.User.LToken);
|
||||
@@ -195,11 +169,6 @@ internal class MiHoYoJSBridge
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取CookieToken
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual async ValueTask<JsResult<Dictionary<string, string>>> GetCookieTokenAsync(JsParam<CookieTokenPayload> param)
|
||||
{
|
||||
IUserService userService = serviceProvider.GetRequiredService<IUserService>();
|
||||
@@ -215,11 +184,6 @@ internal class MiHoYoJSBridge
|
||||
return new() { Data = new() { [Cookie.COOKIE_TOKEN] = userAndUid.User.CookieToken[Cookie.COOKIE_TOKEN] } };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前语言和时区
|
||||
/// </summary>
|
||||
/// <param name="param">param</param>
|
||||
/// <returns>语言与时区</returns>
|
||||
protected virtual JsResult<Dictionary<string, string>> GetCurrentLocale(JsParam<PushPagePayload> param)
|
||||
{
|
||||
MetadataOptions metadataOptions = serviceProvider.GetRequiredService<MetadataOptions>();
|
||||
@@ -234,11 +198,6 @@ internal class MiHoYoJSBridge
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取1代动态密钥
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual JsResult<Dictionary<string, string>> GetDynamicSecrectV1(JsParam param)
|
||||
{
|
||||
DataSignOptions options = DataSignOptions.CreateForGeneration1(SaltType.LK2, true);
|
||||
@@ -251,11 +210,6 @@ internal class MiHoYoJSBridge
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取2代动态密钥
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual JsResult<Dictionary<string, string>> GetDynamicSecrectV2(JsParam<DynamicSecrect2Playload> param)
|
||||
{
|
||||
DataSignOptions options = DataSignOptions.CreateForGeneration2(SaltType.X4, false, param.Payload.Body, param.Payload.GetQueryParam());
|
||||
@@ -268,11 +222,6 @@ internal class MiHoYoJSBridge
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Http请求头
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>Http请求头</returns>
|
||||
protected virtual JsResult<Dictionary<string, string>> GetHttpRequestHeader(JsParam param)
|
||||
{
|
||||
Dictionary<string, string> headers = new()
|
||||
@@ -306,21 +255,11 @@ internal class MiHoYoJSBridge
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取状态栏高度
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>结果</returns>
|
||||
protected virtual JsResult<Dictionary<string, object>> GetStatusBarHeight(JsParam param)
|
||||
{
|
||||
return new() { Data = new() { ["statusBarHeight"] = 0 } };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户基本信息
|
||||
/// </summary>
|
||||
/// <param name="param">参数</param>
|
||||
/// <returns>响应</returns>
|
||||
protected virtual async ValueTask<JsResult<Dictionary<string, object>>> GetUserInfoAsync(JsParam param)
|
||||
{
|
||||
Response<UserFullInfoWrapper> response = await serviceProvider
|
||||
@@ -374,81 +313,18 @@ internal class MiHoYoJSBridge
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual async ValueTask<IJsBridgeResult?> Share(JsParam<SharePayload> param)
|
||||
protected virtual async ValueTask<IJsBridgeResult?> ShareAsync(JsParam<SharePayload> param)
|
||||
{
|
||||
if (param.Payload.Type is "image")
|
||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||
{
|
||||
if (param.Payload.Content.ImageUrl is { } imageUrl)
|
||||
{
|
||||
using (Stream stream = await httpClient.GetStreamAsync(imageUrl).ConfigureAwait(false))
|
||||
{
|
||||
using (InMemoryRandomAccessStream origStream = new())
|
||||
{
|
||||
await stream.CopyToAsync(origStream.AsStreamForWrite()).ConfigureAwait(false);
|
||||
using (InMemoryRandomAccessStream imageStream = new())
|
||||
{
|
||||
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, imageStream);
|
||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(origStream);
|
||||
encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync());
|
||||
await encoder.FlushAsync();
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
if (clipboardProvider.SetBitmap(imageStream))
|
||||
{
|
||||
infoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (param.Payload.Content.ImageBase64 is { } imageBase64)
|
||||
{
|
||||
await ShareImageBase64CoreAsync(imageBase64).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (param.Payload.Type is "screenshot")
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
string base64Json = await coreWebView2.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", """{"format":"png","captureBeyondViewport":true}""");
|
||||
string? base64 = JsonDocument.Parse(base64Json).RootElement.GetProperty("data").GetString();
|
||||
ArgumentNullException.ThrowIfNull(base64);
|
||||
JsonSerializerOptions jsonSerializerOptions = scope.ServiceProvider.GetRequiredService<JsonSerializerOptions>();
|
||||
HttpClient httpClient = scope.ServiceProvider.GetRequiredService<HttpClient>();
|
||||
IClipboardProvider clipboardProvider = scope.ServiceProvider.GetRequiredService<IClipboardProvider>();
|
||||
IInfoBarService infoBarService = scope.ServiceProvider.GetRequiredService<IInfoBarService>();
|
||||
|
||||
await ShareImageBase64CoreAsync(base64).ConfigureAwait(false);
|
||||
}
|
||||
BridgeShareContext context = new(coreWebView2, taskContext, httpClient, infoBarService, clipboardProvider, jsonSerializerOptions);
|
||||
|
||||
return new JsResult<Dictionary<string, string>>()
|
||||
{
|
||||
Data = new()
|
||||
{
|
||||
["type"] = param.Payload.Type,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async ValueTask ShareImageBase64CoreAsync(string base64)
|
||||
{
|
||||
using (MemoryStream imageStream = new())
|
||||
{
|
||||
await imageStream.WriteAsync(Convert.FromBase64String(base64)).ConfigureAwait(false);
|
||||
using (InMemoryRandomAccessStream stream = new())
|
||||
{
|
||||
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
|
||||
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream.AsRandomAccessStream());
|
||||
encoder.SetSoftwareBitmap(await decoder.GetSoftwareBitmapAsync());
|
||||
await encoder.FlushAsync();
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
if (clipboardProvider.SetBitmap(stream))
|
||||
{
|
||||
infoBarService.Success(SH.WebBridgeShareCopyToClipboardSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoBarService.Error(SH.WebBridgeShareCopyToClipboardFailed);
|
||||
}
|
||||
}
|
||||
return await BridgeShareImplmentation.ShareAsync(param, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +395,7 @@ internal class MiHoYoJSBridge
|
||||
.Append(')')
|
||||
.ToString();
|
||||
|
||||
logger?.LogInformation("[{Id}][ExecuteScript: {callback}]\n{payload}", interfaceId, callback, payload);
|
||||
logger?.LogInformation("[{Id}][ExecuteScript: {callback}]\n{payload}", bridgeId, callback, payload);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
if (coreWebView2 is null || coreWebView2.IsDisposed())
|
||||
@@ -533,7 +409,7 @@ internal class MiHoYoJSBridge
|
||||
private async void OnWebMessageReceived(CoreWebView2 webView2, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
string message = args.TryGetWebMessageAsString();
|
||||
logger.LogInformation("[{Id}][OnRawMessage]\n{message}", interfaceId, message);
|
||||
logger.LogInformation("[{Id}][OnRawMessage]\n{message}", bridgeId, message);
|
||||
JsParam? param = JsonSerializer.Deserialize<JsParam>(message);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(param);
|
||||
@@ -582,7 +458,7 @@ internal class MiHoYoJSBridge
|
||||
"hideLoading" => null,
|
||||
"login" => null,
|
||||
"pushPage" => await PushPageAsync(param).ConfigureAwait(false),
|
||||
"share" => await Share(param).ConfigureAwait(false),
|
||||
"share" => await ShareAsync(param).ConfigureAwait(false),
|
||||
"showLoading" => null,
|
||||
_ => LogUnhandledMessage("Unhandled Message Type: {Method}", param.Method),
|
||||
};
|
||||
|
||||
@@ -6,20 +6,12 @@ namespace Snap.Hutao.Web.Bridge.Model;
|
||||
[SuppressMessage("", "SA1124")]
|
||||
internal sealed class ShareContent
|
||||
{
|
||||
#region Screenshot
|
||||
|
||||
[JsonPropertyName("preview")]
|
||||
public bool Preview { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Image
|
||||
|
||||
[JsonPropertyName("image_url")]
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("image_base64")]
|
||||
public string? ImageBase64 { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user