mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
feat(core): add onDanmakuHandshake userscript event
This commit is contained in:
parent
324c41ab1d
commit
8a81450d80
|
@ -31,6 +31,8 @@ namespace BililiveRecorder.Core.Api.Danmaku
|
|||
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||
public event EventHandler<DanmakuReceivedEventArgs>? DanmakuReceived;
|
||||
|
||||
public Func<string, string?>? BeforeHandshake { get; set; } = null;
|
||||
|
||||
public DanmakuClient(IDanmakuServerApiClient apiClient, ILogger logger)
|
||||
{
|
||||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
|
@ -96,7 +98,7 @@ namespace BililiveRecorder.Core.Api.Danmaku
|
|||
|
||||
this.danmakuTransport = transport;
|
||||
|
||||
await this.SendHelloAsync(roomid, apiClient.GetUid(), danmakuServerInfo.Token ?? string.Empty).ConfigureAwait(false);
|
||||
await this.SendHelloAsync(roomid, this.apiClient.GetUid(), danmakuServerInfo.Token ?? string.Empty).ConfigureAwait(false);
|
||||
await this.SendPingAsync().ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
|
@ -211,8 +213,9 @@ namespace BililiveRecorder.Core.Api.Danmaku
|
|||
|
||||
#region Send
|
||||
|
||||
private Task SendHelloAsync(int roomid, long uid, string token) =>
|
||||
this.SendMessageAsync(7, JsonConvert.SerializeObject(new
|
||||
private Task SendHelloAsync(int roomid, long uid, string token)
|
||||
{
|
||||
var body = JsonConvert.SerializeObject(new
|
||||
{
|
||||
uid = uid,
|
||||
roomid = roomid,
|
||||
|
@ -220,7 +223,20 @@ namespace BililiveRecorder.Core.Api.Danmaku
|
|||
platform = "web",
|
||||
type = 2,
|
||||
key = token,
|
||||
}, Formatting.None));
|
||||
}, Formatting.None);
|
||||
|
||||
if (this.BeforeHandshake is { } func)
|
||||
{
|
||||
var newBody = func(body);
|
||||
if (newBody is not null)
|
||||
{
|
||||
this.logger.Debug("Danmaku BeforeHandshake: {OldBody} => {NewBody}", body, newBody);
|
||||
body = newBody;
|
||||
}
|
||||
}
|
||||
|
||||
return this.SendMessageAsync(7, body);
|
||||
}
|
||||
|
||||
private Task SendPingAsync() => this.SendMessageAsync(2);
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace BililiveRecorder.Core.Api
|
|||
event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||
event EventHandler<DanmakuReceivedEventArgs>? DanmakuReceived;
|
||||
|
||||
Func<string, string?>? BeforeHandshake { get; set; }
|
||||
|
||||
Task ConnectAsync(int roomid, DanmakuTransportMode transportMode, CancellationToken cancellationToken);
|
||||
Task DisconnectAsync();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ using BililiveRecorder.Core.Config.V3;
|
|||
using BililiveRecorder.Core.Danmaku;
|
||||
using BililiveRecorder.Core.Event;
|
||||
using BililiveRecorder.Core.Recording;
|
||||
using BililiveRecorder.Core.Scripting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Polly;
|
||||
|
@ -35,6 +36,7 @@ namespace BililiveRecorder.Core
|
|||
private readonly IApiClient apiClient;
|
||||
private readonly IBasicDanmakuWriter basicDanmakuWriter;
|
||||
private readonly IRecordTaskFactory recordTaskFactory;
|
||||
private readonly UserScriptRunner userScriptRunner;
|
||||
private readonly CancellationTokenSource cts;
|
||||
private readonly CancellationToken ct;
|
||||
|
||||
|
@ -63,7 +65,7 @@ namespace BililiveRecorder.Core
|
|||
coverDownloadHttpClient.DefaultRequestHeaders.UserAgent.Clear();
|
||||
}
|
||||
|
||||
public Room(IServiceScope scope, RoomConfig roomConfig, int initDelayFactor, ILogger logger, IDanmakuClient danmakuClient, IApiClient apiClient, IBasicDanmakuWriter basicDanmakuWriter, IRecordTaskFactory recordTaskFactory)
|
||||
public Room(IServiceScope scope, RoomConfig roomConfig, int initDelayFactor, ILogger logger, IDanmakuClient danmakuClient, IApiClient apiClient, IBasicDanmakuWriter basicDanmakuWriter, IRecordTaskFactory recordTaskFactory, UserScriptRunner userScriptRunner)
|
||||
{
|
||||
this.scope = scope ?? throw new ArgumentNullException(nameof(scope));
|
||||
this.RoomConfig = roomConfig ?? throw new ArgumentNullException(nameof(roomConfig));
|
||||
|
@ -73,6 +75,7 @@ namespace BililiveRecorder.Core
|
|||
this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
|
||||
this.basicDanmakuWriter = basicDanmakuWriter ?? throw new ArgumentNullException(nameof(basicDanmakuWriter));
|
||||
this.recordTaskFactory = recordTaskFactory ?? throw new ArgumentNullException(nameof(recordTaskFactory));
|
||||
this.userScriptRunner = userScriptRunner ?? throw new ArgumentNullException(nameof(userScriptRunner));
|
||||
|
||||
this.timer = new Timer(this.RoomConfig.TimingCheckInterval * 1000d);
|
||||
this.cts = new CancellationTokenSource();
|
||||
|
@ -85,6 +88,7 @@ namespace BililiveRecorder.Core
|
|||
|
||||
this.danmakuClient.StatusChanged += this.DanmakuClient_StatusChanged;
|
||||
this.danmakuClient.DanmakuReceived += this.DanmakuClient_DanmakuReceived;
|
||||
this.danmakuClient.BeforeHandshake = this.DanmakuClient_BeforeHandshake;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
|
@ -532,6 +536,11 @@ retry:
|
|||
});
|
||||
}
|
||||
|
||||
private string? DanmakuClient_BeforeHandshake(string json)
|
||||
{
|
||||
return this.userScriptRunner.CallOnDanmakuHandshake(this.logger, this, json);
|
||||
}
|
||||
|
||||
private void DanmakuClient_DanmakuReceived(object sender, Api.Danmaku.DanmakuReceivedEventArgs e)
|
||||
{
|
||||
var d = e.Danmaku;
|
||||
|
@ -564,21 +573,19 @@ retry:
|
|||
|
||||
private void DanmakuClient_StatusChanged(object sender, Api.Danmaku.StatusChangedEventArgs e)
|
||||
{
|
||||
this.DanmakuConnected = e.Connected;
|
||||
if (e.Connected)
|
||||
{
|
||||
this.DanmakuConnected = true;
|
||||
this.danmakuClientConnectTime = DateTimeOffset.UtcNow;
|
||||
this.logger.Information("弹幕服务器已连接");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DanmakuConnected = false;
|
||||
this.logger.Information("与弹幕服务器的连接被断开");
|
||||
|
||||
// 如果连接弹幕服务器的时间在至少 1 分钟之前,重连时不需要等待
|
||||
// 针对偶尔的网络波动的优化,如果偶尔断开了尽快重连,减少漏录的弹幕量
|
||||
this.StartDamakuConnection(delay: !((DateTimeOffset.UtcNow - this.danmakuClientConnectTime) > danmakuClientReconnectNoDelay));
|
||||
|
||||
this.danmakuClientConnectTime = DateTimeOffset.MaxValue;
|
||||
}
|
||||
}
|
||||
|
|
23
BililiveRecorder.Core/Scripting/Runtime/JintRoomInfo.cs
Normal file
23
BililiveRecorder.Core/Scripting/Runtime/JintRoomInfo.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Jint;
|
||||
using Jint.Native.Object;
|
||||
using Jint.Runtime.Descriptors;
|
||||
|
||||
namespace BililiveRecorder.Core.Scripting.Runtime
|
||||
{
|
||||
internal class JintRoomInfo : ObjectInstance
|
||||
{
|
||||
public JintRoomInfo(Engine engine, IRoom room) : base(engine)
|
||||
{
|
||||
if (room is null) throw new ArgumentNullException(nameof(room));
|
||||
|
||||
this.FastSetProperty("roomId", new PropertyDescriptor(room.RoomConfig.RoomId, false, true, false));
|
||||
this.FastSetProperty("shortId", new PropertyDescriptor(room.ShortId, false, true, false));
|
||||
this.FastSetProperty("name", new PropertyDescriptor(room.Name, false, true, false));
|
||||
this.FastSetProperty("title", new PropertyDescriptor(room.Title, false, true, false));
|
||||
this.FastSetProperty("areaParent", new PropertyDescriptor(room.AreaNameParent, false, true, false));
|
||||
this.FastSetProperty("areaChild", new PropertyDescriptor(room.AreaNameChild, false, true, false));
|
||||
this.FastSetProperty("objectId", new PropertyDescriptor(room.ObjectId.ToString(), false, true, false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -251,5 +251,44 @@ globalThis.recorderEvents = {};
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改发给弹幕服务器的握手包 JSON
|
||||
/// </summary>
|
||||
/// <param name="logger">logger</param>
|
||||
/// <param name="room">对应的直播间</param>
|
||||
/// <param name="json">原握手包文本 JSON 数据</param>
|
||||
/// <returns>新的握手包JSON 或 null</returns>
|
||||
public string? CallOnDanmakuHandshake(ILogger logger, IRoom room, string json)
|
||||
{
|
||||
const string callbackName = "onDanmakuHandshake";
|
||||
var log = BuildLogger(logger);
|
||||
try
|
||||
{
|
||||
var func = this.ExecuteScriptThenGetEventHandler(log, callbackName);
|
||||
if (func is null)
|
||||
return null;
|
||||
|
||||
var roomInfo = new JintRoomInfo(func.Engine, room);
|
||||
|
||||
var result = func.Engine.Call(func, roomInfo, json);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case JsString jsString:
|
||||
return jsString.ToString();
|
||||
case JsUndefined or JsNull:
|
||||
return null;
|
||||
default:
|
||||
log.Warning($"{RecorderEvents}.{callbackName}() 返回了不支持的类型: {{ValueType}}", result.Type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex, $"执行脚本 {callbackName} 时发生错误");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -513,6 +513,7 @@ namespace BililiveRecorder.Core.Scripting
|
|||
{
|
||||
public UserScriptRunner(BililiveRecorder.Core.Config.V3.GlobalConfig config) { }
|
||||
public bool CallOnDanmaku(Serilog.ILogger logger, string json) { }
|
||||
public string? CallOnDanmakuHandshake(Serilog.ILogger logger, BililiveRecorder.Core.IRoom room, string json) { }
|
||||
public string? CallOnFetchStreamUrl(Serilog.ILogger logger, int roomid, int[] qnSetting) { }
|
||||
public void CallOnTest(Serilog.ILogger logger, System.Action<string>? alert) { }
|
||||
[return: System.Runtime.CompilerServices.TupleElementNames(new string[] {
|
||||
|
|
Loading…
Reference in New Issue
Block a user