diff --git a/.codex_tmp_danmaku/index.js b/.codex_tmp_danmaku/index.js
deleted file mode 100644
index d0769c3..0000000
--- a/.codex_tmp_danmaku/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// index.js
-const sign = require('./xbogus');
-
-module.exports = function (url, userAgent) {
- return sign(url.split("?")[1], userAgent);
-};
-
diff --git a/.codex_tmp_danmaku/probe.js b/.codex_tmp_danmaku/probe.js
deleted file mode 100644
index 96fae88..0000000
--- a/.codex_tmp_danmaku/probe.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const sqlite3 = require('node:sqlite');
-const https = require('https');
-const sign = require(path.join(process.argv[2], 'index.js'));
-const db = new sqlite3.DatabaseSync(process.argv[3]);
-const rows = db.prepare("select Key, Value from AppSettings where Key in ('douyin.cookie','douyin.user_agent')").all();
-const settings = Object.fromEntries(rows.map(r => [r.Key, r.Value]));
-const webRid = '24482384478';
-function request(url, headers) {
- return new Promise((resolve, reject) => {
- https.get(url, { headers }, res => {
- const chunks = [];
- res.on('data', chunk => chunks.push(chunk));
- res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
- }).on('error', reject);
- });
-}
-(async () => {
- const headers = {
- 'user-agent': settings['douyin.user_agent'] || 'Mozilla/5.0',
- 'referer': `https://live.douyin.com/${webRid}`,
- 'origin': 'https://live.douyin.com',
- 'cookie': settings['douyin.cookie'] || ''
- };
- const html = await request(`https://live.douyin.com/${webRid}`, headers);
- const match = html.match(/
@@ -188,16 +453,20 @@ onMounted(loadRooms);
直播间管理
- 直播间长期保留在系统中。临时不想录制时直接停用即可,停用后后台巡检不会再为该直播间自动创建任务。
+ 直播间会长期保留在系统里。单房间配置优先于全局配置;未设置的项会自动回退到系统设置。
+
+
直播间总数
@@ -225,83 +494,111 @@ onMounted(loadRooms);
-
-
-
-
- N/A
-
-
+
+
+
-
+
任务信息
@@ -173,6 +239,22 @@ onMounted(loadDetailAndPreview);
{{ detail.task.outputFilePath || "-" }}
+
+
+
+
{{ detail.task.postProcessStage }}
+
+
+ {{ detail.task.postProcessDetail || formatProgress(detail.task.postProcessProgressPercent) }}
+
+
+
+ -
+
{{ detail.task.errorMessage || "-" }}
@@ -223,11 +305,22 @@ onMounted(loadDetailAndPreview);
视频预览
- 仅支持已完成的 MP4 分片。预览票据会短期失效,过期后刷新即可重新获取。
+ 仅支持已完成收尾的 MP4 分片。预览票据会短期失效,过期后刷新即可重新获取。
-
@@ -249,7 +342,8 @@ onMounted(loadDetailAndPreview);
关联日志
日志已带上会话与任务关联,可看到 ffmpeg、巡检和弹幕采集的上下文。
-
+
+
@@ -270,7 +364,8 @@ onMounted(loadDetailAndPreview);
{{ formatDate(row.createdAt) }}
-
+
+
@@ -282,10 +377,23 @@ onMounted(loadDetailAndPreview);
gap: 24px;
}
+.page-error-alert {
+ border-radius: 14px;
+}
+
.header-actions {
align-self: center;
}
+.detail-logs-shell {
+ margin-inline: -4px;
+ padding-inline: 4px;
+}
+
+.detail-logs-table {
+ min-width: 940px;
+}
+
.detail-card :deep(.el-card__body),
.preview-card :deep(.el-card__body),
.logs-card :deep(.el-card__body) {
@@ -307,6 +415,12 @@ onMounted(loadDetailAndPreview);
font-size: 12px;
}
+.preview-actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
.preview-player {
width: 100%;
max-height: 560px;
@@ -326,15 +440,53 @@ onMounted(loadDetailAndPreview);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.76), rgba(246, 243, 238, 0.78));
}
+.detail-postprocess {
+ display: grid;
+ gap: 8px;
+}
+
+.detail-postprocess__stage {
+ font-size: 13px;
+ font-weight: 600;
+ color: #31465b;
+}
+
+.detail-postprocess__detail {
+ font-size: 12px;
+ color: #6c7a86;
+ line-height: 1.4;
+}
+
.log-detail {
font-size: 12px;
color: #6d665b;
}
@media (max-width: 768px) {
+ .header-actions {
+ width: 100%;
+ justify-content: stretch;
+ }
+
+ .header-actions :deep(.el-button) {
+ flex: 1 1 0;
+ margin: 0;
+ }
+
.preview-header {
+ flex-direction: column;
+ align-items: flex-start;
margin-bottom: 16px;
padding-bottom: 16px;
}
+
+ .preview-actions {
+ width: 100%;
+ justify-content: space-between;
+ }
+
+ .preview-player {
+ max-height: 360px;
+ }
}
diff --git a/frontend/src/views/RecordTasksView.vue b/frontend/src/views/RecordTasksView.vue
index 812c933..64222e4 100644
--- a/frontend/src/views/RecordTasksView.vue
+++ b/frontend/src/views/RecordTasksView.vue
@@ -1,8 +1,14 @@
@@ -257,12 +389,12 @@ onMounted(loadSessions);
录制任务
- 每一场直播按会话分组展示;分段模式下,一个分片就是一个任务,结束后的分片可按需删除,整场会话也支持直接清理。
+ 按直播会话聚合展示分片任务,列表会自动接收状态、转码进度和分片变更;保留手动刷新入口用于兜底。
+
+
+
+
录制会话
@@ -284,7 +427,7 @@ onMounted(loadSessions);
活跃会话
{{ activeSessionCount }}
-
Starting / Running / Stopping
+
Starting / Running / Stopping / Processing
分片任务
@@ -303,7 +446,7 @@ onMounted(loadSessions);
会话列表
- 会话层负责整场直播,分片层负责单个视频文件。停止入口作用于整个会话,详情入口作用于单个分片。
+ 会话层代表整场录制,分片层代表单个视频文件。停止入口作用于整个会话,详情入口作用于单个分片。
@@ -379,80 +522,96 @@ onMounted(loadSessions);
-
-
+
@@ -461,7 +620,7 @@ onMounted(loadSessions);
import { computed, onMounted, reactive, ref } from "vue";
import { ElMessage } from "element-plus";
-import apiClient from "@/api/client";
+import apiClient, { getApiErrorMessage } from "@/api/client";
import type { SystemSettings } from "@/types";
import { outputFormatLabelMap, recordingTemplateLabelMap, saveModeLabelMap } from "@/types";
const loading = ref(false);
const saving = ref(false);
const testingEmail = ref(false);
+const loadError = ref("");
const form = reactive({
ffmpegPath: "ffmpeg",
@@ -19,6 +20,11 @@ const form = reactive({
saveMode: 0,
recordingTemplate: 0,
segmentDurationMinutes: 30,
+ maxConcurrentFfmpegTranscodeTasks: 1,
+ mp4FinalizeTimeoutMinutes: 60,
+ enableStorageGuard: true,
+ pauseRecordingWhenFreeSpaceBelowMegabytes: 1024,
+ resumeRecordingWhenFreeSpaceAboveMegabytes: 4096,
enableAutoReconnect: true,
reconnectDelayMaxSeconds: 5,
readWriteTimeoutMilliseconds: 15000000,
@@ -29,6 +35,11 @@ const form = reactive({
enableBackgroundPolling: true,
autoStartRecordingOnLive: true,
pollingIntervalSeconds: 60,
+ enableEventScripts: false,
+ liveStartedScriptPath: "",
+ liveEndedScriptPath: "",
+ segmentCompletedScriptPath: "",
+ eventScriptTimeoutSeconds: 60,
enableEmailNotification: false,
emailSmtpHost: "",
emailSmtpPort: 587,
@@ -120,10 +131,13 @@ const nestedSegmentedExamplePath = computed(() => {
async function loadSettings() {
loading.value = true;
+ loadError.value = "";
try {
const { data } = await apiClient.get("/settings");
Object.assign(form, data);
+ } catch (error) {
+ loadError.value = getApiErrorMessage(error, "系统设置加载失败,请稍后重试。");
} finally {
loading.value = false;
}
@@ -185,6 +199,8 @@ onMounted(loadSettings);
保存设置
+
+
录制基础
@@ -248,6 +264,16 @@ onMounted(loadSettings);
+
+
+
+
+
+
+
+
+
+
@@ -267,6 +293,45 @@ onMounted(loadSettings);
+
+ 存储保护
+ 磁盘空间低于暂停阈值时会停止活跃录制并暂停 MP4 转码;空间恢复到恢复阈值后,后台巡检会重新启动录制并补转码。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 恢复阈值建议大于暂停阈值,避免磁盘空间在临界值附近反复暂停和恢复。MP4 转码会额外预留源 TS 文件大小,避免再次出现 No space left on device。
+
+
+
弹幕录制
控制是否并行录制弹幕 XML、是否保留非聊天事件,以及轮询和失败重连节奏。
@@ -375,6 +440,50 @@ onMounted(loadSettings);
+
+ 事件脚本
+ 主播开播、下播、分片完成时可执行自定义脚本。脚本通过环境变量获取上下文,分片完成时可读取 LIVE_RECORDER_SEGMENT_FILE_PATH。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 常用环境变量:LIVE_RECORDER_EVENT、LIVE_RECORDER_PLATFORM、LIVE_RECORDER_ROOM_ID、LIVE_RECORDER_ANCHOR、LIVE_RECORDER_RECORD_SESSION_ID、LIVE_RECORDER_RECORD_TASK_ID、LIVE_RECORDER_SEGMENT_INDEX、LIVE_RECORDER_SEGMENT_FILE_PATH、LIVE_RECORDER_DANMAKU_FILE_PATH。
+
+
+
邮件通知
支持 SMTP、开播提醒和异常提醒 HTML 模板。测试发送会直接使用当前表单中的 SMTP 与模板。
@@ -531,6 +640,10 @@ onMounted(loadSettings);
gap: 24px;
}
+.page-error-alert {
+ border-radius: 14px;
+}
+
.settings-grid {
display: grid;
grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.85fr);
@@ -649,9 +762,28 @@ onMounted(loadSettings);
}
@media (max-width: 900px) {
+ .page-header :deep(.el-button) {
+ width: 100%;
+ }
+
.email-actions {
flex-direction: column;
align-items: flex-start;
}
}
+
+@media (max-width: 768px) {
+ .settings-card :deep(.el-col) {
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .template-section {
+ padding: 16px 16px 2px;
+ }
+
+ .email-actions :deep(.el-button) {
+ width: 100%;
+ }
+}
diff --git a/src/LiveRecorder.Application/Abstractions/Logging/ISystemLogService.cs b/src/LiveRecorder.Application/Abstractions/Logging/ISystemLogService.cs
index 131ee47..1ea84ba 100644
--- a/src/LiveRecorder.Application/Abstractions/Logging/ISystemLogService.cs
+++ b/src/LiveRecorder.Application/Abstractions/Logging/ISystemLogService.cs
@@ -19,6 +19,7 @@ public interface ISystemLogService
Guid? liveRoomId = null,
Guid? recordSessionId = null,
Guid? recordTaskId = null,
+ SystemLogLevel? level = null,
int take = 200,
CancellationToken cancellationToken = default);
}
diff --git a/src/LiveRecorder.Application/Abstractions/Persistence/PersistenceContracts.cs b/src/LiveRecorder.Application/Abstractions/Persistence/PersistenceContracts.cs
index 79ea6b7..6b2c643 100644
--- a/src/LiveRecorder.Application/Abstractions/Persistence/PersistenceContracts.cs
+++ b/src/LiveRecorder.Application/Abstractions/Persistence/PersistenceContracts.cs
@@ -91,6 +91,7 @@ public interface ISystemLogRepository
Guid? liveRoomId = null,
Guid? recordSessionId = null,
Guid? recordTaskId = null,
+ SystemLogLevel? level = null,
int take = 200,
CancellationToken cancellationToken = default);
diff --git a/src/LiveRecorder.Application/Abstractions/Platforms/PlatformContracts.cs b/src/LiveRecorder.Application/Abstractions/Platforms/PlatformContracts.cs
index e409418..ab5e035 100644
--- a/src/LiveRecorder.Application/Abstractions/Platforms/PlatformContracts.cs
+++ b/src/LiveRecorder.Application/Abstractions/Platforms/PlatformContracts.cs
@@ -35,6 +35,8 @@ public sealed record LiveStatusSnapshot(
bool IsLive,
string? Title,
string? AnchorName,
+ string? AnchorId,
+ string? AvatarUrl,
string? CoverUrl,
int? StatusCode,
string? RawStatus);
@@ -57,4 +59,5 @@ public sealed record StreamUrlResult(
string SelectedProtocol,
string SelectedUrl,
StreamInputHeaders? InputHeaders,
- IReadOnlyList AvailableQualities);
+ IReadOnlyList AvailableQualities,
+ string? SelectedVideoCodec = null);
diff --git a/src/LiveRecorder.Application/Abstractions/Recording/IFfmpegService.cs b/src/LiveRecorder.Application/Abstractions/Recording/IFfmpegService.cs
index 09645b1..d94ba23 100644
--- a/src/LiveRecorder.Application/Abstractions/Recording/IFfmpegService.cs
+++ b/src/LiveRecorder.Application/Abstractions/Recording/IFfmpegService.cs
@@ -1,5 +1,6 @@
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Domain.Entities;
+using LiveRecorder.Domain.Enums;
namespace LiveRecorder.Application.Abstractions.Recording;
@@ -9,6 +10,7 @@ public interface IFfmpegService
RecordSession recordSession,
RecordTask initialTask,
StreamUrlResult streamUrlResult,
+ RecordingExecutionSettings recordingSettings,
CancellationToken cancellationToken = default);
Task CompleteAsync(Guid recordSessionId, CancellationToken cancellationToken = default);
@@ -28,5 +30,31 @@ public interface IFfmpegService
Task TryReconcileInactiveSessionAsync(Guid recordSessionId, CancellationToken cancellationToken = default);
+ Task StartManualFinalizeTaskAsync(Guid recordTaskId, CancellationToken cancellationToken = default);
+
+ Task ResumePausedFinalizationsAsync(CancellationToken cancellationToken = default);
+
bool IsRunning(Guid recordSessionId);
+
+ IReadOnlyDictionary GetTaskRuntimeStates(IReadOnlyCollection recordTaskIds);
}
+
+public sealed record RecordTaskRuntimeState(
+ RecordTaskStatus Status,
+ string Stage,
+ double? ProgressPercent,
+ string? Detail);
+
+public sealed record RecordingExecutionSettings(
+ string PreferredQuality,
+ RecordOutputFormat OutputFormat,
+ RecordSaveMode SaveMode,
+ RecordingTemplateType RecordingTemplate,
+ int SegmentDurationMinutes,
+ bool EnableAutoReconnect,
+ int ReconnectDelayMaxSeconds,
+ int ReadWriteTimeoutMilliseconds,
+ bool EnableDanmakuRecording,
+ bool DanmakuIncludeNonChatEvents,
+ int DanmakuMinPollIntervalMilliseconds,
+ int DanmakuRetryDelayMaxSeconds);
diff --git a/src/LiveRecorder.Application/Abstractions/Scripting/IEventScriptService.cs b/src/LiveRecorder.Application/Abstractions/Scripting/IEventScriptService.cs
new file mode 100644
index 0000000..ba15389
--- /dev/null
+++ b/src/LiveRecorder.Application/Abstractions/Scripting/IEventScriptService.cs
@@ -0,0 +1,20 @@
+using LiveRecorder.Domain.Entities;
+
+namespace LiveRecorder.Application.Abstractions.Scripting;
+
+public interface IEventScriptService
+{
+ Task RunLiveStartedAsync(LiveRoom liveRoom, DateTimeOffset occurredAt, CancellationToken cancellationToken = default);
+
+ Task RunLiveEndedAsync(LiveRoom liveRoom, DateTimeOffset occurredAt, CancellationToken cancellationToken = default);
+
+ Task RunSegmentCompletedAsync(
+ LiveRoom? liveRoom,
+ RecordSession recordSession,
+ RecordTask recordTask,
+ RecordResult? recordResult,
+ string segmentFilePath,
+ DateTimeOffset occurredAt,
+ CancellationToken cancellationToken = default);
+}
+
diff --git a/src/LiveRecorder.Application/Abstractions/Storage/IStorageGuardService.cs b/src/LiveRecorder.Application/Abstractions/Storage/IStorageGuardService.cs
new file mode 100644
index 0000000..f7beacc
--- /dev/null
+++ b/src/LiveRecorder.Application/Abstractions/Storage/IStorageGuardService.cs
@@ -0,0 +1,19 @@
+using LiveRecorder.Application.Models.Settings;
+
+namespace LiveRecorder.Application.Abstractions.Storage;
+
+public interface IStorageGuardService
+{
+ StorageGuardResult CheckCanStartOrResume(SystemSettingsDto settings, long additionalRequiredBytes = 0);
+
+ StorageGuardResult CheckShouldPause(SystemSettingsDto settings);
+}
+
+public sealed record StorageGuardResult(
+ bool IsEnabled,
+ bool HasEnoughSpace,
+ string CheckedPath,
+ long AvailableBytes,
+ long RequiredBytes,
+ string Message);
+
diff --git a/src/LiveRecorder.Application/Models/LiveRooms/LiveRoomModels.cs b/src/LiveRecorder.Application/Models/LiveRooms/LiveRoomModels.cs
index 69d2f7c..f0c42b8 100644
--- a/src/LiveRecorder.Application/Models/LiveRooms/LiveRoomModels.cs
+++ b/src/LiveRecorder.Application/Models/LiveRooms/LiveRoomModels.cs
@@ -7,6 +7,81 @@ public sealed class CreateLiveRoomRequest
public string Url { get; set; } = string.Empty;
public LivePlatformType? PlatformOverride { get; set; }
+
+ public string? AnchorName { get; set; }
+}
+
+public sealed class ImportLiveRoomsRequest
+{
+ public string Content { get; set; } = string.Empty;
+
+ public LivePlatformType? PlatformOverride { get; set; }
+}
+
+public sealed class ImportLiveRoomsResultDto
+{
+ public int TotalCount { get; init; }
+
+ public int SuccessCount { get; init; }
+
+ public int FailedCount { get; init; }
+
+ public int CreatedCount { get; init; }
+
+ public int UpdatedCount { get; init; }
+
+ public required IReadOnlyList Items { get; init; }
+}
+
+public sealed class ImportLiveRoomItemResultDto
+{
+ public int LineNumber { get; init; }
+
+ public required string RawLine { get; init; }
+
+ public string? Url { get; init; }
+
+ public string? AnchorName { get; init; }
+
+ public bool Success { get; init; }
+
+ public bool Created { get; init; }
+
+ public string? ErrorMessage { get; init; }
+
+ public LiveRoomDto? LiveRoom { get; init; }
+}
+
+public sealed class BatchSetLiveRoomsEnabledRequest
+{
+ public IReadOnlyList LiveRoomIds { get; set; } = [];
+
+ public bool IsEnabled { get; set; }
+}
+
+public sealed class BatchDeleteLiveRoomsRequest
+{
+ public IReadOnlyList LiveRoomIds { get; set; } = [];
+}
+
+public sealed class BatchLiveRoomsResultDto
+{
+ public int RequestedCount { get; init; }
+
+ public int SuccessCount { get; init; }
+
+ public int FailedCount { get; init; }
+
+ public required IReadOnlyList Items { get; init; }
+}
+
+public sealed class BatchLiveRoomItemResultDto
+{
+ public Guid LiveRoomId { get; init; }
+
+ public bool Success { get; init; }
+
+ public string? ErrorMessage { get; init; }
}
public sealed class LiveRoomDto
@@ -27,8 +102,16 @@ public sealed class LiveRoomDto
public string? AnchorName { get; init; }
+ public string? AnchorId { get; init; }
+
+ public string? AvatarUrl { get; init; }
+
public string? CoverUrl { get; init; }
+ public required LiveRoomSettingsOverridesDto Overrides { get; init; }
+
+ public required LiveRoomEffectiveSettingsDto EffectiveSettings { get; init; }
+
public bool IsEnabled { get; init; }
public required LiveRoomAvailabilityStatus AvailabilityStatus { get; init; }
@@ -44,3 +127,84 @@ public sealed class SetLiveRoomEnabledRequest
{
public bool IsEnabled { get; set; }
}
+
+public sealed class UpdateLiveRoomSettingsRequest
+{
+ public string? PreferredQualityOverride { get; set; }
+
+ public RecordOutputFormat? OutputFormatOverride { get; set; }
+
+ public RecordSaveMode? SaveModeOverride { get; set; }
+
+ public RecordingTemplateType? RecordingTemplateOverride { get; set; }
+
+ public int? SegmentDurationMinutesOverride { get; set; }
+
+ public bool? EnableAutoReconnectOverride { get; set; }
+
+ public int? ReconnectDelayMaxSecondsOverride { get; set; }
+
+ public int? ReadWriteTimeoutMillisecondsOverride { get; set; }
+
+ public bool? EnableDanmakuRecordingOverride { get; set; }
+
+ public bool? DanmakuIncludeNonChatEventsOverride { get; set; }
+
+ public int? DanmakuMinPollIntervalMillisecondsOverride { get; set; }
+
+ public int? DanmakuRetryDelayMaxSecondsOverride { get; set; }
+}
+
+public sealed class LiveRoomSettingsOverridesDto
+{
+ public string? PreferredQuality { get; init; }
+
+ public RecordOutputFormat? OutputFormat { get; init; }
+
+ public RecordSaveMode? SaveMode { get; init; }
+
+ public RecordingTemplateType? RecordingTemplate { get; init; }
+
+ public int? SegmentDurationMinutes { get; init; }
+
+ public bool? EnableAutoReconnect { get; init; }
+
+ public int? ReconnectDelayMaxSeconds { get; init; }
+
+ public int? ReadWriteTimeoutMilliseconds { get; init; }
+
+ public bool? EnableDanmakuRecording { get; init; }
+
+ public bool? DanmakuIncludeNonChatEvents { get; init; }
+
+ public int? DanmakuMinPollIntervalMilliseconds { get; init; }
+
+ public int? DanmakuRetryDelayMaxSeconds { get; init; }
+}
+
+public sealed class LiveRoomEffectiveSettingsDto
+{
+ public required string PreferredQuality { get; init; }
+
+ public required RecordOutputFormat OutputFormat { get; init; }
+
+ public required RecordSaveMode SaveMode { get; init; }
+
+ public required RecordingTemplateType RecordingTemplate { get; init; }
+
+ public required int SegmentDurationMinutes { get; init; }
+
+ public required bool EnableAutoReconnect { get; init; }
+
+ public required int ReconnectDelayMaxSeconds { get; init; }
+
+ public required int ReadWriteTimeoutMilliseconds { get; init; }
+
+ public required bool EnableDanmakuRecording { get; init; }
+
+ public required bool DanmakuIncludeNonChatEvents { get; init; }
+
+ public required int DanmakuMinPollIntervalMilliseconds { get; init; }
+
+ public required int DanmakuRetryDelayMaxSeconds { get; init; }
+}
diff --git a/src/LiveRecorder.Application/Models/RecordTasks/RecordTaskModels.cs b/src/LiveRecorder.Application/Models/RecordTasks/RecordTaskModels.cs
index 7bcb4f9..78d7eca 100644
--- a/src/LiveRecorder.Application/Models/RecordTasks/RecordTaskModels.cs
+++ b/src/LiveRecorder.Application/Models/RecordTasks/RecordTaskModels.cs
@@ -70,6 +70,12 @@ public sealed class RecordTaskDto
public DateTimeOffset? EndedAt { get; init; }
public double? DurationSeconds { get; init; }
+
+ public string? PostProcessStage { get; init; }
+
+ public double? PostProcessProgressPercent { get; init; }
+
+ public string? PostProcessDetail { get; init; }
}
public sealed class RecordTaskDetailDto
diff --git a/src/LiveRecorder.Application/Models/Settings/SettingsModels.cs b/src/LiveRecorder.Application/Models/Settings/SettingsModels.cs
index 20c707d..158f18f 100644
--- a/src/LiveRecorder.Application/Models/Settings/SettingsModels.cs
+++ b/src/LiveRecorder.Application/Models/Settings/SettingsModels.cs
@@ -22,6 +22,16 @@ public sealed class SystemSettingsDto
public int SegmentDurationMinutes { get; set; } = 30;
+ public int MaxConcurrentFfmpegTranscodeTasks { get; set; } = 1;
+
+ public int Mp4FinalizeTimeoutMinutes { get; set; } = 60;
+
+ public bool EnableStorageGuard { get; set; } = true;
+
+ public int PauseRecordingWhenFreeSpaceBelowMegabytes { get; set; } = 1024;
+
+ public int ResumeRecordingWhenFreeSpaceAboveMegabytes { get; set; } = 4096;
+
public bool EnableAutoReconnect { get; set; } = true;
public int ReconnectDelayMaxSeconds { get; set; } = 5;
@@ -42,6 +52,16 @@ public sealed class SystemSettingsDto
public int PollingIntervalSeconds { get; set; } = 60;
+ public bool EnableEventScripts { get; set; } = false;
+
+ public string LiveStartedScriptPath { get; set; } = string.Empty;
+
+ public string LiveEndedScriptPath { get; set; } = string.Empty;
+
+ public string SegmentCompletedScriptPath { get; set; } = string.Empty;
+
+ public int EventScriptTimeoutSeconds { get; set; } = 60;
+
public bool EnableEmailNotification { get; set; } = false;
public string EmailSmtpHost { get; set; } = string.Empty;
@@ -127,6 +147,16 @@ public sealed class UpdateSystemSettingsRequest
public int SegmentDurationMinutes { get; set; } = 30;
+ public int MaxConcurrentFfmpegTranscodeTasks { get; set; } = 1;
+
+ public int Mp4FinalizeTimeoutMinutes { get; set; } = 60;
+
+ public bool EnableStorageGuard { get; set; } = true;
+
+ public int PauseRecordingWhenFreeSpaceBelowMegabytes { get; set; } = 1024;
+
+ public int ResumeRecordingWhenFreeSpaceAboveMegabytes { get; set; } = 4096;
+
public bool EnableAutoReconnect { get; set; } = true;
public int ReconnectDelayMaxSeconds { get; set; } = 5;
@@ -147,6 +177,16 @@ public sealed class UpdateSystemSettingsRequest
public int PollingIntervalSeconds { get; set; } = 60;
+ public bool EnableEventScripts { get; set; } = false;
+
+ public string LiveStartedScriptPath { get; set; } = string.Empty;
+
+ public string LiveEndedScriptPath { get; set; } = string.Empty;
+
+ public string SegmentCompletedScriptPath { get; set; } = string.Empty;
+
+ public int EventScriptTimeoutSeconds { get; set; } = 60;
+
public bool EnableEmailNotification { get; set; } = false;
public string EmailSmtpHost { get; set; } = string.Empty;
diff --git a/src/LiveRecorder.Application/Services/LiveRoomRecordingSettingsResolver.cs b/src/LiveRecorder.Application/Services/LiveRoomRecordingSettingsResolver.cs
new file mode 100644
index 0000000..e9e7076
--- /dev/null
+++ b/src/LiveRecorder.Application/Services/LiveRoomRecordingSettingsResolver.cs
@@ -0,0 +1,89 @@
+using LiveRecorder.Application.Abstractions.Recording;
+using LiveRecorder.Application.Abstractions.Settings;
+using LiveRecorder.Application.Models.LiveRooms;
+using LiveRecorder.Application.Models.Settings;
+using LiveRecorder.Domain.Entities;
+
+namespace LiveRecorder.Application.Services;
+
+public sealed class LiveRoomRecordingSettingsResolver
+{
+ private readonly ISystemSettingsService _systemSettingsService;
+
+ public LiveRoomRecordingSettingsResolver(ISystemSettingsService systemSettingsService)
+ {
+ _systemSettingsService = systemSettingsService;
+ }
+
+ public async Task ResolveAsync(LiveRoom liveRoom, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(liveRoom);
+
+ var systemSettings = await _systemSettingsService.GetAsync(cancellationToken);
+ return Resolve(liveRoom, systemSettings);
+ }
+
+ public RecordingExecutionSettings Resolve(LiveRoom liveRoom, SystemSettingsDto systemSettings)
+ {
+ ArgumentNullException.ThrowIfNull(liveRoom);
+ ArgumentNullException.ThrowIfNull(systemSettings);
+
+ return new RecordingExecutionSettings(
+ PreferredQuality: string.IsNullOrWhiteSpace(liveRoom.PreferredQualityOverride)
+ ? systemSettings.DefaultQuality
+ : liveRoom.PreferredQualityOverride.Trim(),
+ OutputFormat: liveRoom.OutputFormatOverride ?? systemSettings.DefaultOutputFormat,
+ SaveMode: liveRoom.SaveModeOverride ?? systemSettings.SaveMode,
+ RecordingTemplate: liveRoom.RecordingTemplateOverride ?? systemSettings.RecordingTemplate,
+ SegmentDurationMinutes: liveRoom.SegmentDurationMinutesOverride ?? systemSettings.SegmentDurationMinutes,
+ EnableAutoReconnect: liveRoom.EnableAutoReconnectOverride ?? systemSettings.EnableAutoReconnect,
+ ReconnectDelayMaxSeconds: liveRoom.ReconnectDelayMaxSecondsOverride ?? systemSettings.ReconnectDelayMaxSeconds,
+ ReadWriteTimeoutMilliseconds: liveRoom.ReadWriteTimeoutMillisecondsOverride ?? systemSettings.ReadWriteTimeoutMilliseconds,
+ EnableDanmakuRecording: liveRoom.EnableDanmakuRecordingOverride ?? systemSettings.EnableDanmakuRecording,
+ DanmakuIncludeNonChatEvents: liveRoom.DanmakuIncludeNonChatEventsOverride ?? systemSettings.DanmakuIncludeNonChatEvents,
+ DanmakuMinPollIntervalMilliseconds: liveRoom.DanmakuMinPollIntervalMillisecondsOverride ?? systemSettings.DanmakuMinPollIntervalMilliseconds,
+ DanmakuRetryDelayMaxSeconds: liveRoom.DanmakuRetryDelayMaxSecondsOverride ?? systemSettings.DanmakuRetryDelayMaxSeconds);
+ }
+
+ public LiveRoomSettingsOverridesDto BuildOverridesDto(LiveRoom liveRoom)
+ {
+ ArgumentNullException.ThrowIfNull(liveRoom);
+
+ return new LiveRoomSettingsOverridesDto
+ {
+ PreferredQuality = liveRoom.PreferredQualityOverride,
+ OutputFormat = liveRoom.OutputFormatOverride,
+ SaveMode = liveRoom.SaveModeOverride,
+ RecordingTemplate = liveRoom.RecordingTemplateOverride,
+ SegmentDurationMinutes = liveRoom.SegmentDurationMinutesOverride,
+ EnableAutoReconnect = liveRoom.EnableAutoReconnectOverride,
+ ReconnectDelayMaxSeconds = liveRoom.ReconnectDelayMaxSecondsOverride,
+ ReadWriteTimeoutMilliseconds = liveRoom.ReadWriteTimeoutMillisecondsOverride,
+ EnableDanmakuRecording = liveRoom.EnableDanmakuRecordingOverride,
+ DanmakuIncludeNonChatEvents = liveRoom.DanmakuIncludeNonChatEventsOverride,
+ DanmakuMinPollIntervalMilliseconds = liveRoom.DanmakuMinPollIntervalMillisecondsOverride,
+ DanmakuRetryDelayMaxSeconds = liveRoom.DanmakuRetryDelayMaxSecondsOverride
+ };
+ }
+
+ public LiveRoomEffectiveSettingsDto BuildEffectiveDto(RecordingExecutionSettings settings)
+ {
+ ArgumentNullException.ThrowIfNull(settings);
+
+ return new LiveRoomEffectiveSettingsDto
+ {
+ PreferredQuality = settings.PreferredQuality,
+ OutputFormat = settings.OutputFormat,
+ SaveMode = settings.SaveMode,
+ RecordingTemplate = settings.RecordingTemplate,
+ SegmentDurationMinutes = settings.SegmentDurationMinutes,
+ EnableAutoReconnect = settings.EnableAutoReconnect,
+ ReconnectDelayMaxSeconds = settings.ReconnectDelayMaxSeconds,
+ ReadWriteTimeoutMilliseconds = settings.ReadWriteTimeoutMilliseconds,
+ EnableDanmakuRecording = settings.EnableDanmakuRecording,
+ DanmakuIncludeNonChatEvents = settings.DanmakuIncludeNonChatEvents,
+ DanmakuMinPollIntervalMilliseconds = settings.DanmakuMinPollIntervalMilliseconds,
+ DanmakuRetryDelayMaxSeconds = settings.DanmakuRetryDelayMaxSeconds
+ };
+ }
+}
diff --git a/src/LiveRecorder.Application/Services/LiveRoomService.cs b/src/LiveRecorder.Application/Services/LiveRoomService.cs
index ba2670f..84b6d68 100644
--- a/src/LiveRecorder.Application/Services/LiveRoomService.cs
+++ b/src/LiveRecorder.Application/Services/LiveRoomService.cs
@@ -1,6 +1,8 @@
using LiveRecorder.Application.Abstractions.Logging;
using LiveRecorder.Application.Abstractions.Persistence;
using LiveRecorder.Application.Abstractions.Platforms;
+using LiveRecorder.Application.Abstractions.Recording;
+using LiveRecorder.Application.Abstractions.Settings;
using LiveRecorder.Application.Models.LiveRooms;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
@@ -12,6 +14,8 @@ public sealed class LiveRoomService
private readonly ILiveRoomRepository _liveRoomRepository;
private readonly ILivePlatformAdapterFactory _livePlatformAdapterFactory;
private readonly LiveRoomStatusService _liveRoomStatusService;
+ private readonly LiveRoomRecordingSettingsResolver _liveRoomRecordingSettingsResolver;
+ private readonly ISystemSettingsService _systemSettingsService;
private readonly StoppedOrphanRecordSessionCleanupService _stoppedOrphanRecordSessionCleanupService;
private readonly IUnitOfWork _unitOfWork;
private readonly ISystemLogService _systemLogService;
@@ -20,6 +24,8 @@ public sealed class LiveRoomService
ILiveRoomRepository liveRoomRepository,
ILivePlatformAdapterFactory livePlatformAdapterFactory,
LiveRoomStatusService liveRoomStatusService,
+ LiveRoomRecordingSettingsResolver liveRoomRecordingSettingsResolver,
+ ISystemSettingsService systemSettingsService,
StoppedOrphanRecordSessionCleanupService stoppedOrphanRecordSessionCleanupService,
IUnitOfWork unitOfWork,
ISystemLogService systemLogService)
@@ -27,6 +33,8 @@ public sealed class LiveRoomService
_liveRoomRepository = liveRoomRepository;
_livePlatformAdapterFactory = livePlatformAdapterFactory;
_liveRoomStatusService = liveRoomStatusService;
+ _liveRoomRecordingSettingsResolver = liveRoomRecordingSettingsResolver;
+ _systemSettingsService = systemSettingsService;
_stoppedOrphanRecordSessionCleanupService = stoppedOrphanRecordSessionCleanupService;
_unitOfWork = unitOfWork;
_systemLogService = systemLogService;
@@ -35,30 +43,146 @@ public sealed class LiveRoomService
public async Task> ListAsync(CancellationToken cancellationToken = default)
{
var rooms = await _liveRoomRepository.ListAsync(cancellationToken);
+ var effectiveSettings = await BuildEffectiveSettingsLookupAsync(rooms, cancellationToken);
return rooms
.OrderByDescending(static item => item.UpdatedAt)
- .Select(Map)
+ .Select(item => Map(item, effectiveSettings[item.Id]))
.ToList();
}
public async Task GetAsync(Guid id, CancellationToken cancellationToken = default)
{
var room = await _liveRoomRepository.GetByIdAsync(id, cancellationToken);
- return room is null ? null : Map(room);
+ if (room is null)
+ {
+ return null;
+ }
+
+ var effectiveSettings = await _liveRoomRecordingSettingsResolver.ResolveAsync(room, cancellationToken);
+ return Map(room, effectiveSettings);
}
public async Task CreateAsync(CreateLiveRoomRequest request, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
- var input = request.Url.Trim();
+ var (liveRoom, effectiveSettings, _) = await CreateOrUpdateAsync(
+ request.Url,
+ request.PlatformOverride,
+ request.AnchorName,
+ cancellationToken);
+
+ return Map(liveRoom, effectiveSettings);
+ }
+
+ public async Task ImportAsync(
+ ImportLiveRoomsRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(request);
+
+ var results = new List();
+ var lines = request.Content
+ .Replace("\r\n", "\n", StringComparison.Ordinal)
+ .Replace('\r', '\n')
+ .Split('\n')
+ .Select((line, index) => new { RawLine = line, LineNumber = index + 1 })
+ .Where(item => !string.IsNullOrWhiteSpace(item.RawLine))
+ .ToArray();
+
+ foreach (var line in lines)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (!TryParseImportLine(line.RawLine, out var url, out var anchorName, out var parseError))
+ {
+ results.Add(new ImportLiveRoomItemResultDto
+ {
+ LineNumber = line.LineNumber,
+ RawLine = line.RawLine,
+ Url = url,
+ AnchorName = anchorName,
+ Success = false,
+ Created = false,
+ ErrorMessage = parseError
+ });
+ continue;
+ }
+
+ try
+ {
+ var (liveRoom, effectiveSettings, created) = await CreateOrUpdateAsync(
+ url,
+ request.PlatformOverride,
+ anchorName,
+ cancellationToken);
+
+ results.Add(new ImportLiveRoomItemResultDto
+ {
+ LineNumber = line.LineNumber,
+ RawLine = line.RawLine,
+ Url = url,
+ AnchorName = anchorName,
+ Success = true,
+ Created = created,
+ LiveRoom = Map(liveRoom, effectiveSettings)
+ });
+ }
+ catch (Exception ex)
+ {
+ results.Add(new ImportLiveRoomItemResultDto
+ {
+ LineNumber = line.LineNumber,
+ RawLine = line.RawLine,
+ Url = url,
+ AnchorName = anchorName,
+ Success = false,
+ Created = false,
+ ErrorMessage = ex.Message
+ });
+ }
+ }
+
+ var successCount = results.Count(static item => item.Success);
+ var createdCount = results.Count(static item => item.Success && item.Created);
+ return new ImportLiveRoomsResultDto
+ {
+ TotalCount = results.Count,
+ SuccessCount = successCount,
+ FailedCount = results.Count - successCount,
+ CreatedCount = createdCount,
+ UpdatedCount = successCount - createdCount,
+ Items = results
+ };
+ }
+
+ public async Task ExportAsync(CancellationToken cancellationToken = default)
+ {
+ var rooms = await _liveRoomRepository.ListAsync(cancellationToken);
+ var lines = rooms
+ .OrderBy(static item => item.Platform)
+ .ThenBy(static item => item.AnchorName)
+ .ThenBy(static item => item.RoomId)
+ .Select(FormatExportLine)
+ .ToArray();
+
+ return string.Join(Environment.NewLine, lines);
+ }
+
+ private async Task<(LiveRoom LiveRoom, RecordingExecutionSettings EffectiveSettings, bool Created)> CreateOrUpdateAsync(
+ string rawInput,
+ LivePlatformType? platformOverride,
+ string? fallbackAnchorName,
+ CancellationToken cancellationToken)
+ {
+ var input = rawInput.Trim();
if (string.IsNullOrWhiteSpace(input))
{
throw new InvalidOperationException("Live room URL is required.");
}
- var adapter = request.PlatformOverride.HasValue && request.PlatformOverride.Value != LivePlatformType.Unknown
- ? _livePlatformAdapterFactory.GetByPlatform(request.PlatformOverride.Value)
+ var adapter = platformOverride.HasValue && platformOverride.Value != LivePlatformType.Unknown
+ ? _livePlatformAdapterFactory.GetByPlatform(platformOverride.Value)
: _livePlatformAdapterFactory.GetByInput(input);
var parsedRoom = await adapter.ParseRoomAsync(input, cancellationToken);
@@ -66,10 +190,12 @@ public sealed class LiveRoomService
var now = DateTimeOffset.UtcNow;
var liveRoom = await _liveRoomRepository.GetByPlatformRoomIdAsync(parsedRoom.PlatformType, parsedRoom.RoomId, cancellationToken);
+ var created = liveRoom is null;
if (liveRoom is null)
{
liveRoom = new LiveRoom(parsedRoom.PlatformType, parsedRoom.SourceUrl, parsedRoom.RoomId, parsedRoom.NormalizedUrl, now);
await _liveRoomStatusService.ApplySnapshotAsync(liveRoom, liveStatus, now, cancellationToken);
+ liveRoom.UpdateMetadata(null, fallbackAnchorName, null, null, null, now);
await _liveRoomRepository.AddAsync(liveRoom, cancellationToken);
}
@@ -78,6 +204,7 @@ public sealed class LiveRoomService
liveRoom.UpdateSource(parsedRoom.SourceUrl, parsedRoom.NormalizedUrl, now);
liveRoom.UpdateRoomId(parsedRoom.RoomId, now);
await _liveRoomStatusService.ApplySnapshotAsync(liveRoom, liveStatus, now, cancellationToken);
+ liveRoom.UpdateMetadata(null, fallbackAnchorName, null, null, null, now);
}
await _unitOfWork.SaveChangesAsync(cancellationToken);
@@ -88,7 +215,8 @@ public sealed class LiveRoomService
liveRoomId: liveRoom.Id,
cancellationToken: cancellationToken);
- return Map(liveRoom);
+ var effectiveSettings = await _liveRoomRecordingSettingsResolver.ResolveAsync(liveRoom, cancellationToken);
+ return (liveRoom, effectiveSettings, created);
}
public async Task RefreshStatusAsync(Guid id, CancellationToken cancellationToken = default)
@@ -111,7 +239,8 @@ public sealed class LiveRoomService
liveRoomId: room.Id,
cancellationToken: cancellationToken);
- return Map(room);
+ var effectiveSettings = await _liveRoomRecordingSettingsResolver.ResolveAsync(room, cancellationToken);
+ return Map(room, effectiveSettings);
}
public async Task SetEnabledAsync(Guid id, bool isEnabled, CancellationToken cancellationToken = default)
@@ -131,7 +260,80 @@ public sealed class LiveRoomService
liveRoomId: room.Id,
cancellationToken: cancellationToken);
- return Map(room);
+ var effectiveSettings = await _liveRoomRecordingSettingsResolver.ResolveAsync(room, cancellationToken);
+ return Map(room, effectiveSettings);
+ }
+
+ public async Task SetEnabledBatchAsync(
+ BatchSetLiveRoomsEnabledRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(request);
+
+ var results = new List();
+ foreach (var liveRoomId in request.LiveRoomIds.Distinct())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await SetEnabledAsync(liveRoomId, request.IsEnabled, cancellationToken);
+ results.Add(new BatchLiveRoomItemResultDto
+ {
+ LiveRoomId = liveRoomId,
+ Success = true
+ });
+ }
+ catch (Exception ex)
+ {
+ results.Add(new BatchLiveRoomItemResultDto
+ {
+ LiveRoomId = liveRoomId,
+ Success = false,
+ ErrorMessage = ex.Message
+ });
+ }
+ }
+
+ return BuildBatchResult(request.LiveRoomIds.Count, results);
+ }
+
+ public async Task UpdateSettingsAsync(
+ Guid id,
+ UpdateLiveRoomSettingsRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(request);
+
+ var room = await _liveRoomRepository.GetByIdAsync(id, cancellationToken)
+ ?? throw new KeyNotFoundException("Live room was not found.");
+
+ room.UpdateRecordingSettingsOverrides(
+ request.PreferredQualityOverride,
+ request.OutputFormatOverride,
+ request.SaveModeOverride,
+ request.RecordingTemplateOverride,
+ ClampNullable(request.SegmentDurationMinutesOverride, 1, 720),
+ request.EnableAutoReconnectOverride,
+ ClampNullable(request.ReconnectDelayMaxSecondsOverride, 1, 300),
+ ClampNullable(request.ReadWriteTimeoutMillisecondsOverride, 1000, 60000000),
+ request.EnableDanmakuRecordingOverride,
+ request.DanmakuIncludeNonChatEventsOverride,
+ ClampNullable(request.DanmakuMinPollIntervalMillisecondsOverride, 100, 60000),
+ ClampNullable(request.DanmakuRetryDelayMaxSecondsOverride, 1, 300),
+ DateTimeOffset.UtcNow);
+
+ await _unitOfWork.SaveChangesAsync(cancellationToken);
+
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Info,
+ "LiveRoom",
+ $"Recording settings updated for room {room.RoomId}.",
+ liveRoomId: room.Id,
+ cancellationToken: cancellationToken);
+
+ var effectiveSettings = await _liveRoomRecordingSettingsResolver.ResolveAsync(room, cancellationToken);
+ return Map(room, effectiveSettings);
}
public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
@@ -155,7 +357,55 @@ public sealed class LiveRoomService
cancellationToken: cancellationToken);
}
- private static LiveRoomDto Map(LiveRoom room) => new()
+ public async Task DeleteBatchAsync(
+ BatchDeleteLiveRoomsRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(request);
+
+ var results = new List();
+ foreach (var liveRoomId in request.LiveRoomIds.Distinct())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ await DeleteAsync(liveRoomId, cancellationToken);
+ results.Add(new BatchLiveRoomItemResultDto
+ {
+ LiveRoomId = liveRoomId,
+ Success = true
+ });
+ }
+ catch (Exception ex)
+ {
+ results.Add(new BatchLiveRoomItemResultDto
+ {
+ LiveRoomId = liveRoomId,
+ Success = false,
+ ErrorMessage = ex.Message
+ });
+ }
+ }
+
+ return BuildBatchResult(request.LiveRoomIds.Count, results);
+ }
+
+ private async Task> BuildEffectiveSettingsLookupAsync(
+ IReadOnlyCollection rooms,
+ CancellationToken cancellationToken)
+ {
+ if (rooms.Count == 0)
+ {
+ return [];
+ }
+
+ var systemSettings = await _systemSettingsService.GetAsync(cancellationToken);
+
+ return rooms.ToDictionary(item => item.Id, item => _liveRoomRecordingSettingsResolver.Resolve(item, systemSettings));
+ }
+
+ private LiveRoomDto Map(LiveRoom room, RecordingExecutionSettings effectiveSettings) => new()
{
Id = room.Id,
Platform = room.Platform,
@@ -165,11 +415,109 @@ public sealed class LiveRoomService
NormalizedUrl = room.NormalizedUrl,
Title = room.Title,
AnchorName = room.AnchorName,
+ AnchorId = room.AnchorId,
+ AvatarUrl = room.AvatarUrl,
CoverUrl = room.CoverUrl,
+ Overrides = _liveRoomRecordingSettingsResolver.BuildOverridesDto(room),
+ EffectiveSettings = _liveRoomRecordingSettingsResolver.BuildEffectiveDto(effectiveSettings),
IsEnabled = room.IsEnabled,
AvailabilityStatus = room.AvailabilityStatus,
LastCheckedAt = room.LastCheckedAt,
CreatedAt = room.CreatedAt,
UpdatedAt = room.UpdatedAt
};
+
+ private static int? ClampNullable(int? value, int min, int max) =>
+ value.HasValue ? Math.Clamp(value.Value, min, max) : null;
+
+ private static BatchLiveRoomsResultDto BuildBatchResult(
+ int requestedCount,
+ IReadOnlyList results)
+ {
+ var successCount = results.Count(static item => item.Success);
+ return new BatchLiveRoomsResultDto
+ {
+ RequestedCount = requestedCount,
+ SuccessCount = successCount,
+ FailedCount = results.Count - successCount,
+ Items = results
+ };
+ }
+
+ private static bool TryParseImportLine(
+ string rawLine,
+ out string url,
+ out string? anchorName,
+ out string? errorMessage)
+ {
+ url = string.Empty;
+ anchorName = null;
+ errorMessage = null;
+
+ var line = rawLine.Trim();
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ errorMessage = "Import line is empty.";
+ return false;
+ }
+
+ var markerIndex = IndexOfAnchorMarker(line);
+ if (markerIndex >= 0)
+ {
+ var markerLength = line.AsSpan(markerIndex).StartsWith("主播:", StringComparison.OrdinalIgnoreCase)
+ ? "主播:".Length
+ : "主播:".Length;
+ url = line[..markerIndex].Trim().TrimEnd(',', ',', ';', ';', '\t', ' ');
+ anchorName = line[(markerIndex + markerLength)..].Trim().Trim(',', ',', ';', ';', '\t', ' ');
+ }
+ else
+ {
+ url = line.Trim().TrimEnd(',', ',', ';', ';');
+ }
+
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ errorMessage = "Import line does not contain a live room URL.";
+ return false;
+ }
+
+ if (string.IsNullOrWhiteSpace(anchorName))
+ {
+ anchorName = null;
+ }
+
+ return true;
+ }
+
+ private static int IndexOfAnchorMarker(string line)
+ {
+ var halfWidthIndex = line.IndexOf("主播:", StringComparison.OrdinalIgnoreCase);
+ var fullWidthIndex = line.IndexOf("主播:", StringComparison.OrdinalIgnoreCase);
+
+ if (halfWidthIndex < 0)
+ {
+ return fullWidthIndex;
+ }
+
+ if (fullWidthIndex < 0)
+ {
+ return halfWidthIndex;
+ }
+
+ return Math.Min(halfWidthIndex, fullWidthIndex);
+ }
+
+ private static string FormatExportLine(LiveRoom room)
+ {
+ var url = string.IsNullOrWhiteSpace(room.NormalizedUrl) ? room.SourceUrl : room.NormalizedUrl;
+ var anchorName = NormalizeSingleLine(room.AnchorName);
+ return string.IsNullOrWhiteSpace(anchorName)
+ ? NormalizeSingleLine(url)
+ : $"{NormalizeSingleLine(url)},主播: {anchorName}";
+ }
+
+ private static string NormalizeSingleLine(string? value) =>
+ string.IsNullOrWhiteSpace(value)
+ ? string.Empty
+ : value.Trim().Replace("\r", " ", StringComparison.Ordinal).Replace("\n", " ", StringComparison.Ordinal);
}
diff --git a/src/LiveRecorder.Application/Services/LiveRoomStatusService.cs b/src/LiveRecorder.Application/Services/LiveRoomStatusService.cs
index 3a5cc51..86ff7b5 100644
--- a/src/LiveRecorder.Application/Services/LiveRoomStatusService.cs
+++ b/src/LiveRecorder.Application/Services/LiveRoomStatusService.cs
@@ -1,5 +1,6 @@
using LiveRecorder.Application.Abstractions.Notifications;
using LiveRecorder.Application.Abstractions.Platforms;
+using LiveRecorder.Application.Abstractions.Scripting;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
@@ -8,10 +9,14 @@ namespace LiveRecorder.Application.Services;
public sealed class LiveRoomStatusService
{
private readonly IEmailNotificationService _emailNotificationService;
+ private readonly IEventScriptService _eventScriptService;
- public LiveRoomStatusService(IEmailNotificationService emailNotificationService)
+ public LiveRoomStatusService(
+ IEmailNotificationService emailNotificationService,
+ IEventScriptService eventScriptService)
{
_emailNotificationService = emailNotificationService;
+ _eventScriptService = eventScriptService;
}
public async Task ApplySnapshotAsync(
@@ -23,17 +28,32 @@ public sealed class LiveRoomStatusService
ArgumentNullException.ThrowIfNull(liveRoom);
ArgumentNullException.ThrowIfNull(liveStatus);
- liveRoom.UpdateMetadata(liveStatus.Title, liveStatus.AnchorName, liveStatus.CoverUrl, observedAt);
+ var wasLive = liveRoom.AvailabilityStatus == LiveRoomAvailabilityStatus.Live;
+
+ liveRoom.UpdateMetadata(
+ liveStatus.Title,
+ liveStatus.AnchorName,
+ liveStatus.AnchorId,
+ liveStatus.AvatarUrl,
+ liveStatus.CoverUrl,
+ observedAt);
liveRoom.UpdateAvailability(
liveStatus.IsLive ? LiveRoomAvailabilityStatus.Live : LiveRoomAvailabilityStatus.Offline,
observedAt);
+ if (wasLive && !liveStatus.IsLive)
+ {
+ await _eventScriptService.RunLiveEndedAsync(liveRoom, observedAt, cancellationToken);
+ return;
+ }
+
if (!liveStatus.IsLive || liveRoom.HasSentLiveNotificationForCurrentSession)
{
return;
}
await _emailNotificationService.SendLiveStartedAsync(liveRoom, cancellationToken);
+ await _eventScriptService.RunLiveStartedAsync(liveRoom, observedAt, cancellationToken);
liveRoom.MarkLiveNotificationSent(observedAt);
}
}
diff --git a/src/LiveRecorder.Application/Services/RecordModelMapper.cs b/src/LiveRecorder.Application/Services/RecordModelMapper.cs
index d73e47c..fd1bfb5 100644
--- a/src/LiveRecorder.Application/Services/RecordModelMapper.cs
+++ b/src/LiveRecorder.Application/Services/RecordModelMapper.cs
@@ -1,3 +1,4 @@
+using LiveRecorder.Application.Abstractions.Recording;
using LiveRecorder.Application.Models.RecordTasks;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
@@ -6,7 +7,7 @@ namespace LiveRecorder.Application.Services;
internal static class RecordModelMapper
{
- public static RecordTaskDto MapTask(RecordTask recordTask) => new()
+ public static RecordTaskDto MapTask(RecordTask recordTask, RecordTaskRuntimeState? runtimeState = null) => new()
{
Id = recordTask.Id,
LiveRoomId = recordTask.LiveRoomId,
@@ -15,7 +16,7 @@ internal static class RecordModelMapper
LiveRoomTitle = recordTask.LiveRoom?.Title ?? recordTask.LiveRoom?.AnchorName ?? recordTask.LiveRoom?.RoomId ?? "Unknown Room",
Platform = recordTask.LiveRoom?.Platform ?? LivePlatformType.Unknown,
RoomId = recordTask.LiveRoom?.RoomId ?? string.Empty,
- Status = recordTask.Status,
+ Status = runtimeState?.Status ?? recordTask.Status,
PreferredQuality = recordTask.PreferredQuality,
OutputFormat = recordTask.OutputFormat,
StreamUrl = recordTask.StreamUrl,
@@ -25,7 +26,10 @@ internal static class RecordModelMapper
CreatedAt = recordTask.CreatedAt,
StartedAt = recordTask.StartedAt,
EndedAt = recordTask.EndedAt,
- DurationSeconds = recordTask.DurationSeconds
+ DurationSeconds = recordTask.DurationSeconds,
+ PostProcessStage = runtimeState?.Stage,
+ PostProcessProgressPercent = runtimeState?.ProgressPercent,
+ PostProcessDetail = runtimeState?.Detail
};
public static RecordResultDto MapResult(RecordResult recordResult) => new()
@@ -41,7 +45,9 @@ internal static class RecordModelMapper
CreatedAt = recordResult.CreatedAt
};
- public static RecordSessionDto MapSession(RecordSession recordSession)
+ public static RecordSessionDto MapSession(
+ RecordSession recordSession,
+ IReadOnlyDictionary? runtimeStates = null)
{
var orderedTasks = recordSession.RecordTasks
.OrderBy(static item => item.SegmentIndex)
@@ -74,11 +80,19 @@ internal static class RecordModelMapper
EndedAt = recordSession.EndedAt,
TotalFileSizeBytes = totalFileSizeBytes,
TotalDanmakuMessageCount = totalDanmakuMessageCount,
- Tasks = orderedTasks.Select(item => MapTaskWithFallback(item, recordSession)).ToList()
+ Tasks = orderedTasks.Select(item => MapTaskWithFallback(
+ item,
+ recordSession,
+ runtimeStates is not null && runtimeStates.TryGetValue(item.Id, out var runtimeState)
+ ? runtimeState
+ : null)).ToList()
};
}
- private static RecordTaskDto MapTaskWithFallback(RecordTask recordTask, RecordSession recordSession)
+ private static RecordTaskDto MapTaskWithFallback(
+ RecordTask recordTask,
+ RecordSession recordSession,
+ RecordTaskRuntimeState? runtimeState)
{
var liveRoom = recordTask.LiveRoom ?? recordSession.LiveRoom;
return new RecordTaskDto
@@ -90,7 +104,7 @@ internal static class RecordModelMapper
LiveRoomTitle = liveRoom?.Title ?? liveRoom?.AnchorName ?? liveRoom?.RoomId ?? "Unknown Room",
Platform = liveRoom?.Platform ?? LivePlatformType.Unknown,
RoomId = liveRoom?.RoomId ?? string.Empty,
- Status = recordTask.Status,
+ Status = runtimeState?.Status ?? recordTask.Status,
PreferredQuality = recordTask.PreferredQuality,
OutputFormat = recordTask.OutputFormat,
StreamUrl = recordTask.StreamUrl,
@@ -100,7 +114,10 @@ internal static class RecordModelMapper
CreatedAt = recordTask.CreatedAt,
StartedAt = recordTask.StartedAt,
EndedAt = recordTask.EndedAt,
- DurationSeconds = recordTask.DurationSeconds
+ DurationSeconds = recordTask.DurationSeconds,
+ PostProcessStage = runtimeState?.Stage,
+ PostProcessProgressPercent = runtimeState?.ProgressPercent,
+ PostProcessDetail = runtimeState?.Detail
};
}
}
diff --git a/src/LiveRecorder.Application/Services/RecordService.cs b/src/LiveRecorder.Application/Services/RecordService.cs
index 17f5f50..fac3016 100644
--- a/src/LiveRecorder.Application/Services/RecordService.cs
+++ b/src/LiveRecorder.Application/Services/RecordService.cs
@@ -5,6 +5,7 @@ using LiveRecorder.Application.Abstractions.Persistence;
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Application.Abstractions.Recording;
using LiveRecorder.Application.Abstractions.Settings;
+using LiveRecorder.Application.Abstractions.Storage;
using LiveRecorder.Application.Models.RecordTasks;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
@@ -25,6 +26,8 @@ public sealed class RecordService
private readonly ISystemLogService _systemLogService;
private readonly IEmailNotificationService _emailNotificationService;
private readonly LiveRoomStatusService _liveRoomStatusService;
+ private readonly LiveRoomRecordingSettingsResolver _liveRoomRecordingSettingsResolver;
+ private readonly IStorageGuardService _storageGuardService;
private readonly IUnitOfWork _unitOfWork;
public RecordService(
@@ -40,6 +43,8 @@ public sealed class RecordService
ISystemLogService systemLogService,
IEmailNotificationService emailNotificationService,
LiveRoomStatusService liveRoomStatusService,
+ LiveRoomRecordingSettingsResolver liveRoomRecordingSettingsResolver,
+ IStorageGuardService storageGuardService,
IUnitOfWork unitOfWork)
{
_liveRoomRepository = liveRoomRepository;
@@ -54,6 +59,8 @@ public sealed class RecordService
_systemLogService = systemLogService;
_emailNotificationService = emailNotificationService;
_liveRoomStatusService = liveRoomStatusService;
+ _liveRoomRecordingSettingsResolver = liveRoomRecordingSettingsResolver;
+ _storageGuardService = storageGuardService;
_unitOfWork = unitOfWork;
}
@@ -62,9 +69,12 @@ public sealed class RecordService
await ReconcileActiveSessionsAsync(liveRoomId, cancellationToken);
var tasks = await _recordTaskRepository.ListAsync(liveRoomId, cancellationToken);
+ var runtimeStates = _ffmpegService.GetTaskRuntimeStates(tasks.Select(static item => item.Id).ToArray());
return tasks
.OrderByDescending(static item => item.CreatedAt)
- .Select(RecordModelMapper.MapTask)
+ .Select(item => RecordModelMapper.MapTask(
+ item,
+ runtimeStates.TryGetValue(item.Id, out var runtimeState) ? runtimeState : null))
.ToList();
}
@@ -92,10 +102,13 @@ public sealed class RecordService
recordTaskId: id,
take: 300,
cancellationToken: cancellationToken);
+ var runtimeStates = _ffmpegService.GetTaskRuntimeStates([id]);
return new RecordTaskDetailDto
{
- Task = RecordModelMapper.MapTask(recordTask),
+ Task = RecordModelMapper.MapTask(
+ recordTask,
+ runtimeStates.TryGetValue(id, out var runtimeState) ? runtimeState : null),
Result = recordResult is null ? null : RecordModelMapper.MapResult(recordResult),
Logs = logs
};
@@ -120,10 +133,26 @@ public sealed class RecordService
}
var settings = await _systemSettingsService.GetAsync(cancellationToken);
+ var storageCheck = _storageGuardService.CheckCanStartOrResume(settings);
+ if (!storageCheck.HasEnoughSpace)
+ {
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Warning,
+ "Storage",
+ "Recording start paused because storage is below threshold.",
+ storageCheck.Message,
+ liveRoomId: liveRoom.Id,
+ cancellationToken: cancellationToken);
+ throw new InvalidOperationException(storageCheck.Message);
+ }
+
+ var effectiveSettings = _liveRoomRecordingSettingsResolver.Resolve(liveRoom, settings);
var adapter = _livePlatformAdapterFactory.GetByPlatform(liveRoom.Platform);
- var preferredQuality = string.IsNullOrWhiteSpace(request.PreferredQuality) ? settings.DefaultQuality : request.PreferredQuality.Trim();
- var outputFormat = request.OutputFormat ?? settings.DefaultOutputFormat;
- var saveMode = settings.SaveMode;
+ var preferredQuality = string.IsNullOrWhiteSpace(request.PreferredQuality)
+ ? effectiveSettings.PreferredQuality
+ : request.PreferredQuality.Trim();
+ var outputFormat = request.OutputFormat ?? effectiveSettings.OutputFormat;
+ var saveMode = effectiveSettings.SaveMode;
var now = DateTimeOffset.UtcNow;
var recordSession = new RecordSession(liveRoom.Id, preferredQuality, outputFormat, saveMode, now);
@@ -175,7 +204,7 @@ public sealed class RecordService
initialTask.MarkStarting(streamResult.SelectedUrl, initialOutputPath, now);
await _unitOfWork.SaveChangesAsync(cancellationToken);
- await _ffmpegService.StartAsync(recordSession, initialTask, streamResult, cancellationToken);
+ await _ffmpegService.StartAsync(recordSession, initialTask, streamResult, effectiveSettings, cancellationToken);
recordSession.MarkRunning(DateTimeOffset.UtcNow);
initialTask.MarkRunning(DateTimeOffset.UtcNow);
@@ -349,6 +378,40 @@ public sealed class RecordService
};
}
+ public async Task StartManualTranscodeAsync(Guid id, CancellationToken cancellationToken = default)
+ {
+ var recordTask = await _recordTaskRepository.GetByIdAsync(id, cancellationToken)
+ ?? throw new KeyNotFoundException("Recording task was not found.");
+
+ if (recordTask.OutputFormat != RecordOutputFormat.Mp4)
+ {
+ throw new InvalidOperationException("Only MP4 tasks support manual transcoding.");
+ }
+
+ if (IsActiveTaskStatus(recordTask.Status))
+ {
+ throw new InvalidOperationException("The task is still active. Stop the recording before starting manual transcoding.");
+ }
+
+ var started = await _ffmpegService.StartManualFinalizeTaskAsync(id, cancellationToken);
+ if (!started)
+ {
+ throw new InvalidOperationException("No intermediate recording file is available for manual transcoding.");
+ }
+
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Info,
+ "FFmpeg",
+ "Manual MP4 finalization was requested.",
+ liveRoomId: recordTask.LiveRoomId,
+ recordSessionId: recordTask.RecordSessionId,
+ recordTaskId: recordTask.Id,
+ cancellationToken: cancellationToken);
+
+ return (await GetDetailAsync(id, cancellationToken))
+ ?? throw new KeyNotFoundException("Recording task was not found after starting manual transcoding.");
+ }
+
public async Task StopAsync(Guid id, CancellationToken cancellationToken = default)
{
var recordTask = await _recordTaskRepository.GetByIdAsync(id, cancellationToken)
@@ -404,7 +467,10 @@ public sealed class RecordService
};
private static bool IsActiveTaskStatus(RecordTaskStatus status) =>
- status is RecordTaskStatus.Starting or RecordTaskStatus.Running or RecordTaskStatus.Stopping;
+ status is RecordTaskStatus.Starting
+ or RecordTaskStatus.Running
+ or RecordTaskStatus.Stopping
+ or RecordTaskStatus.Processing;
private static bool IsActiveSessionStatus(RecordSessionStatus status) =>
status is RecordSessionStatus.Starting or RecordSessionStatus.Running or RecordSessionStatus.Stopping;
@@ -551,7 +617,8 @@ public sealed class RecordService
? (forPathSegment ? "{platform}/{yyyy}/{MM}/{dd}/{anchor}" : "{HHmmss}_{anchor}_{title}_{roomId}{segmentSuffix}")
: template;
- var tokens = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ // Path tokens must stay case-sensitive so {MM} (month) and {mm} (minute) don't collide.
+ var tokens = new Dictionary(StringComparer.Ordinal)
{
["platform"] = platform.ToString(),
["roomId"] = roomId,
@@ -610,6 +677,7 @@ public sealed class RecordService
if (!string.IsNullOrWhiteSpace(outputPath))
{
TryDeletePath(outputPath, warnings, deletedFilePaths, $"output for task {recordTask.Id}");
+ TryDeleteIntermediateRecordingArtifacts(outputPath, recordTask.OutputFormat, warnings, deletedFilePaths, recordTask.Id);
}
var danmakuPath = recordTask.Result?.DanmakuFilePath;
@@ -619,6 +687,43 @@ public sealed class RecordService
}
}
+ private static void TryDeleteIntermediateRecordingArtifacts(
+ string finalOutputPath,
+ RecordOutputFormat outputFormat,
+ List warnings,
+ List deletedFilePaths,
+ Guid recordTaskId)
+ {
+ if (outputFormat != RecordOutputFormat.Mp4)
+ {
+ return;
+ }
+
+ var absoluteFinalPath = Path.IsPathRooted(finalOutputPath)
+ ? finalOutputPath
+ : Path.GetFullPath(finalOutputPath, AppContext.BaseDirectory);
+
+ var intermediateCandidates = new[]
+ {
+ Path.ChangeExtension(absoluteFinalPath, ".ts"),
+ Path.Combine(
+ Path.GetDirectoryName(absoluteFinalPath) ?? string.Empty,
+ $"{Path.GetFileNameWithoutExtension(absoluteFinalPath)}.recording.ts")
+ };
+
+ foreach (var candidate in intermediateCandidates
+ .Where(static path => !string.IsNullOrWhiteSpace(path))
+ .Distinct(StringComparer.OrdinalIgnoreCase))
+ {
+ if (string.Equals(candidate, absoluteFinalPath, StringComparison.OrdinalIgnoreCase) || !File.Exists(candidate))
+ {
+ continue;
+ }
+
+ TryDeletePath(candidate, warnings, deletedFilePaths, $"intermediate output for task {recordTaskId}");
+ }
+ }
+
private static void TryDeletePath(
string path,
List warnings,
diff --git a/src/LiveRecorder.Application/Services/RecordSessionService.cs b/src/LiveRecorder.Application/Services/RecordSessionService.cs
index f2b974e..9608e30 100644
--- a/src/LiveRecorder.Application/Services/RecordSessionService.cs
+++ b/src/LiveRecorder.Application/Services/RecordSessionService.cs
@@ -51,9 +51,11 @@ public sealed class RecordSessionService
await ReconcileActiveSessionsAsync(liveRoomId, cancellationToken);
var sessions = await _recordSessionRepository.ListAsync(liveRoomId, cancellationToken);
+ var runtimeStates = _ffmpegService.GetTaskRuntimeStates(
+ sessions.SelectMany(static item => item.RecordTasks).Select(static item => item.Id).ToArray());
return sessions
.OrderByDescending(static item => item.CreatedAt)
- .Select(RecordModelMapper.MapSession)
+ .Select(item => RecordModelMapper.MapSession(item, runtimeStates))
.ToList();
}
@@ -76,9 +78,11 @@ public sealed class RecordSessionService
}
var logs = await _systemLogService.ListAsync(recordSessionId: id, take: 500, cancellationToken: cancellationToken);
+ var runtimeStates = _ffmpegService.GetTaskRuntimeStates(
+ session.RecordTasks.Select(static item => item.Id).ToArray());
return new RecordSessionDetailDto
{
- Session = RecordModelMapper.MapSession(session),
+ Session = RecordModelMapper.MapSession(session, runtimeStates),
Logs = logs
};
}
@@ -105,7 +109,9 @@ public sealed class RecordSessionService
recordSessionId: session.Id,
cancellationToken: cancellationToken);
- return RecordModelMapper.MapSession(session);
+ var runtimeStates = _ffmpegService.GetTaskRuntimeStates(
+ session.RecordTasks.Select(static item => item.Id).ToArray());
+ return RecordModelMapper.MapSession(session, runtimeStates);
}
public async Task DeleteAsync(
diff --git a/src/LiveRecorder.Application/Services/SystemLogService.cs b/src/LiveRecorder.Application/Services/SystemLogService.cs
index b9b210c..a102f73 100644
--- a/src/LiveRecorder.Application/Services/SystemLogService.cs
+++ b/src/LiveRecorder.Application/Services/SystemLogService.cs
@@ -4,6 +4,7 @@ using LiveRecorder.Application.Models.Logs;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
namespace LiveRecorder.Application.Services;
@@ -11,13 +12,16 @@ public sealed class SystemLogService : ISystemLogService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ISystemLogRepository _systemLogRepository;
+ private readonly ILogger _logger;
public SystemLogService(
IServiceScopeFactory serviceScopeFactory,
- ISystemLogRepository systemLogRepository)
+ ISystemLogRepository systemLogRepository,
+ ILogger logger)
{
_serviceScopeFactory = serviceScopeFactory;
_systemLogRepository = systemLogRepository;
+ _logger = logger;
}
public async Task WriteAsync(
@@ -30,23 +34,46 @@ public sealed class SystemLogService : ISystemLogService
Guid? recordTaskId = null,
CancellationToken cancellationToken = default)
{
- var entry = new SystemLogEntry(level, category, message, detail, liveRoomId, recordSessionId, recordTaskId, DateTimeOffset.UtcNow);
+ try
+ {
+ var entry = new SystemLogEntry(level, category, message, detail, liveRoomId, recordSessionId, recordTaskId, DateTimeOffset.UtcNow);
- using var scope = _serviceScopeFactory.CreateScope();
- var repository = scope.ServiceProvider.GetRequiredService();
- var unitOfWork = scope.ServiceProvider.GetRequiredService();
- await repository.AddAsync(entry, cancellationToken);
- await unitOfWork.SaveChangesAsync(cancellationToken);
+ using var scope = _serviceScopeFactory.CreateScope();
+ var repository = scope.ServiceProvider.GetRequiredService();
+ var unitOfWork = scope.ServiceProvider.GetRequiredService();
+ await repository.AddAsync(entry, cancellationToken);
+ await unitOfWork.SaveChangesAsync(cancellationToken);
+ }
+ catch (Exception ex) when (!cancellationToken.IsCancellationRequested)
+ {
+ // Logging must be best-effort. If SQLite is locked/full, throwing from here
+ // causes the scheduler to turn a log persistence failure into an email storm.
+ _logger.LogWarning(
+ ex,
+ "System log write failed. Category={Category}; Message={Message}; LiveRoomId={LiveRoomId}; RecordSessionId={RecordSessionId}; RecordTaskId={RecordTaskId}",
+ category,
+ message,
+ liveRoomId,
+ recordSessionId,
+ recordTaskId);
+ }
}
public async Task> ListAsync(
Guid? liveRoomId = null,
Guid? recordSessionId = null,
Guid? recordTaskId = null,
+ SystemLogLevel? level = null,
int take = 200,
CancellationToken cancellationToken = default)
{
- var entries = await _systemLogRepository.ListAsync(liveRoomId, recordSessionId, recordTaskId, take, cancellationToken);
+ var entries = await _systemLogRepository.ListAsync(
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ level,
+ take,
+ cancellationToken);
return entries
.Select(static item => new SystemLogDto
{
diff --git a/src/LiveRecorder.Application/Services/SystemSettingsService.cs b/src/LiveRecorder.Application/Services/SystemSettingsService.cs
index fad6bc2..e96d039 100644
--- a/src/LiveRecorder.Application/Services/SystemSettingsService.cs
+++ b/src/LiveRecorder.Application/Services/SystemSettingsService.cs
@@ -17,6 +17,11 @@ public sealed class SystemSettingsService : ISystemSettingsService
private const string SaveModeKey = "recording.save_mode";
private const string RecordingTemplateKey = "recording.template";
private const string SegmentDurationMinutesKey = "recording.segment_duration_minutes";
+ private const string MaxConcurrentFfmpegTranscodeTasksKey = "recording.max_concurrent_ffmpeg_transcode_tasks";
+ private const string Mp4FinalizeTimeoutMinutesKey = "recording.mp4_finalize_timeout_minutes";
+ private const string EnableStorageGuardKey = "storage.guard.enabled";
+ private const string PauseRecordingWhenFreeSpaceBelowMegabytesKey = "storage.guard.pause_recording_below_mb";
+ private const string ResumeRecordingWhenFreeSpaceAboveMegabytesKey = "storage.guard.resume_recording_above_mb";
private const string EnableReconnectKey = "recording.enable_auto_reconnect";
private const string ReconnectDelayMaxSecondsKey = "recording.reconnect_delay_max_seconds";
private const string ReadWriteTimeoutMillisecondsKey = "recording.read_write_timeout_milliseconds";
@@ -27,6 +32,11 @@ public sealed class SystemSettingsService : ISystemSettingsService
private const string EnableBackgroundPollingKey = "scheduler.enable_background_polling";
private const string AutoStartRecordingOnLiveKey = "scheduler.auto_start_recording_on_live";
private const string PollingIntervalSecondsKey = "scheduler.polling_interval_seconds";
+ private const string EnableEventScriptsKey = "event_scripts.enabled";
+ private const string LiveStartedScriptPathKey = "event_scripts.live_started.path";
+ private const string LiveEndedScriptPathKey = "event_scripts.live_ended.path";
+ private const string SegmentCompletedScriptPathKey = "event_scripts.segment_completed.path";
+ private const string EventScriptTimeoutSecondsKey = "event_scripts.timeout_seconds";
private const string EnableEmailNotificationKey = "notification.email.enabled";
private const string EmailSmtpHostKey = "notification.email.smtp_host";
private const string EmailSmtpPortKey = "notification.email.smtp_port";
@@ -77,6 +87,11 @@ public sealed class SystemSettingsService : ISystemSettingsService
? recordingTemplate
: RecordingTemplateType.StreamCopy,
SegmentDurationMinutes = GetIntValue(lookup, SegmentDurationMinutesKey, 30, 1, 720),
+ MaxConcurrentFfmpegTranscodeTasks = GetIntValue(lookup, MaxConcurrentFfmpegTranscodeTasksKey, 1, 1, 16),
+ Mp4FinalizeTimeoutMinutes = GetIntValue(lookup, Mp4FinalizeTimeoutMinutesKey, 60, 1, 1440),
+ EnableStorageGuard = bool.TryParse(GetValue(lookup, EnableStorageGuardKey, "true"), out var enableStorageGuard) && enableStorageGuard,
+ PauseRecordingWhenFreeSpaceBelowMegabytes = GetIntValue(lookup, PauseRecordingWhenFreeSpaceBelowMegabytesKey, 1024, 0, 1048576),
+ ResumeRecordingWhenFreeSpaceAboveMegabytes = GetIntValue(lookup, ResumeRecordingWhenFreeSpaceAboveMegabytesKey, 4096, 0, 1048576),
EnableAutoReconnect = bool.TryParse(GetValue(lookup, EnableReconnectKey, "true"), out var enableReconnect) && enableReconnect,
ReconnectDelayMaxSeconds = GetIntValue(lookup, ReconnectDelayMaxSecondsKey, 5, 1, 300),
ReadWriteTimeoutMilliseconds = GetIntValue(lookup, ReadWriteTimeoutMillisecondsKey, 15000000, 1000, 60000000),
@@ -87,6 +102,11 @@ public sealed class SystemSettingsService : ISystemSettingsService
EnableBackgroundPolling = bool.TryParse(GetValue(lookup, EnableBackgroundPollingKey, "true"), out var enableBackgroundPolling) && enableBackgroundPolling,
AutoStartRecordingOnLive = bool.TryParse(GetValue(lookup, AutoStartRecordingOnLiveKey, "true"), out var autoStartRecordingOnLive) && autoStartRecordingOnLive,
PollingIntervalSeconds = GetIntValue(lookup, PollingIntervalSecondsKey, 60, 10, 3600),
+ EnableEventScripts = bool.TryParse(GetValue(lookup, EnableEventScriptsKey, "false"), out var enableEventScripts) && enableEventScripts,
+ LiveStartedScriptPath = GetValue(lookup, LiveStartedScriptPathKey, string.Empty),
+ LiveEndedScriptPath = GetValue(lookup, LiveEndedScriptPathKey, string.Empty),
+ SegmentCompletedScriptPath = GetValue(lookup, SegmentCompletedScriptPathKey, string.Empty),
+ EventScriptTimeoutSeconds = GetIntValue(lookup, EventScriptTimeoutSecondsKey, 60, 1, 3600),
EnableEmailNotification = bool.TryParse(GetValue(lookup, EnableEmailNotificationKey, "false"), out var enableEmailNotification) && enableEmailNotification,
EmailSmtpHost = GetValue(lookup, EmailSmtpHostKey, string.Empty),
EmailSmtpPort = GetIntValue(lookup, EmailSmtpPortKey, 587, 1, 65535),
@@ -159,6 +179,27 @@ public sealed class SystemSettingsService : ISystemSettingsService
await UpsertAsync(SaveModeKey, request.SaveMode.ToString(), now, cancellationToken);
await UpsertAsync(RecordingTemplateKey, request.RecordingTemplate.ToString(), now, cancellationToken);
await UpsertAsync(SegmentDurationMinutesKey, request.SegmentDurationMinutes.ToString(), now, cancellationToken);
+ await UpsertAsync(
+ MaxConcurrentFfmpegTranscodeTasksKey,
+ Math.Clamp(request.MaxConcurrentFfmpegTranscodeTasks, 1, 16).ToString(),
+ now,
+ cancellationToken);
+ await UpsertAsync(
+ Mp4FinalizeTimeoutMinutesKey,
+ Math.Clamp(request.Mp4FinalizeTimeoutMinutes, 1, 1440).ToString(),
+ now,
+ cancellationToken);
+ await UpsertAsync(EnableStorageGuardKey, request.EnableStorageGuard.ToString(), now, cancellationToken);
+ await UpsertAsync(
+ PauseRecordingWhenFreeSpaceBelowMegabytesKey,
+ Math.Clamp(request.PauseRecordingWhenFreeSpaceBelowMegabytes, 0, 1048576).ToString(),
+ now,
+ cancellationToken);
+ await UpsertAsync(
+ ResumeRecordingWhenFreeSpaceAboveMegabytesKey,
+ Math.Clamp(request.ResumeRecordingWhenFreeSpaceAboveMegabytes, 0, 1048576).ToString(),
+ now,
+ cancellationToken);
await UpsertAsync(EnableReconnectKey, request.EnableAutoReconnect.ToString(), now, cancellationToken);
await UpsertAsync(ReconnectDelayMaxSecondsKey, request.ReconnectDelayMaxSeconds.ToString(), now, cancellationToken);
await UpsertAsync(ReadWriteTimeoutMillisecondsKey, request.ReadWriteTimeoutMilliseconds.ToString(), now, cancellationToken);
@@ -169,6 +210,11 @@ public sealed class SystemSettingsService : ISystemSettingsService
await UpsertAsync(EnableBackgroundPollingKey, request.EnableBackgroundPolling.ToString(), now, cancellationToken);
await UpsertAsync(AutoStartRecordingOnLiveKey, request.AutoStartRecordingOnLive.ToString(), now, cancellationToken);
await UpsertAsync(PollingIntervalSecondsKey, request.PollingIntervalSeconds.ToString(), now, cancellationToken);
+ await UpsertAsync(EnableEventScriptsKey, request.EnableEventScripts.ToString(), now, cancellationToken);
+ await UpsertAsync(LiveStartedScriptPathKey, request.LiveStartedScriptPath.Trim(), now, cancellationToken);
+ await UpsertAsync(LiveEndedScriptPathKey, request.LiveEndedScriptPath.Trim(), now, cancellationToken);
+ await UpsertAsync(SegmentCompletedScriptPathKey, request.SegmentCompletedScriptPath.Trim(), now, cancellationToken);
+ await UpsertAsync(EventScriptTimeoutSecondsKey, Math.Clamp(request.EventScriptTimeoutSeconds, 1, 3600).ToString(), now, cancellationToken);
await UpsertAsync(EnableEmailNotificationKey, request.EnableEmailNotification.ToString(), now, cancellationToken);
await UpsertAsync(EmailSmtpHostKey, request.EmailSmtpHost.Trim(), now, cancellationToken);
await UpsertAsync(EmailSmtpPortKey, request.EmailSmtpPort.ToString(), now, cancellationToken);
diff --git a/src/LiveRecorder.Domain/Entities/LiveRoom.cs b/src/LiveRecorder.Domain/Entities/LiveRoom.cs
index 77c7895..001884b 100644
--- a/src/LiveRecorder.Domain/Entities/LiveRoom.cs
+++ b/src/LiveRecorder.Domain/Entities/LiveRoom.cs
@@ -40,8 +40,36 @@ public class LiveRoom
public string? AnchorName { get; private set; }
+ public string? AnchorId { get; private set; }
+
+ public string? AvatarUrl { get; private set; }
+
public string? CoverUrl { get; private set; }
+ public string? PreferredQualityOverride { get; private set; }
+
+ public RecordOutputFormat? OutputFormatOverride { get; private set; }
+
+ public RecordSaveMode? SaveModeOverride { get; private set; }
+
+ public RecordingTemplateType? RecordingTemplateOverride { get; private set; }
+
+ public int? SegmentDurationMinutesOverride { get; private set; }
+
+ public bool? EnableAutoReconnectOverride { get; private set; }
+
+ public int? ReconnectDelayMaxSecondsOverride { get; private set; }
+
+ public int? ReadWriteTimeoutMillisecondsOverride { get; private set; }
+
+ public bool? EnableDanmakuRecordingOverride { get; private set; }
+
+ public bool? DanmakuIncludeNonChatEventsOverride { get; private set; }
+
+ public int? DanmakuMinPollIntervalMillisecondsOverride { get; private set; }
+
+ public int? DanmakuRetryDelayMaxSecondsOverride { get; private set; }
+
public bool IsEnabled { get; private set; }
public bool HasSentLiveNotificationForCurrentSession { get; private set; }
@@ -69,11 +97,13 @@ public class LiveRoom
UpdatedAt = updatedAt;
}
- public void UpdateMetadata(string? title, string? anchorName, string? coverUrl, DateTimeOffset updatedAt)
+ public void UpdateMetadata(string? title, string? anchorName, string? anchorId, string? avatarUrl, string? coverUrl, DateTimeOffset updatedAt)
{
- Title = title;
- AnchorName = anchorName;
- CoverUrl = coverUrl;
+ Title = PreferIncomingValue(title, Title);
+ AnchorName = PreferIncomingValue(anchorName, AnchorName);
+ AnchorId = PreferIncomingValue(anchorId, AnchorId);
+ AvatarUrl = PreferIncomingValue(avatarUrl, AvatarUrl);
+ CoverUrl = PreferIncomingValue(coverUrl, CoverUrl);
UpdatedAt = updatedAt;
}
@@ -100,4 +130,40 @@ public class LiveRoom
HasSentLiveNotificationForCurrentSession = true;
UpdatedAt = updatedAt;
}
+
+ public void UpdateRecordingSettingsOverrides(
+ string? preferredQualityOverride,
+ RecordOutputFormat? outputFormatOverride,
+ RecordSaveMode? saveModeOverride,
+ RecordingTemplateType? recordingTemplateOverride,
+ int? segmentDurationMinutesOverride,
+ bool? enableAutoReconnectOverride,
+ int? reconnectDelayMaxSecondsOverride,
+ int? readWriteTimeoutMillisecondsOverride,
+ bool? enableDanmakuRecordingOverride,
+ bool? danmakuIncludeNonChatEventsOverride,
+ int? danmakuMinPollIntervalMillisecondsOverride,
+ int? danmakuRetryDelayMaxSecondsOverride,
+ DateTimeOffset updatedAt)
+ {
+ PreferredQualityOverride = NormalizeNullable(preferredQualityOverride);
+ OutputFormatOverride = outputFormatOverride;
+ SaveModeOverride = saveModeOverride;
+ RecordingTemplateOverride = recordingTemplateOverride;
+ SegmentDurationMinutesOverride = segmentDurationMinutesOverride;
+ EnableAutoReconnectOverride = enableAutoReconnectOverride;
+ ReconnectDelayMaxSecondsOverride = reconnectDelayMaxSecondsOverride;
+ ReadWriteTimeoutMillisecondsOverride = readWriteTimeoutMillisecondsOverride;
+ EnableDanmakuRecordingOverride = enableDanmakuRecordingOverride;
+ DanmakuIncludeNonChatEventsOverride = danmakuIncludeNonChatEventsOverride;
+ DanmakuMinPollIntervalMillisecondsOverride = danmakuMinPollIntervalMillisecondsOverride;
+ DanmakuRetryDelayMaxSecondsOverride = danmakuRetryDelayMaxSecondsOverride;
+ UpdatedAt = updatedAt;
+ }
+
+ private static string? PreferIncomingValue(string? incomingValue, string? existingValue) =>
+ string.IsNullOrWhiteSpace(incomingValue) ? existingValue : incomingValue;
+
+ private static string? NormalizeNullable(string? value) =>
+ string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
diff --git a/src/LiveRecorder.Domain/Entities/RecordTask.cs b/src/LiveRecorder.Domain/Entities/RecordTask.cs
index f81798f..7468d07 100644
--- a/src/LiveRecorder.Domain/Entities/RecordTask.cs
+++ b/src/LiveRecorder.Domain/Entities/RecordTask.cs
@@ -108,6 +108,14 @@ public class RecordTask
UpdatedAt = updatedAt;
}
+ public void MarkProcessing(string? statusMessage, DateTimeOffset updatedAt)
+ {
+ Status = RecordTaskStatus.Processing;
+ ErrorMessage = statusMessage;
+ RecorderProcessId = null;
+ UpdatedAt = updatedAt;
+ }
+
public void MarkCompleted(DateTimeOffset endedAt, double? durationSeconds)
{
Status = RecordTaskStatus.Completed;
diff --git a/src/LiveRecorder.Domain/Enums/RecordTaskStatus.cs b/src/LiveRecorder.Domain/Enums/RecordTaskStatus.cs
index 3860ff4..1e30941 100644
--- a/src/LiveRecorder.Domain/Enums/RecordTaskStatus.cs
+++ b/src/LiveRecorder.Domain/Enums/RecordTaskStatus.cs
@@ -8,5 +8,6 @@ public enum RecordTaskStatus
Stopping = 3,
Completed = 4,
Failed = 5,
- Stopped = 6
+ Stopped = 6,
+ Processing = 7
}
diff --git a/src/LiveRecorder.Infrastructure/Persistence/DatabaseInitializer.cs b/src/LiveRecorder.Infrastructure/Persistence/DatabaseInitializer.cs
index 645be40..b6a12f5 100644
--- a/src/LiveRecorder.Infrastructure/Persistence/DatabaseInitializer.cs
+++ b/src/LiveRecorder.Infrastructure/Persistence/DatabaseInitializer.cs
@@ -18,6 +18,8 @@ public sealed class DatabaseInitializer
public async Task InitializeAsync(CancellationToken cancellationToken = default)
{
await _dbContext.Database.EnsureCreatedAsync(cancellationToken);
+ await _dbContext.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL;", cancellationToken);
+ await _dbContext.Database.ExecuteSqlRawAsync("PRAGMA synchronous=NORMAL;", cancellationToken);
await EnsureSchemaAsync(cancellationToken);
await BackfillRecordSessionsAsync(cancellationToken);
@@ -39,6 +41,11 @@ public sealed class DatabaseInitializer
["recording.save_mode"] = "SingleFile",
["recording.template"] = "StreamCopy",
["recording.segment_duration_minutes"] = "30",
+ ["recording.max_concurrent_ffmpeg_transcode_tasks"] = "1",
+ ["recording.mp4_finalize_timeout_minutes"] = "60",
+ ["storage.guard.enabled"] = "True",
+ ["storage.guard.pause_recording_below_mb"] = "1024",
+ ["storage.guard.resume_recording_above_mb"] = "4096",
["recording.enable_auto_reconnect"] = "True",
["recording.reconnect_delay_max_seconds"] = "5",
["recording.read_write_timeout_milliseconds"] = "15000000",
@@ -49,6 +56,11 @@ public sealed class DatabaseInitializer
["scheduler.enable_background_polling"] = "True",
["scheduler.auto_start_recording_on_live"] = "True",
["scheduler.polling_interval_seconds"] = "60",
+ ["event_scripts.enabled"] = "False",
+ ["event_scripts.live_started.path"] = string.Empty,
+ ["event_scripts.live_ended.path"] = string.Empty,
+ ["event_scripts.segment_completed.path"] = string.Empty,
+ ["event_scripts.timeout_seconds"] = "60",
["notification.email.enabled"] = "False",
["notification.email.smtp_host"] = string.Empty,
["notification.email.smtp_port"] = "587",
@@ -132,6 +144,21 @@ public sealed class DatabaseInitializer
{
}
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN AvatarUrl TEXT NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN AnchorId TEXT NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN PreferredQualityOverride TEXT NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN OutputFormatOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN SaveModeOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN RecordingTemplateOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN SegmentDurationMinutesOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN EnableAutoReconnectOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN ReconnectDelayMaxSecondsOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN ReadWriteTimeoutMillisecondsOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN EnableDanmakuRecordingOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN DanmakuIncludeNonChatEventsOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN DanmakuMinPollIntervalMillisecondsOverride INTEGER NULL;", cancellationToken);
+ await ExecuteAddColumnAsync("ALTER TABLE LiveRooms ADD COLUMN DanmakuRetryDelayMaxSecondsOverride INTEGER NULL;", cancellationToken);
+
await _dbContext.Database.ExecuteSqlRawAsync(
"""
CREATE TABLE IF NOT EXISTS RecordSessions (
diff --git a/src/LiveRecorder.Infrastructure/Persistence/LiveRecorderDbContext.cs b/src/LiveRecorder.Infrastructure/Persistence/LiveRecorderDbContext.cs
index 161dcfe..76c5831 100644
--- a/src/LiveRecorder.Infrastructure/Persistence/LiveRecorderDbContext.cs
+++ b/src/LiveRecorder.Infrastructure/Persistence/LiveRecorderDbContext.cs
@@ -41,7 +41,13 @@ public sealed class LiveRecorderDbContext : DbContext, IUnitOfWork
builder.Property(static x => x.RoomId).HasMaxLength(128);
builder.Property(static x => x.Title).HasMaxLength(256);
builder.Property(static x => x.AnchorName).HasMaxLength(128);
+ builder.Property(static x => x.AnchorId).HasMaxLength(128);
+ builder.Property(static x => x.AvatarUrl).HasMaxLength(512);
builder.Property(static x => x.CoverUrl).HasMaxLength(512);
+ builder.Property(static x => x.PreferredQualityOverride).HasMaxLength(64);
+ builder.Property(static x => x.OutputFormatOverride).HasConversion();
+ builder.Property(static x => x.SaveModeOverride).HasConversion();
+ builder.Property(static x => x.RecordingTemplateOverride).HasConversion();
builder.Property(static x => x.IsEnabled).HasDefaultValue(true);
builder.Property(static x => x.HasSentLiveNotificationForCurrentSession).HasDefaultValue(false);
});
diff --git a/src/LiveRecorder.Infrastructure/Persistence/Repositories/Repositories.cs b/src/LiveRecorder.Infrastructure/Persistence/Repositories/Repositories.cs
index 2ed117f..c4c16c2 100644
--- a/src/LiveRecorder.Infrastructure/Persistence/Repositories/Repositories.cs
+++ b/src/LiveRecorder.Infrastructure/Persistence/Repositories/Repositories.cs
@@ -257,6 +257,7 @@ public sealed class SystemLogRepository : ISystemLogRepository
Guid? liveRoomId = null,
Guid? recordSessionId = null,
Guid? recordTaskId = null,
+ SystemLogLevel? level = null,
int take = 200,
CancellationToken cancellationToken = default)
{
@@ -277,6 +278,11 @@ public sealed class SystemLogRepository : ISystemLogRepository
query = query.Where(item => item.RecordSessionId == recordSessionId.Value);
}
+ if (level.HasValue)
+ {
+ query = query.Where(item => item.Level == level.Value);
+ }
+
var items = await query.ToListAsync(cancellationToken);
return items
.OrderByDescending(static item => item.CreatedAt)
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliHttpClient.cs b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliHttpClient.cs
new file mode 100644
index 0000000..c35270d
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliHttpClient.cs
@@ -0,0 +1,800 @@
+using System.Net;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using LiveRecorder.Application.Abstractions.Platforms;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.Logging;
+
+namespace LiveRecorder.Infrastructure.Platforms.Bilibili;
+
+public sealed class BilibiliHttpClient
+{
+ private static readonly Regex RoomIdRegex = new(
+ """^(?:(?:https?:\/\/)?live\.bilibili\.com\/(?:blanc\/|h5\/)?)?(?\d+)\/?(?:[#\?].*)?$""",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
+
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly BilibiliWbiSigner _wbiSigner;
+ private readonly ILogger _logger;
+ private readonly string _buvid3 = BilibiliRequestDefaults.GenerateBuvid3();
+
+ public BilibiliHttpClient(
+ IHttpClientFactory httpClientFactory,
+ BilibiliWbiSigner wbiSigner,
+ ILogger logger)
+ {
+ _httpClientFactory = httpClientFactory;
+ _wbiSigner = wbiSigner;
+ _logger = logger;
+ }
+
+ internal async Task ResolveRoomAsync(string input, CancellationToken cancellationToken = default)
+ {
+ var trimmedInput = input.Trim();
+ var roomReference = ExtractRoomReference(trimmedInput);
+ if (string.IsNullOrWhiteSpace(roomReference))
+ {
+ throw new InvalidOperationException("Unable to extract a Bilibili live room id from the input.");
+ }
+
+ var roomInit = await GetRoomInitAsync(roomReference, cancellationToken);
+ if (string.IsNullOrWhiteSpace(roomInit.RoomId))
+ {
+ throw new InvalidOperationException("Bilibili did not return a valid room id.");
+ }
+
+ return new BilibiliResolvedRoom(
+ roomInit.RoomId,
+ roomInit.ShortRoomId,
+ $"https://live.bilibili.com/{roomInit.RoomId}");
+ }
+
+ internal async Task GetRoomInfoAsync(string roomId, CancellationToken cancellationToken = default)
+ {
+ using var document = await GetSignedApiDocumentAsync(
+ $"{BilibiliRequestDefaults.LiveApiHost}/xlive/web-room/v1/index/getInfoByRoom",
+ [
+ new("room_id", roomId),
+ new("web_location", "444.8")
+ ],
+ cancellationToken);
+
+ var data = GetRequiredProperty(document.RootElement, "data");
+ var roomInfo = GetRequiredProperty(data, "room_info");
+ var anchorInfo = TryGetNestedProperty(data, out var anchorValue, "anchor_info", "base_info")
+ ? anchorValue
+ : default;
+
+ return new BilibiliRoomInfo(
+ GetNumericLikeString(roomInfo, "room_id") ?? roomId,
+ GetNumericLikeString(roomInfo, "short_id"),
+ GetInt(roomInfo, "live_status") ?? 0,
+ GetString(roomInfo, "title"),
+ GetString(anchorInfo, "uname"),
+ GetNumericLikeString(anchorInfo, "uid"),
+ GetString(anchorInfo, "face") ?? GetString(anchorInfo, "head_photo"),
+ GetString(roomInfo, "cover") ?? GetString(roomInfo, "keyframe"));
+ }
+
+ internal async Task GetStreamUrlAsync(
+ string roomId,
+ string? preferredQuality,
+ CancellationToken cancellationToken = default)
+ {
+ var requestQn = MapRequestedQn(preferredQuality);
+ using var document = await GetSignedApiDocumentAsync(
+ $"{BilibiliRequestDefaults.LiveApiHost}/xlive/web-room/v2/index/getRoomPlayInfo",
+ [
+ new("room_id", roomId),
+ new("no_playurl", "0"),
+ new("mask", "1"),
+ new("qn", requestQn.ToString()),
+ new("platform", "web"),
+ new("protocol", "0,1"),
+ new("format", "0,1,2"),
+ new("codec", "0,1,2"),
+ new("dolby", "5"),
+ new("panorama", "1"),
+ new("hdr_type", "0,1"),
+ new("web_location", "444.8")
+ ],
+ cancellationToken);
+
+ var data = GetRequiredProperty(document.RootElement, "data");
+ var liveStatus = GetInt(data, "live_status") ?? 0;
+ if (liveStatus != 1)
+ {
+ throw new InvalidOperationException($"Bilibili room {roomId} is not currently live.");
+ }
+
+ if (!TryGetNestedProperty(data, out var streams, "playurl_info", "playurl", "stream") ||
+ streams.ValueKind != JsonValueKind.Array)
+ {
+ throw new InvalidOperationException("Bilibili did not return any playable stream entries.");
+ }
+
+ var candidates = new List();
+ foreach (var stream in streams.EnumerateArray())
+ {
+ var protocolName = GetString(stream, "protocol_name") ?? string.Empty;
+ if (!TryGetProperty(stream, "format", out var formatArray) || formatArray.ValueKind != JsonValueKind.Array)
+ {
+ continue;
+ }
+
+ foreach (var format in formatArray.EnumerateArray())
+ {
+ var formatName = GetString(format, "format_name") ?? string.Empty;
+ if (!TryGetProperty(format, "codec", out var codecArray) || codecArray.ValueKind != JsonValueKind.Array)
+ {
+ continue;
+ }
+
+ foreach (var codec in codecArray.EnumerateArray())
+ {
+ var baseUrl = GetString(codec, "base_url");
+ if (string.IsNullOrWhiteSpace(baseUrl))
+ {
+ continue;
+ }
+
+ var codecName = GetString(codec, "codec_name") ?? string.Empty;
+ var currentQn = GetInt(codec, "current_qn") ?? requestQn;
+ if (!TryGetProperty(codec, "url_info", out var urlInfoArray) || urlInfoArray.ValueKind != JsonValueKind.Array)
+ {
+ continue;
+ }
+
+ foreach (var urlInfo in urlInfoArray.EnumerateArray())
+ {
+ var host = GetString(urlInfo, "host");
+ var extra = GetString(urlInfo, "extra") ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(host))
+ {
+ continue;
+ }
+
+ var url = BuildPlayableUrl(host, baseUrl, extra);
+ var qualityKey = GetQualityKey(currentQn);
+ candidates.Add(new BilibiliStreamCandidate(
+ qualityKey,
+ GetQualityName(currentQn),
+ currentQn,
+ InferProtocol(protocolName, formatName, url),
+ url,
+ protocolName,
+ formatName,
+ codecName,
+ ComputeRank(currentQn, protocolName, formatName, codecName)));
+ }
+ }
+ }
+ }
+
+ var orderedCandidates = candidates
+ .DistinctBy(static item => item.Url)
+ .OrderByDescending(static item => item.Rank)
+ .ThenBy(static item => item.Protocol)
+ .ToList();
+ if (orderedCandidates.Count == 0)
+ {
+ throw new InvalidOperationException("Bilibili did not return any playable stream URL.");
+ }
+
+ var selected = SelectCandidate(orderedCandidates, preferredQuality);
+ var compatibilityFallback = SelectRecorderCompatibleCandidate(orderedCandidates, preferredQuality);
+ if (compatibilityFallback is not null &&
+ !string.Equals(selected.Url, compatibilityFallback.Url, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogInformation(
+ "Bilibili stream selection fell back to a recorder-compatible stream. RequestedQuality={RequestedQuality}; SelectedCodec={SelectedCodec}; SelectedProtocol={SelectedProtocol}; FallbackCodec={FallbackCodec}; FallbackProtocol={FallbackProtocol}; FallbackQuality={FallbackQuality}",
+ preferredQuality ?? "auto",
+ selected.CodecName,
+ selected.Protocol,
+ compatibilityFallback.CodecName,
+ compatibilityFallback.Protocol,
+ compatibilityFallback.QualityKey);
+ selected = compatibilityFallback;
+ }
+
+ var availableQualities = orderedCandidates
+ .GroupBy(static item => item.QualityKey, StringComparer.OrdinalIgnoreCase)
+ .Select(static group =>
+ {
+ var first = group.OrderByDescending(static item => item.Rank).First();
+ return new StreamQualityOption(
+ first.QualityKey,
+ first.QualityName,
+ first.Url,
+ first.Protocol,
+ first.Rank);
+ })
+ .OrderByDescending(static item => item.Rank)
+ .ToList();
+
+ return new BilibiliStreamSelection(
+ selected,
+ availableQualities,
+ GetStreamInputHeaders(roomId));
+ }
+
+ internal async Task GetDanmakuServerAsync(
+ string roomId,
+ CancellationToken cancellationToken = default)
+ {
+ using var document = await GetSignedApiDocumentAsync(
+ $"{BilibiliRequestDefaults.LiveApiHost}/xlive/web-room/v1/index/getDanmuInfo",
+ [
+ new("id", roomId),
+ new("type", "0"),
+ new("web_location", "444.8")
+ ],
+ cancellationToken);
+
+ var data = GetRequiredProperty(document.RootElement, "data");
+ if (!TryGetProperty(data, "host_list", out var hostList) || hostList.ValueKind != JsonValueKind.Array)
+ {
+ throw new InvalidOperationException("Bilibili danmaku server response did not include a host list.");
+ }
+
+ foreach (var host in hostList.EnumerateArray())
+ {
+ var hostname = GetString(host, "host");
+ var wssPort = GetInt(host, "wss_port");
+ var wssPortValue = wssPort.GetValueOrDefault();
+ if (!string.IsNullOrWhiteSpace(hostname) && wssPortValue > 0)
+ {
+ return new BilibiliDanmakuServerInfo(
+ new Uri($"wss://{hostname}:{wssPortValue}/sub"),
+ GetString(data, "token") ?? string.Empty,
+ _buvid3,
+ 0);
+ }
+ }
+
+ foreach (var host in hostList.EnumerateArray())
+ {
+ var hostname = GetString(host, "host");
+ var wsPort = GetInt(host, "ws_port") ?? GetInt(host, "port");
+ var wsPortValue = wsPort.GetValueOrDefault();
+ if (!string.IsNullOrWhiteSpace(hostname) && wsPortValue > 0)
+ {
+ return new BilibiliDanmakuServerInfo(
+ new Uri($"ws://{hostname}:{wsPortValue}/sub"),
+ GetString(data, "token") ?? string.Empty,
+ _buvid3,
+ 0);
+ }
+ }
+
+ throw new InvalidOperationException("Bilibili danmaku server response did not contain a usable websocket endpoint.");
+ }
+
+ internal StreamInputHeaders GetStreamInputHeaders(string roomId)
+ {
+ return new StreamInputHeaders(
+ BilibiliRequestDefaults.UserAgent,
+ $"{BilibiliRequestDefaults.RefererBase}{roomId}",
+ $"buvid3={_buvid3}",
+ new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["Origin"] = BilibiliRequestDefaults.Origin
+ });
+ }
+
+ internal CookieContainer CreateCookieContainer()
+ {
+ var container = new CookieContainer();
+ container.Add(new Cookie("buvid3", _buvid3, "/", ".bilibili.com"));
+ return container;
+ }
+
+ private async Task GetRoomInitAsync(string roomReference, CancellationToken cancellationToken)
+ {
+ using var document = await GetApiDocumentAsync(
+ $"{BilibiliRequestDefaults.LiveApiHost}/room/v1/Room/room_init",
+ [
+ new("id", roomReference)
+ ],
+ cancellationToken);
+
+ var data = GetRequiredProperty(document.RootElement, "data");
+ return new BilibiliRoomInit(
+ GetNumericLikeString(data, "room_id") ?? roomReference,
+ GetNumericLikeString(data, "short_id"),
+ GetInt(data, "live_status") ?? 0);
+ }
+
+ private async Task GetSignedApiDocumentAsync(
+ string endpoint,
+ IEnumerable> queryParameters,
+ CancellationToken cancellationToken)
+ {
+ var signedQuery = await _wbiSigner.SignAsync(queryParameters, cancellationToken);
+ return await GetApiDocumentAsync(
+ endpoint,
+ signedQuery.Select(static item => new KeyValuePair(item.Key, item.Value)),
+ cancellationToken);
+ }
+
+ private async Task GetApiDocumentAsync(
+ string endpoint,
+ IEnumerable> queryParameters,
+ CancellationToken cancellationToken)
+ {
+ var requestUri = BuildUri(endpoint, queryParameters);
+ using var response = await SendWithRetryAsync(
+ () =>
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
+ BilibiliRequestDefaults.Apply(request);
+ request.Headers.TryAddWithoutValidation("Cookie", $"buvid3={_buvid3}");
+ return request;
+ },
+ HttpCompletionOption.ResponseHeadersRead,
+ cancellationToken);
+
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
+ EnsureApiSuccess(document.RootElement, requestUri);
+ return document;
+ }
+
+ private async Task SendWithRetryAsync(
+ Func requestFactory,
+ HttpCompletionOption completionOption,
+ CancellationToken cancellationToken)
+ {
+ Exception? lastException = null;
+ for (var attempt = 1; attempt <= 3; attempt++)
+ {
+ try
+ {
+ var response = await _httpClientFactory
+ .CreateClient(BilibiliRequestDefaults.ClientName)
+ .SendAsync(requestFactory(), completionOption, cancellationToken);
+
+ if (attempt < 3 && IsTransientStatusCode(response.StatusCode))
+ {
+ response.Dispose();
+ await Task.Delay(TimeSpan.FromMilliseconds(250 * attempt), cancellationToken);
+ continue;
+ }
+
+ response.EnsureSuccessStatusCode();
+ return response;
+ }
+ catch (Exception ex) when (attempt < 3 && IsTransientException(ex, cancellationToken))
+ {
+ lastException = ex;
+ _logger.LogDebug(ex, "Retrying Bilibili request attempt {Attempt}", attempt);
+ await Task.Delay(TimeSpan.FromMilliseconds(250 * attempt), cancellationToken);
+ }
+ }
+
+ throw lastException ?? new InvalidOperationException("Bilibili request failed.");
+ }
+
+ private static string? ExtractRoomReference(string input)
+ {
+ var match = RoomIdRegex.Match(input);
+ if (match.Success)
+ {
+ return match.Groups["id"].Value;
+ }
+
+ if (!Uri.TryCreate(input, UriKind.Absolute, out var uri))
+ {
+ return null;
+ }
+
+ if (!uri.Host.Contains("live.bilibili.com", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ return uri.Segments
+ .Select(static item => item.Trim('/'))
+ .FirstOrDefault(static item => item.All(char.IsDigit));
+ }
+
+ private static string BuildPlayableUrl(string host, string baseUrl, string extra)
+ {
+ var normalizedHost = host.TrimEnd('/');
+ var normalizedBaseUrl = baseUrl.StartsWith('/') ? baseUrl : "/" + baseUrl;
+ return $"{normalizedHost}{normalizedBaseUrl}{extra}";
+ }
+
+ private static string InferProtocol(string protocolName, string formatName, string url)
+ {
+ if (formatName.Contains("flv", StringComparison.OrdinalIgnoreCase))
+ {
+ return "flv";
+ }
+
+ if (protocolName.Contains("hls", StringComparison.OrdinalIgnoreCase) ||
+ url.Contains(".m3u8", StringComparison.OrdinalIgnoreCase))
+ {
+ return "hls";
+ }
+
+ return formatName.Equals("fmp4", StringComparison.OrdinalIgnoreCase) ? "fmp4" : protocolName;
+ }
+
+ private static int ComputeRank(int qn, string protocolName, string formatName, string codecName)
+ {
+ var rank = qn * 100;
+ rank += formatName.Contains("flv", StringComparison.OrdinalIgnoreCase) ? 30 : 0;
+ rank += protocolName.Contains("http_stream", StringComparison.OrdinalIgnoreCase) ? 10 : 0;
+ rank += codecName.Contains("avc", StringComparison.OrdinalIgnoreCase) ? 5 : 0;
+ return rank;
+ }
+
+ private static BilibiliStreamCandidate SelectCandidate(
+ IReadOnlyList candidates,
+ string? preferredQuality)
+ {
+ if (!string.IsNullOrWhiteSpace(preferredQuality))
+ {
+ var normalized = preferredQuality.Trim();
+ var preferredQn = MapRequestedQn(normalized);
+ var matched = candidates
+ .Where(item =>
+ item.Qn == preferredQn ||
+ item.QualityKey.Equals(normalized, StringComparison.OrdinalIgnoreCase) ||
+ item.QualityName.Equals(normalized, StringComparison.OrdinalIgnoreCase))
+ .OrderByDescending(static item => item.Rank)
+ .FirstOrDefault();
+ if (matched is not null)
+ {
+ return matched;
+ }
+ }
+
+ return candidates[0];
+ }
+
+ private static BilibiliStreamCandidate? SelectRecorderCompatibleCandidate(
+ IReadOnlyList candidates,
+ string? preferredQuality)
+ {
+ var exactMatches = string.IsNullOrWhiteSpace(preferredQuality)
+ ? Array.Empty()
+ : candidates
+ .Where(item => MatchesPreferredQuality(item, preferredQuality))
+ .ToArray();
+
+ var exactAvc = OrderRecorderCompatibility(exactMatches)
+ .FirstOrDefault(static item => IsAvcCodec(item.CodecName));
+ if (exactAvc is not null)
+ {
+ return exactAvc;
+ }
+
+ var anyAvc = OrderRecorderCompatibility(candidates)
+ .FirstOrDefault(static item => IsAvcCodec(item.CodecName));
+ if (anyAvc is not null)
+ {
+ return anyAvc;
+ }
+
+ var exactHevc = OrderRecorderCompatibility(exactMatches)
+ .FirstOrDefault(static item => IsHevcCodec(item.CodecName));
+ if (exactHevc is not null)
+ {
+ return exactHevc;
+ }
+
+ var anyHevc = OrderRecorderCompatibility(candidates)
+ .FirstOrDefault(static item => IsHevcCodec(item.CodecName));
+ if (anyHevc is not null)
+ {
+ return anyHevc;
+ }
+
+ return null;
+ }
+
+ private static IEnumerable OrderRecorderCompatibility(IEnumerable candidates) =>
+ candidates
+ .OrderByDescending(static item => GetCodecCompatibilityScore(item.CodecName))
+ .ThenByDescending(static item => GetTransportCompatibilityScore(item.ProtocolName, item.FormatName, item.Protocol))
+ .ThenByDescending(static item => item.Qn)
+ .ThenByDescending(static item => item.Rank);
+
+ private static int GetCodecCompatibilityScore(string codecName)
+ {
+ if (IsAvcCodec(codecName))
+ {
+ return 3;
+ }
+
+ if (IsHevcCodec(codecName))
+ {
+ return 2;
+ }
+
+ if (IsAv1Codec(codecName))
+ {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ private static int GetTransportCompatibilityScore(string protocolName, string formatName, string protocol)
+ {
+ var score = 0;
+ if (formatName.Contains("flv", StringComparison.OrdinalIgnoreCase))
+ {
+ score += 3;
+ }
+ else if (protocolName.Contains("http_stream", StringComparison.OrdinalIgnoreCase))
+ {
+ score += 2;
+ }
+ else if (protocol.Equals("hls", StringComparison.OrdinalIgnoreCase))
+ {
+ score += 1;
+ }
+
+ if (formatName.Equals("fmp4", StringComparison.OrdinalIgnoreCase))
+ {
+ score -= 1;
+ }
+
+ return score;
+ }
+
+ private static bool MatchesPreferredQuality(BilibiliStreamCandidate candidate, string preferredQuality)
+ {
+ var normalized = preferredQuality.Trim();
+ var preferredQn = MapRequestedQn(normalized);
+ return candidate.Qn == preferredQn ||
+ candidate.QualityKey.Equals(normalized, StringComparison.OrdinalIgnoreCase) ||
+ candidate.QualityName.Equals(normalized, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool IsAvcCodec(string codecName) =>
+ codecName.Contains("avc", StringComparison.OrdinalIgnoreCase) ||
+ codecName.Contains("h264", StringComparison.OrdinalIgnoreCase);
+
+ private static bool IsHevcCodec(string codecName) =>
+ codecName.Contains("hevc", StringComparison.OrdinalIgnoreCase) ||
+ codecName.Contains("h265", StringComparison.OrdinalIgnoreCase);
+
+ private static bool IsAv1Codec(string codecName) =>
+ codecName.Contains("av1", StringComparison.OrdinalIgnoreCase);
+
+ private static int MapRequestedQn(string? preferredQuality)
+ {
+ if (string.IsNullOrWhiteSpace(preferredQuality))
+ {
+ return 10000;
+ }
+
+ var normalized = preferredQuality.Trim();
+ if (int.TryParse(normalized, out var numeric))
+ {
+ return numeric;
+ }
+
+ return normalized.ToLowerInvariant() switch
+ {
+ "dolby" => 30000,
+ "4k" or "uhd" => 20000,
+ "origin" or "source" or "raw" => 10000,
+ "full_hd" or "fullhd" or "blue_ray" or "blueray" => 400,
+ "hd" or "super" => 250,
+ "sd" or "high" => 150,
+ "ld" or "smooth" or "fluency" => 80,
+ _ => 10000
+ };
+ }
+
+ private static string GetQualityKey(int qn) =>
+ qn switch
+ {
+ 30000 => "dolby",
+ 20000 => "uhd",
+ 10000 => "origin",
+ 400 => "full_hd",
+ 250 => "hd",
+ 150 => "sd",
+ 80 => "ld",
+ _ => qn.ToString()
+ };
+
+ private static string GetQualityName(int qn) =>
+ qn switch
+ {
+ 30000 => "Dolby",
+ 20000 => "UHD",
+ 10000 => "Origin",
+ 400 => "Full HD",
+ 250 => "HD",
+ 150 => "SD",
+ 80 => "Low",
+ _ => qn.ToString()
+ };
+
+ private static bool IsTransientStatusCode(HttpStatusCode statusCode) =>
+ statusCode == HttpStatusCode.RequestTimeout ||
+ statusCode == (HttpStatusCode)429 ||
+ (int)statusCode >= 500;
+
+ private static bool IsTransientException(Exception exception, CancellationToken cancellationToken)
+ {
+ if (exception is OperationCanceledException && cancellationToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ return exception is HttpRequestException or IOException or TimeoutException ||
+ exception.InnerException is not null && IsTransientException(exception.InnerException, cancellationToken);
+ }
+
+ private static string BuildUri(string endpoint, IEnumerable> queryParameters)
+ {
+ var lookup = queryParameters.ToDictionary(
+ static item => item.Key,
+ static item => item.Value,
+ StringComparer.Ordinal);
+ return QueryHelpers.AddQueryString(endpoint, lookup);
+ }
+
+ private static void EnsureApiSuccess(JsonElement root, string requestUri)
+ {
+ var code = GetInt(root, "code") ?? 0;
+ if (code == 0)
+ {
+ return;
+ }
+
+ var message = GetString(root, "message") ?? GetString(root, "msg") ?? "unknown error";
+ throw new InvalidOperationException(
+ $"Bilibili API request failed ({code}) for request {requestUri}: {message}");
+ }
+
+ private static JsonElement GetRequiredProperty(JsonElement element, string propertyName)
+ {
+ if (!TryGetProperty(element, propertyName, out var value))
+ {
+ throw new InvalidOperationException($"Bilibili response is missing required property '{propertyName}'.");
+ }
+
+ return value;
+ }
+
+ private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
+ {
+ if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(propertyName, out value))
+ {
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ private static bool TryGetNestedProperty(JsonElement element, out JsonElement value, params string[] path)
+ {
+ value = element;
+ foreach (var segment in path)
+ {
+ if (!TryGetProperty(value, segment, out value))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static string? GetString(JsonElement element, string propertyName)
+ {
+ if (!TryGetProperty(element, propertyName, out var value))
+ {
+ return null;
+ }
+
+ return value.ValueKind switch
+ {
+ JsonValueKind.String => value.GetString(),
+ JsonValueKind.Number => value.GetRawText(),
+ _ => null
+ };
+ }
+
+ private static int? GetInt(JsonElement element, string propertyName)
+ {
+ if (!TryGetProperty(element, propertyName, out var value))
+ {
+ return null;
+ }
+
+ if (value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var intValue))
+ {
+ return intValue;
+ }
+
+ return value.ValueKind == JsonValueKind.String && int.TryParse(value.GetString(), out intValue)
+ ? intValue
+ : null;
+ }
+
+ private static string? GetNumericLikeString(JsonElement element, string propertyName)
+ {
+ var value = GetString(element, propertyName);
+ return !string.IsNullOrWhiteSpace(value) && value.All(char.IsDigit) ? value : null;
+ }
+}
+
+internal static class BilibiliRequestDefaults
+{
+ public const string ClientName = "bilibili";
+ public const string LiveApiHost = "https://api.live.bilibili.com";
+ public const string Origin = "https://live.bilibili.com";
+ public const string RefererBase = "https://live.bilibili.com/";
+ public const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0";
+
+ public static void Apply(HttpRequestMessage request)
+ {
+ request.Headers.TryAddWithoutValidation("Accept", "application/json, text/javascript, */*; q=0.01");
+ request.Headers.TryAddWithoutValidation("Accept-Language", "zh-CN");
+ request.Headers.TryAddWithoutValidation("Origin", Origin);
+ request.Headers.TryAddWithoutValidation("Referer", RefererBase);
+ request.Headers.TryAddWithoutValidation("User-Agent", UserAgent);
+ }
+
+ public static string GenerateBuvid3()
+ {
+ Span randomBytes = stackalloc byte[16];
+ Random.Shared.NextBytes(randomBytes);
+ var guid = new Guid(randomBytes).ToString().ToUpperInvariant();
+ var suffix = (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() % 100000).ToString("D5");
+ return $"{guid}{suffix}infoc";
+ }
+}
+
+internal sealed record BilibiliResolvedRoom(
+ string RoomId,
+ string? ShortRoomId,
+ string NormalizedUrl);
+
+internal sealed record BilibiliRoomInit(
+ string RoomId,
+ string? ShortRoomId,
+ int LiveStatus);
+
+internal sealed record BilibiliRoomInfo(
+ string RoomId,
+ string? ShortRoomId,
+ int LiveStatus,
+ string? Title,
+ string? AnchorName,
+ string? AnchorId,
+ string? AvatarUrl,
+ string? CoverUrl);
+
+internal sealed record BilibiliStreamCandidate(
+ string QualityKey,
+ string QualityName,
+ int Qn,
+ string Protocol,
+ string Url,
+ string ProtocolName,
+ string FormatName,
+ string CodecName,
+ int Rank);
+
+internal sealed record BilibiliStreamSelection(
+ BilibiliStreamCandidate Selected,
+ IReadOnlyList AvailableQualities,
+ StreamInputHeaders InputHeaders);
+
+internal sealed record BilibiliDanmakuServerInfo(
+ Uri Endpoint,
+ string Token,
+ string Buvid3,
+ long Uid);
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliLivePlatformAdapter.cs b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliLivePlatformAdapter.cs
index 602a2da..b85a88f 100644
--- a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliLivePlatformAdapter.cs
+++ b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliLivePlatformAdapter.cs
@@ -5,21 +5,61 @@ namespace LiveRecorder.Infrastructure.Platforms.Bilibili;
public sealed class BilibiliLivePlatformAdapter : ILivePlatformAdapter
{
+ private readonly BilibiliHttpClient _bilibiliHttpClient;
+
+ public BilibiliLivePlatformAdapter(BilibiliHttpClient bilibiliHttpClient)
+ {
+ _bilibiliHttpClient = bilibiliHttpClient;
+ }
+
public LivePlatformType PlatformType => LivePlatformType.Bilibili;
- public bool CanHandle(string input) =>
- !string.IsNullOrWhiteSpace(input) &&
- input.Contains("bilibili.com", StringComparison.OrdinalIgnoreCase);
+ public bool CanHandle(string input)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return false;
+ }
- public Task ParseRoomAsync(string input, CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("Bilibili 适配器尚未实现。");
+ return input.Contains("live.bilibili.com", StringComparison.OrdinalIgnoreCase);
+ }
- public Task GetLiveStatusAsync(string roomId, CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("Bilibili 适配器尚未实现。");
+ public async Task ParseRoomAsync(string input, CancellationToken cancellationToken = default)
+ {
+ var resolvedRoom = await _bilibiliHttpClient.ResolveRoomAsync(input, cancellationToken);
+ return new ParsedLiveRoom(
+ PlatformType,
+ resolvedRoom.RoomId,
+ input.Trim(),
+ resolvedRoom.NormalizedUrl);
+ }
- public Task GetStreamUrlAsync(
+ public async Task GetLiveStatusAsync(string roomId, CancellationToken cancellationToken = default)
+ {
+ var roomInfo = await _bilibiliHttpClient.GetRoomInfoAsync(roomId, cancellationToken);
+ return new LiveStatusSnapshot(
+ roomInfo.LiveStatus == 1,
+ roomInfo.Title,
+ roomInfo.AnchorName,
+ roomInfo.AnchorId,
+ roomInfo.AvatarUrl,
+ roomInfo.CoverUrl,
+ roomInfo.LiveStatus,
+ roomInfo.LiveStatus.ToString());
+ }
+
+ public async Task GetStreamUrlAsync(
string roomId,
string? preferredQuality = null,
- CancellationToken cancellationToken = default) =>
- throw new NotSupportedException("Bilibili 适配器尚未实现。");
+ CancellationToken cancellationToken = default)
+ {
+ var selection = await _bilibiliHttpClient.GetStreamUrlAsync(roomId, preferredQuality, cancellationToken);
+ return new StreamUrlResult(
+ selection.Selected.QualityKey,
+ selection.Selected.Protocol,
+ selection.Selected.Url,
+ selection.InputHeaders,
+ selection.AvailableQualities,
+ selection.Selected.CodecName);
+ }
}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliWbiSigner.cs b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliWbiSigner.cs
new file mode 100644
index 0000000..214935b
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/BilibiliWbiSigner.cs
@@ -0,0 +1,136 @@
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
+
+namespace LiveRecorder.Infrastructure.Platforms.Bilibili;
+
+public sealed class BilibiliWbiSigner
+{
+ private static readonly byte[] KeyMap =
+ [
+ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35,
+ 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13,
+ 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4,
+ 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52
+ ];
+
+ private static readonly TimeSpan KeyRefreshInterval = TimeSpan.FromHours(4);
+
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly ILogger _logger;
+ private readonly SemaphoreSlim _gate = new(1, 1);
+
+ private string? _key;
+ private DateTimeOffset _lastUpdatedAt = DateTimeOffset.MinValue;
+
+ public BilibiliWbiSigner(
+ IHttpClientFactory httpClientFactory,
+ ILogger logger)
+ {
+ _httpClientFactory = httpClientFactory;
+ _logger = logger;
+ }
+
+ public async Task>> SignAsync(
+ IEnumerable> queryParameters,
+ CancellationToken cancellationToken = default)
+ {
+ await EnsureKeyAsync(cancellationToken);
+ var key = _key ?? throw new InvalidOperationException("Bilibili WBI key is not available.");
+ var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
+
+ var sanitized = queryParameters
+ .Select(static item => new KeyValuePair(
+ item.Key,
+ SanitizeValue(item.Value)))
+ .Append(new KeyValuePair("wts", timestamp))
+ .OrderBy(static item => item.Key, StringComparer.Ordinal)
+ .ToArray();
+
+ using var formContent = new FormUrlEncodedContent(sanitized);
+ var encoded = await formContent.ReadAsStringAsync(cancellationToken);
+ var hashBytes = MD5.HashData(Encoding.UTF8.GetBytes(encoded + key));
+ var signature = Convert.ToHexString(hashBytes).ToLowerInvariant();
+
+ return sanitized
+ .Append(new KeyValuePair("w_rid", signature))
+ .ToArray();
+ }
+
+ private async Task EnsureKeyAsync(CancellationToken cancellationToken)
+ {
+ if (!string.IsNullOrWhiteSpace(_key) &&
+ _lastUpdatedAt + KeyRefreshInterval > DateTimeOffset.UtcNow)
+ {
+ return;
+ }
+
+ await _gate.WaitAsync(cancellationToken);
+ try
+ {
+ if (!string.IsNullOrWhiteSpace(_key) &&
+ _lastUpdatedAt + KeyRefreshInterval > DateTimeOffset.UtcNow)
+ {
+ return;
+ }
+
+ using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.bilibili.com/x/web-interface/nav");
+ BilibiliRequestDefaults.Apply(request);
+
+ using var response = await _httpClientFactory
+ .CreateClient(BilibiliRequestDefaults.ClientName)
+ .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
+ response.EnsureSuccessStatusCode();
+
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
+ using var document = await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
+ var root = document.RootElement;
+ var data = root.GetProperty("data");
+ var wbiImg = data.GetProperty("wbi_img");
+ var imgUrl = wbiImg.GetProperty("img_url").GetString();
+ var subUrl = wbiImg.GetProperty("sub_url").GetString();
+
+ if (string.IsNullOrWhiteSpace(imgUrl) || string.IsNullOrWhiteSpace(subUrl))
+ {
+ throw new InvalidOperationException("Bilibili WBI response did not include img/sub keys.");
+ }
+
+ _key = BuildKey(
+ Path.GetFileNameWithoutExtension(imgUrl),
+ Path.GetFileNameWithoutExtension(subUrl));
+ _lastUpdatedAt = DateTimeOffset.UtcNow;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Refresh Bilibili WBI key failed.");
+ throw;
+ }
+ finally
+ {
+ _gate.Release();
+ }
+ }
+
+ private static string BuildKey(string img, string sub)
+ {
+ var full = img + sub;
+ Span buffer = stackalloc char[32];
+ for (var index = 0; index < buffer.Length; index++)
+ {
+ buffer[index] = full[KeyMap[index]];
+ }
+
+ return new string(buffer);
+ }
+
+ private static string SanitizeValue(string? value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return string.Empty;
+ }
+
+ return new string(value.Where(static ch => ch is not '!' and not '\'' and not '(' and not ')' and not '*').ToArray());
+ }
+}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuAdapter.cs b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuAdapter.cs
new file mode 100644
index 0000000..ef67d12
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuAdapter.cs
@@ -0,0 +1,279 @@
+using System.Net.WebSockets;
+using System.Text;
+using LiveRecorder.Application.Abstractions.Platforms;
+using LiveRecorder.Domain.Enums;
+using Microsoft.Extensions.Logging;
+
+namespace LiveRecorder.Infrastructure.Platforms.Bilibili.Danmaku;
+
+public sealed class BilibiliDanmakuAdapter : ILiveDanmakuAdapter
+{
+ private readonly BilibiliHttpClient _bilibiliHttpClient;
+ private readonly ILoggerFactory _loggerFactory;
+
+ public BilibiliDanmakuAdapter(
+ BilibiliHttpClient bilibiliHttpClient,
+ ILoggerFactory loggerFactory)
+ {
+ _bilibiliHttpClient = bilibiliHttpClient;
+ _loggerFactory = loggerFactory;
+ }
+
+ public LivePlatformType PlatformType => LivePlatformType.Bilibili;
+
+ public bool CanHandle(LivePlatformType platformType) => platformType == LivePlatformType.Bilibili;
+
+ public Task ConnectAsync(
+ DanmakuConnectionContext context,
+ CancellationToken cancellationToken = default) =>
+ Task.FromResult(
+ new BilibiliDanmakuConnection(
+ _bilibiliHttpClient,
+ _loggerFactory.CreateLogger(),
+ context));
+}
+
+internal sealed class BilibiliDanmakuConnection : ILiveDanmakuConnection
+{
+ private readonly BilibiliHttpClient _bilibiliHttpClient;
+ private readonly ILogger _logger;
+ private readonly DanmakuConnectionContext _context;
+ private readonly SemaphoreSlim _sendGate = new(1, 1);
+
+ private ClientWebSocket? _socket;
+
+ public BilibiliDanmakuConnection(
+ BilibiliHttpClient bilibiliHttpClient,
+ ILogger logger,
+ DanmakuConnectionContext context)
+ {
+ _bilibiliHttpClient = bilibiliHttpClient;
+ _logger = logger;
+ _context = context;
+ }
+
+ public async Task StartAsync(Func onEvent, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(onEvent);
+
+ var backoff = TimeSpan.FromSeconds(1);
+ var maxBackoff = TimeSpan.FromSeconds(Math.Max(1, _context.RetryDelayMaxSeconds));
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ ClientWebSocket? socket = null;
+ CancellationTokenSource? heartbeatCancellation = null;
+ Task? heartbeatTask = null;
+
+ try
+ {
+ var serverInfo = await _bilibiliHttpClient.GetDanmakuServerAsync(_context.RoomId, cancellationToken);
+ socket = CreateSocket();
+ _socket = socket;
+ await socket.ConnectAsync(serverInfo.Endpoint, cancellationToken);
+
+ await SendPacketAsync(
+ socket,
+ BilibiliDanmakuProtocol.CreateAuthenticationPacket(
+ _context.RoomId,
+ serverInfo.Token,
+ serverInfo.Buvid3,
+ serverInfo.Uid),
+ cancellationToken);
+
+ heartbeatCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ heartbeatTask = RunHeartbeatAsync(socket, heartbeatCancellation.Token);
+
+ _logger.LogInformation(
+ "Bilibili danmaku websocket connected for room {RoomId}. Endpoint={Endpoint}",
+ _context.RoomId,
+ serverInfo.Endpoint);
+
+ backoff = TimeSpan.FromSeconds(1);
+ await ReceiveLoopAsync(socket, onEvent, cancellationToken);
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Bilibili danmaku websocket failed for room {RoomId}. Retrying.", _context.RoomId);
+ }
+ finally
+ {
+ if (heartbeatCancellation is not null)
+ {
+ heartbeatCancellation.Cancel();
+ }
+
+ if (heartbeatTask is not null)
+ {
+ try
+ {
+ await heartbeatTask;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "Bilibili heartbeat loop ended with an error for room {RoomId}.", _context.RoomId);
+ }
+ }
+
+ await CloseSocketAsync(socket, cancellationToken);
+ if (ReferenceEquals(_socket, socket))
+ {
+ _socket = null;
+ }
+
+ heartbeatCancellation?.Dispose();
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
+ await Task.Delay(backoff, cancellationToken);
+ backoff = TimeSpan.FromSeconds(Math.Min(maxBackoff.TotalSeconds, backoff.TotalSeconds * 2));
+ }
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await CloseSocketAsync(_socket, CancellationToken.None);
+ _socket = null;
+ _sendGate.Dispose();
+ }
+
+ private ClientWebSocket CreateSocket()
+ {
+ var socket = new ClientWebSocket();
+ socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
+ socket.Options.SetRequestHeader("Origin", BilibiliRequestDefaults.Origin);
+ socket.Options.SetRequestHeader("Referer", $"{BilibiliRequestDefaults.RefererBase}{_context.RoomId}");
+ socket.Options.SetRequestHeader("User-Agent", BilibiliRequestDefaults.UserAgent);
+ socket.Options.Cookies = _bilibiliHttpClient.CreateCookieContainer();
+ return socket;
+ }
+
+ private async Task ReceiveLoopAsync(
+ ClientWebSocket socket,
+ Func onEvent,
+ CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested &&
+ socket.State is WebSocketState.Open or WebSocketState.CloseReceived)
+ {
+ var (messageType, payload) = await ReceiveMessageAsync(socket, cancellationToken);
+ if (messageType == WebSocketMessageType.Close)
+ {
+ _logger.LogWarning("Bilibili danmaku websocket was closed by the remote endpoint for room {RoomId}.", _context.RoomId);
+ return;
+ }
+
+ if (messageType == WebSocketMessageType.Text)
+ {
+ _logger.LogDebug(
+ "Bilibili danmaku websocket returned a text payload for room {RoomId}: {Payload}",
+ _context.RoomId,
+ Encoding.UTF8.GetString(payload));
+ continue;
+ }
+
+ foreach (var danmakuEvent in BilibiliDanmakuProtocol.Parse(payload))
+ {
+ await onEvent(danmakuEvent);
+ }
+ }
+ }
+
+ private async Task RunHeartbeatAsync(ClientWebSocket socket, CancellationToken cancellationToken)
+ {
+ var interval = TimeSpan.FromSeconds(30);
+ while (!cancellationToken.IsCancellationRequested && socket.State == WebSocketState.Open)
+ {
+ await Task.Delay(interval, cancellationToken);
+ if (cancellationToken.IsCancellationRequested || socket.State != WebSocketState.Open)
+ {
+ break;
+ }
+
+ await SendPacketAsync(socket, BilibiliDanmakuProtocol.CreateHeartbeatPacket(), cancellationToken);
+ }
+ }
+
+ private async Task SendPacketAsync(ClientWebSocket socket, byte[] payload, CancellationToken cancellationToken)
+ {
+ await _sendGate.WaitAsync(cancellationToken);
+ try
+ {
+ if (socket.State != WebSocketState.Open)
+ {
+ return;
+ }
+
+ await socket.SendAsync(
+ new ArraySegment(payload),
+ WebSocketMessageType.Binary,
+ true,
+ cancellationToken);
+ }
+ finally
+ {
+ _sendGate.Release();
+ }
+ }
+
+ private static async Task<(WebSocketMessageType MessageType, byte[] Payload)> ReceiveMessageAsync(
+ ClientWebSocket socket,
+ CancellationToken cancellationToken)
+ {
+ var buffer = new byte[8192];
+ using var output = new MemoryStream();
+
+ while (true)
+ {
+ var result = await socket.ReceiveAsync(new ArraySegment(buffer), cancellationToken);
+ if (result.MessageType == WebSocketMessageType.Close)
+ {
+ return (WebSocketMessageType.Close, Array.Empty());
+ }
+
+ if (result.Count > 0)
+ {
+ output.Write(buffer, 0, result.Count);
+ }
+
+ if (result.EndOfMessage)
+ {
+ return (result.MessageType, output.ToArray());
+ }
+ }
+ }
+
+ private static async Task CloseSocketAsync(ClientWebSocket? socket, CancellationToken cancellationToken)
+ {
+ if (socket is null)
+ {
+ return;
+ }
+
+ try
+ {
+ if (socket.State is WebSocketState.Open or WebSocketState.CloseReceived)
+ {
+ await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "disposed", cancellationToken);
+ }
+ }
+ catch
+ {
+ }
+ finally
+ {
+ socket.Dispose();
+ }
+ }
+}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuProtocol.cs b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuProtocol.cs
new file mode 100644
index 0000000..f58e955
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Bilibili/Danmaku/BilibiliDanmakuProtocol.cs
@@ -0,0 +1,395 @@
+using System.Buffers.Binary;
+using System.IO.Compression;
+using System.Text;
+using System.Text.Json;
+using LiveRecorder.Application.Abstractions.Platforms;
+
+namespace LiveRecorder.Infrastructure.Platforms.Bilibili.Danmaku;
+
+internal static class BilibiliDanmakuProtocol
+{
+ private const short HeaderLength = 16;
+ private const int OperationHeartbeat = 2;
+ private const int OperationAuth = 7;
+ private const int OperationMessage = 5;
+
+ public static byte[] CreateAuthenticationPacket(string roomId, string token, string buvid3, long uid)
+ {
+ var payload = JsonSerializer.SerializeToUtf8Bytes(new
+ {
+ uid,
+ roomid = long.Parse(roomId),
+ protover = 3,
+ buvid = buvid3,
+ platform = "web",
+ type = 2,
+ key = token
+ });
+
+ return CreatePacket(OperationAuth, payload);
+ }
+
+ public static byte[] CreateHeartbeatPacket() => CreatePacket(OperationHeartbeat, Array.Empty());
+
+ public static IReadOnlyList Parse(byte[] payload)
+ {
+ var events = new List();
+ ParsePackets(payload, events);
+ return events;
+ }
+
+ private static byte[] CreatePacket(int operation, byte[] payload)
+ {
+ var buffer = new byte[HeaderLength + payload.Length];
+ BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(0, 4), buffer.Length);
+ BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(4, 2), HeaderLength);
+ BinaryPrimitives.WriteInt16BigEndian(buffer.AsSpan(6, 2), 1);
+ BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(8, 4), operation);
+ BinaryPrimitives.WriteInt32BigEndian(buffer.AsSpan(12, 4), 1);
+ payload.CopyTo(buffer.AsSpan(HeaderLength));
+ return buffer;
+ }
+
+ private static void ParsePackets(ReadOnlySpan buffer, ICollection events)
+ {
+ var offset = 0;
+ while (offset + HeaderLength <= buffer.Length)
+ {
+ var packetLength = BinaryPrimitives.ReadInt32BigEndian(buffer.Slice(offset, 4));
+ if (packetLength <= HeaderLength || offset + packetLength > buffer.Length)
+ {
+ break;
+ }
+
+ var headerLength = BinaryPrimitives.ReadInt16BigEndian(buffer.Slice(offset + 4, 2));
+ var version = BinaryPrimitives.ReadInt16BigEndian(buffer.Slice(offset + 6, 2));
+ var operation = BinaryPrimitives.ReadInt32BigEndian(buffer.Slice(offset + 8, 4));
+ var bodyOffset = offset + headerLength;
+ var bodyLength = packetLength - headerLength;
+ if (bodyLength > 0)
+ {
+ var body = buffer.Slice(bodyOffset, bodyLength);
+ if (operation == OperationMessage)
+ {
+ if (version == 2)
+ {
+ ParsePackets(DecompressZlib(body), events);
+ }
+ else if (version == 3)
+ {
+ ParsePackets(DecompressBrotli(body), events);
+ }
+ else
+ {
+ var danmakuEvent = ParseJsonEvent(body);
+ if (danmakuEvent is not null)
+ {
+ events.Add(danmakuEvent);
+ }
+ }
+ }
+ }
+
+ offset += packetLength;
+ }
+ }
+
+ private static DanmakuEvent? ParseJsonEvent(ReadOnlySpan payload)
+ {
+ var json = Encoding.UTF8.GetString(payload);
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ return null;
+ }
+
+ using var document = JsonDocument.Parse(json);
+ var root = document.RootElement;
+ var cmd = GetString(root, "cmd") ?? "UNKNOWN";
+ var normalizedCmd = cmd.Split(':', 2)[0];
+ var occurredAt = ResolveOccurredAt(root);
+
+ return normalizedCmd switch
+ {
+ "DANMU_MSG" => ParseChatEvent(root, cmd, json, occurredAt),
+ "SEND_GIFT" => ParseGiftEvent(root, cmd, json, occurredAt),
+ "GUARD_BUY" => ParseGuardEvent(root, cmd, json, occurredAt),
+ "INTERACT_WORD" => ParseInteractEvent(root, cmd, json, occurredAt),
+ "LIKE_INFO_V3_CLICK" or "LIKE_INFO_V3_UPDATE" or "LIKE_INFO_V3_NOTICE" => ParseLikeEvent(root, cmd, json, occurredAt),
+ "SUPER_CHAT_MESSAGE" => ParseSuperChatEvent(root, cmd, json, occurredAt),
+ "LIVE" or "PREPARING" or "ROOM_CHANGE" or "ROOM_BLOCK_MSG" or "WARNING" => ParseStatusEvent(normalizedCmd.ToLowerInvariant(), cmd, json, occurredAt),
+ _ => ParseOtherEvent(root, normalizedCmd.ToLowerInvariant(), cmd, json, occurredAt)
+ };
+ }
+
+ private static DanmakuEvent ParseChatEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var info = TryGetArrayIndex(root, "info", 0);
+ var content = TryGetArrayString(GetPropertyOrDefault(root, "info"), 1);
+ var userElement = TryGetArrayIndex(GetPropertyOrDefault(root, "info"), 2) ?? default;
+ var user = TryGetArrayString(userElement, 1);
+ var userId = TryGetArrayString(userElement, 0);
+
+ return new DanmakuEvent(
+ "chat",
+ user,
+ userId,
+ content,
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd), ("infoPresent", (info is not null).ToString())));
+ }
+
+ private static DanmakuEvent ParseGiftEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ var giftName = GetString(data, "giftName");
+ var count = GetString(data, "num") ?? "1";
+ var content = string.IsNullOrWhiteSpace(giftName) ? null : $"{giftName} x{count}";
+
+ return new DanmakuEvent(
+ "gift",
+ GetString(data, "uname"),
+ GetString(data, "uid"),
+ content,
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd), ("giftName", giftName), ("count", count)));
+ }
+
+ private static DanmakuEvent ParseGuardEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ return new DanmakuEvent(
+ "member",
+ GetString(data, "username"),
+ GetString(data, "uid"),
+ $"guard x{GetString(data, "num") ?? "1"}",
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd), ("guardLevel", GetString(data, "guard_level"))));
+ }
+
+ private static DanmakuEvent ParseInteractEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ var msgType = GetInt(data, "msg_type") ?? 0;
+ var content = msgType switch
+ {
+ 1 => "entered the room",
+ 2 => "followed the room",
+ 3 => "shared the room",
+ _ => "interacted"
+ };
+
+ return new DanmakuEvent(
+ msgType is 1 ? "enter" : "member",
+ GetString(data, "uname"),
+ GetString(data, "uid"),
+ content,
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd), ("msgType", msgType.ToString())));
+ }
+
+ private static DanmakuEvent ParseLikeEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ return new DanmakuEvent(
+ "like",
+ GetString(data, "uname"),
+ GetString(data, "uid"),
+ GetString(data, "like_text") ?? GetString(data, "click_count") ?? "liked",
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd)));
+ }
+
+ private static DanmakuEvent ParseSuperChatEvent(JsonElement root, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ var userInfo = GetPropertyOrDefault(data, "user_info");
+ return new DanmakuEvent(
+ "superchat",
+ GetString(userInfo, "uname"),
+ GetString(data, "uid"),
+ GetString(data, "message"),
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd), ("price", GetString(data, "price"))));
+ }
+
+ private static DanmakuEvent ParseStatusEvent(string type, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ return new DanmakuEvent(
+ type,
+ null,
+ null,
+ null,
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd)));
+ }
+
+ private static DanmakuEvent ParseOtherEvent(JsonElement root, string type, string cmd, string rawPayload, DateTimeOffset occurredAt)
+ {
+ var data = GetPropertyOrDefault(root, "data");
+ return new DanmakuEvent(
+ string.IsNullOrWhiteSpace(type) ? "other" : type,
+ GetString(data, "uname") ?? GetString(data, "username"),
+ GetString(data, "uid"),
+ GetString(data, "msg") ?? GetString(data, "giftName") ?? GetString(data, "message"),
+ occurredAt,
+ rawPayload,
+ CreateExtra(("cmd", cmd)));
+ }
+
+ private static DateTimeOffset ResolveOccurredAt(JsonElement root)
+ {
+ var candidates = new[]
+ {
+ GetInt64(root, "timestamp"),
+ GetInt64(root, "ts"),
+ GetNestedInt64(root, "data", "timestamp"),
+ GetNestedInt64(root, "data", "send_time"),
+ GetNestedInt64(root, "data", "start_time"),
+ GetNestedInt64(root, "data", "time")
+ };
+
+ foreach (var candidate in candidates)
+ {
+ if (!candidate.HasValue || candidate <= 0)
+ {
+ continue;
+ }
+
+ return candidate > 9_999_999_999
+ ? DateTimeOffset.FromUnixTimeMilliseconds(candidate.Value)
+ : DateTimeOffset.FromUnixTimeSeconds(candidate.Value);
+ }
+
+ return DateTimeOffset.UtcNow;
+ }
+
+ private static byte[] DecompressZlib(ReadOnlySpan payload)
+ {
+ using var input = payload.Length > 2 && payload[0] == 0x78
+ ? new MemoryStream(payload[2..].ToArray(), writable: false)
+ : new MemoryStream(payload.ToArray(), writable: false);
+ using var deflate = new DeflateStream(input, CompressionMode.Decompress);
+ using var output = new MemoryStream();
+ deflate.CopyTo(output);
+ return output.ToArray();
+ }
+
+ private static byte[] DecompressBrotli(ReadOnlySpan payload)
+ {
+ using var input = new MemoryStream(payload.ToArray(), writable: false);
+ using var brotli = new BrotliStream(input, CompressionMode.Decompress);
+ using var output = new MemoryStream();
+ brotli.CopyTo(output);
+ return output.ToArray();
+ }
+
+ private static IReadOnlyDictionary? CreateExtra(params (string Key, string? Value)[] pairs)
+ {
+ var dictionary = pairs
+ .Where(static pair => !string.IsNullOrWhiteSpace(pair.Value))
+ .ToDictionary(static pair => pair.Key, static pair => pair.Value!, StringComparer.OrdinalIgnoreCase);
+ return dictionary.Count == 0 ? null : dictionary;
+ }
+
+ private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
+ {
+ if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(propertyName, out value))
+ {
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ private static JsonElement GetPropertyOrDefault(JsonElement element, string propertyName) =>
+ TryGetProperty(element, propertyName, out var value) ? value : default;
+
+ private static JsonElement? TryGetArrayIndex(JsonElement element, string propertyName, int index)
+ {
+ if (!TryGetProperty(element, propertyName, out var value) || value.ValueKind != JsonValueKind.Array || value.GetArrayLength() <= index)
+ {
+ return null;
+ }
+
+ return value[index];
+ }
+
+ private static JsonElement? TryGetArrayIndex(JsonElement element, int index)
+ {
+ if (element.ValueKind != JsonValueKind.Array || element.GetArrayLength() <= index)
+ {
+ return null;
+ }
+
+ return element[index];
+ }
+
+ private static string? TryGetArrayString(JsonElement element, int index)
+ {
+ if (element.ValueKind != JsonValueKind.Array || element.GetArrayLength() <= index)
+ {
+ return null;
+ }
+
+ return ReadScalarString(element[index]);
+ }
+
+ private static string? GetString(JsonElement element, string propertyName) =>
+ TryGetProperty(element, propertyName, out var value) ? ReadScalarString(value) : null;
+
+ private static int? GetInt(JsonElement element, string propertyName)
+ {
+ if (!TryGetProperty(element, propertyName, out var value))
+ {
+ return null;
+ }
+
+ return value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var intValue)
+ ? intValue
+ : int.TryParse(ReadScalarString(value), out intValue)
+ ? intValue
+ : null;
+ }
+
+ private static long? GetInt64(JsonElement element, string propertyName)
+ {
+ if (!TryGetProperty(element, propertyName, out var value))
+ {
+ return null;
+ }
+
+ return value.ValueKind == JsonValueKind.Number && value.TryGetInt64(out var longValue)
+ ? longValue
+ : long.TryParse(ReadScalarString(value), out longValue)
+ ? longValue
+ : null;
+ }
+
+ private static long? GetNestedInt64(JsonElement element, string parentPropertyName, string propertyName)
+ {
+ if (!TryGetProperty(element, parentPropertyName, out var parent))
+ {
+ return null;
+ }
+
+ return GetInt64(parent, propertyName);
+ }
+
+ private static string? ReadScalarString(JsonElement value) =>
+ value.ValueKind switch
+ {
+ JsonValueKind.String => value.GetString(),
+ JsonValueKind.Number => value.GetRawText(),
+ JsonValueKind.True => bool.TrueString.ToLowerInvariant(),
+ JsonValueKind.False => bool.FalseString.ToLowerInvariant(),
+ _ => null
+ };
+}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuAdapter.cs b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuAdapter.cs
index ee7b727..58b298c 100644
--- a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuAdapter.cs
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuAdapter.cs
@@ -1,3 +1,6 @@
+using System.Net;
+using System.Net.WebSockets;
+using System.Text;
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Domain.Enums;
using Microsoft.Extensions.Logging;
@@ -30,6 +33,9 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
private readonly DouyinHttpClient _douyinHttpClient;
private readonly ILogger _logger;
private readonly DanmakuConnectionContext _context;
+ private readonly SemaphoreSlim _sendGate = new(1, 1);
+
+ private ClientWebSocket? _socket;
public DouyinDanmakuConnection(
DouyinHttpClient douyinHttpClient,
@@ -45,24 +51,225 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
{
ArgumentNullException.ThrowIfNull(onEvent);
- var bootstrap = await _douyinHttpClient.GetDanmakuBootstrapAsync(_context.RoomId, cancellationToken);
+ var backoff = TimeSpan.FromSeconds(1);
+ var maxBackoff = TimeSpan.FromSeconds(Math.Max(1, _context.RetryDelayMaxSeconds));
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ ClientWebSocket? socket = null;
+ CancellationTokenSource? heartbeatCancellation = null;
+ Task? heartbeatTask = null;
+
+ try
+ {
+ var bootstrap = await _douyinHttpClient.GetDanmakuBootstrapAsync(_context.RoomId, cancellationToken);
+
+ _logger.LogInformation(
+ "Douyin danmaku bootstrap resolved web room {WebRoomId} to im room {DanmakuRoomId}. UserUniqueId={UserUniqueId}; PushServer={PushServer}; InitialEvents={InitialEvents}",
+ _context.RoomId,
+ bootstrap.DanmakuRoomId,
+ bootstrap.UserUniqueId,
+ bootstrap.PushServer ?? "(bootstrap-pending)",
+ bootstrap.InitialEvents.Count);
+
+ foreach (var danmakuEvent in bootstrap.InitialEvents)
+ {
+ await onEvent(danmakuEvent);
+ }
+
+ try
+ {
+ var request = await _douyinHttpClient.GetDanmakuWebSocketRequestAsync(bootstrap, cancellationToken);
+ socket = CreateSocket(request);
+ _socket = socket;
+ await socket.ConnectAsync(request.Uri, cancellationToken);
+
+ _logger.LogInformation(
+ "Douyin danmaku websocket connected for room {WebRoomId}. Endpoint={Endpoint}",
+ _context.RoomId,
+ request.Uri);
+
+ heartbeatCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ heartbeatTask = RunHeartbeatAsync(socket, request.HeartbeatInterval, heartbeatCancellation.Token);
+
+ backoff = TimeSpan.FromSeconds(1);
+ await ReceiveLoopAsync(socket, bootstrap, onEvent, cancellationToken);
+
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ _logger.LogWarning(
+ "Douyin danmaku websocket ended unexpectedly for room {RoomId}. Falling back to HTTP polling.",
+ _context.RoomId);
+ await RunPollingLoopAsync(bootstrap, onEvent, cancellationToken);
+ }
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(
+ ex,
+ "Douyin danmaku websocket startup failed for room {RoomId}. Falling back to HTTP polling.",
+ _context.RoomId);
+ await RunPollingLoopAsync(bootstrap, onEvent, cancellationToken);
+ }
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Douyin danmaku websocket failed for room {RoomId}. Retrying.", _context.RoomId);
+ }
+ finally
+ {
+ if (heartbeatCancellation is not null)
+ {
+ heartbeatCancellation.Cancel();
+ }
+
+ if (heartbeatTask is not null)
+ {
+ try
+ {
+ await heartbeatTask;
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "Douyin danmaku heartbeat loop ended with an error for room {RoomId}.", _context.RoomId);
+ }
+ }
+
+ await CloseSocketAsync(socket, cancellationToken);
+ if (ReferenceEquals(_socket, socket))
+ {
+ _socket = null;
+ }
+
+ heartbeatCancellation?.Dispose();
+ }
+
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
+ await Task.Delay(backoff, cancellationToken);
+ backoff = TimeSpan.FromSeconds(Math.Min(maxBackoff.TotalSeconds, backoff.TotalSeconds * 2));
+ }
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ await CloseSocketAsync(_socket, CancellationToken.None);
+ _socket = null;
+ _sendGate.Dispose();
+ }
+
+ private ClientWebSocket CreateSocket(DouyinDanmakuWebSocketRequest request)
+ {
+ var socket = new ClientWebSocket();
+ socket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan;
+ socket.Options.SetRequestHeader("Origin", "https://live.douyin.com");
+ socket.Options.SetRequestHeader("User-Agent", request.UserAgent);
+ socket.Options.SetRequestHeader("Referer", request.Referer);
+
+ var cookies = BuildCookieContainer(request.CookieHeader);
+ if (cookies.Count > 0)
+ {
+ socket.Options.Cookies = cookies;
+ }
+
+ return socket;
+ }
+
+ private async Task ReceiveLoopAsync(
+ ClientWebSocket socket,
+ DouyinDanmakuBootstrap bootstrap,
+ Func onEvent,
+ CancellationToken cancellationToken)
+ {
+ string? cursor = bootstrap.Cursor;
+ string? internalExt = bootstrap.InternalExt;
+
+ while (!cancellationToken.IsCancellationRequested &&
+ socket.State is WebSocketState.Open or WebSocketState.CloseReceived)
+ {
+ var (messageType, payload) = await ReceiveMessageAsync(socket, cancellationToken);
+ if (messageType == WebSocketMessageType.Close)
+ {
+ _logger.LogWarning(
+ "Douyin danmaku websocket closed by remote endpoint for room {RoomId}. State={State}",
+ _context.RoomId,
+ socket.State);
+ return;
+ }
+
+ if (messageType == WebSocketMessageType.Text)
+ {
+ _logger.LogDebug(
+ "Douyin danmaku websocket returned text payload for room {RoomId}: {Payload}",
+ _context.RoomId,
+ Encoding.UTF8.GetString(payload));
+ continue;
+ }
+
+ var envelope = DouyinDanmakuProtocol.Parse(payload);
+ if (!string.IsNullOrWhiteSpace(envelope.Cursor))
+ {
+ cursor = envelope.Cursor;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.InternalExt))
+ {
+ internalExt = envelope.InternalExt;
+ }
+
+ if (envelope.NeedAck)
+ {
+ await SendFrameAsync(
+ socket,
+ DouyinDanmakuProtocol.CreateAckFrame(internalExt, envelope.LogId),
+ cancellationToken);
+ }
+
+ foreach (var danmakuEvent in envelope.Events)
+ {
+ await onEvent(danmakuEvent);
+ }
+
+ if (envelope.Events.Count == 0 &&
+ string.Equals(envelope.PayloadType, "msg", StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogDebug(
+ "Douyin danmaku websocket received an empty message frame for room {RoomId}. CursorPresent={HasCursor}; InternalExtPresent={HasInternalExt}",
+ _context.RoomId,
+ !string.IsNullOrWhiteSpace(cursor),
+ !string.IsNullOrWhiteSpace(internalExt));
+ }
+ }
+ }
+
+ private async Task RunPollingLoopAsync(
+ DouyinDanmakuBootstrap bootstrap,
+ Func onEvent,
+ CancellationToken cancellationToken)
+ {
var danmakuRoomId = bootstrap.DanmakuRoomId;
var cursor = bootstrap.Cursor ?? string.Empty;
var internalExt = bootstrap.InternalExt ?? string.Empty;
- var userUniqueId = bootstrap.UserUniqueId ?? throw new InvalidOperationException("Douyin danmaku bootstrap did not provide a user unique id.");
+ var userUniqueId = bootstrap.UserUniqueId ?? throw new InvalidOperationException("Douyin danmaku polling fallback requires a resolved user unique id.");
var backoff = TimeSpan.FromSeconds(1);
var minPollInterval = TimeSpan.FromMilliseconds(Math.Max(100, _context.MinPollIntervalMilliseconds));
var maxBackoff = TimeSpan.FromSeconds(Math.Max(1, _context.RetryDelayMaxSeconds));
var consecutiveEmptyPolls = 0;
- _logger.LogInformation(
- "Douyin danmaku bootstrap resolved web room {WebRoomId} to im room {DanmakuRoomId}. CursorPresent={HasCursor}; InternalExtPresent={HasInternalExt}; UserUniqueId={UserUniqueId}",
- _context.RoomId,
- danmakuRoomId,
- !string.IsNullOrWhiteSpace(cursor),
- !string.IsNullOrWhiteSpace(internalExt),
- userUniqueId);
-
while (!cancellationToken.IsCancellationRequested)
{
try
@@ -75,8 +282,15 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
cancellationToken);
var envelope = DouyinDanmakuProtocol.Parse(bytes);
- cursor = envelope.Cursor ?? cursor;
- internalExt = envelope.InternalExt ?? internalExt;
+ if (!string.IsNullOrWhiteSpace(envelope.Cursor))
+ {
+ cursor = envelope.Cursor;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.InternalExt))
+ {
+ internalExt = envelope.InternalExt;
+ }
foreach (var danmakuEvent in envelope.Events)
{
@@ -103,7 +317,7 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
if (!string.Equals(refreshedBootstrap.DanmakuRoomId, danmakuRoomId, StringComparison.Ordinal))
{
_logger.LogInformation(
- "Douyin danmaku bootstrap refreshed im room id from {OldDanmakuRoomId} to {NewDanmakuRoomId} for web room {WebRoomId}.",
+ "Douyin danmaku polling refreshed im room id from {OldDanmakuRoomId} to {NewDanmakuRoomId} for web room {WebRoomId}.",
danmakuRoomId,
refreshedBootstrap.DanmakuRoomId,
_context.RoomId);
@@ -119,6 +333,11 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
{
internalExt = refreshedBootstrap.InternalExt;
}
+
+ if (!string.IsNullOrWhiteSpace(refreshedBootstrap.UserUniqueId))
+ {
+ userUniqueId = refreshedBootstrap.UserUniqueId;
+ }
}
}
else
@@ -136,12 +355,132 @@ internal sealed class DouyinDanmakuConnection : ILiveDanmakuConnection
}
catch (Exception ex)
{
- _logger.LogWarning(ex, "Douyin danmaku polling failed for room {RoomId}. Retrying.", _context.RoomId);
+ _logger.LogWarning(ex, "Douyin danmaku polling fallback failed for room {RoomId}. Retrying.", _context.RoomId);
await Task.Delay(backoff, cancellationToken);
backoff = TimeSpan.FromSeconds(Math.Min(maxBackoff.TotalSeconds, backoff.TotalSeconds * 2));
}
}
}
- public ValueTask DisposeAsync() => ValueTask.CompletedTask;
+ private async Task RunHeartbeatAsync(ClientWebSocket socket, TimeSpan heartbeatInterval, CancellationToken cancellationToken)
+ {
+ var effectiveInterval = heartbeatInterval <= TimeSpan.Zero
+ ? TimeSpan.FromSeconds(10)
+ : heartbeatInterval < TimeSpan.FromSeconds(10)
+ ? TimeSpan.FromSeconds(10)
+ : heartbeatInterval;
+
+ while (!cancellationToken.IsCancellationRequested &&
+ socket.State == WebSocketState.Open)
+ {
+ await Task.Delay(effectiveInterval, cancellationToken);
+ if (cancellationToken.IsCancellationRequested || socket.State != WebSocketState.Open)
+ {
+ break;
+ }
+
+ await SendFrameAsync(socket, DouyinDanmakuProtocol.CreateHeartbeatFrame(), cancellationToken);
+ }
+ }
+
+ private async Task SendFrameAsync(ClientWebSocket socket, byte[] payload, CancellationToken cancellationToken)
+ {
+ await _sendGate.WaitAsync(cancellationToken);
+ try
+ {
+ if (socket.State != WebSocketState.Open)
+ {
+ return;
+ }
+
+ await socket.SendAsync(
+ new ArraySegment(payload),
+ WebSocketMessageType.Binary,
+ true,
+ cancellationToken);
+ }
+ finally
+ {
+ _sendGate.Release();
+ }
+ }
+
+ private static async Task<(WebSocketMessageType MessageType, byte[] Payload)> ReceiveMessageAsync(
+ ClientWebSocket socket,
+ CancellationToken cancellationToken)
+ {
+ var buffer = new byte[8192];
+ using var output = new MemoryStream();
+
+ while (true)
+ {
+ var result = await socket.ReceiveAsync(new ArraySegment(buffer), cancellationToken);
+ if (result.MessageType == WebSocketMessageType.Close)
+ {
+ return (WebSocketMessageType.Close, Array.Empty());
+ }
+
+ if (result.Count > 0)
+ {
+ output.Write(buffer, 0, result.Count);
+ }
+
+ if (result.EndOfMessage)
+ {
+ return (result.MessageType, output.ToArray());
+ }
+ }
+ }
+
+ private static CookieContainer BuildCookieContainer(string? cookieHeader)
+ {
+ var container = new CookieContainer();
+ if (string.IsNullOrWhiteSpace(cookieHeader))
+ {
+ return container;
+ }
+
+ foreach (var segment in cookieHeader.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
+ {
+ var separatorIndex = segment.IndexOf('=');
+ if (separatorIndex <= 0)
+ {
+ continue;
+ }
+
+ var name = segment[..separatorIndex].Trim();
+ var value = segment[(separatorIndex + 1)..].Trim();
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ continue;
+ }
+
+ container.Add(new Cookie(name, value, "/", ".douyin.com"));
+ }
+
+ return container;
+ }
+
+ private static async Task CloseSocketAsync(ClientWebSocket? socket, CancellationToken cancellationToken)
+ {
+ if (socket is null)
+ {
+ return;
+ }
+
+ try
+ {
+ if (socket.State is WebSocketState.Open or WebSocketState.CloseReceived)
+ {
+ await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "disposed", cancellationToken);
+ }
+ }
+ catch
+ {
+ }
+ finally
+ {
+ socket.Dispose();
+ }
+ }
}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuProtocol.cs b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuProtocol.cs
index d469828..cee78d5 100644
--- a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuProtocol.cs
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Danmaku/DouyinDanmakuProtocol.cs
@@ -1,4 +1,5 @@
using System.IO.Compression;
+using System.Linq;
using System.Text;
using LiveRecorder.Application.Abstractions.Platforms;
@@ -10,23 +11,58 @@ internal static class DouyinDanmakuProtocol
{
if (payload.Length == 0)
{
- return new DouyinDanmakuEnvelope([], null, null, TimeSpan.FromSeconds(1));
+ return CreateEmptyEnvelope();
}
- var responseBytes = payload.AsSpan();
- var pushFrame = TryParsePushFrame(responseBytes);
- if (pushFrame.Payload.Length > 0)
+ var candidates = new List();
+ AddCandidate(candidates, payload);
+ DouyinPushFrame? parsedPushFrame = null;
+
+ if (TryGunzip(payload, out var gunzippedPayload))
{
- responseBytes = pushFrame.Payload;
+ AddCandidate(candidates, gunzippedPayload);
}
- if (LooksLikeGzip(responseBytes))
+ if (TryParsePushFrame(payload, out var pushFrame) &&
+ pushFrame.Payload.Length > 0)
{
- responseBytes = Decompress(responseBytes);
+ parsedPushFrame = pushFrame;
+ AddCandidate(candidates, pushFrame.Payload);
+
+ if (pushFrame.IsGzipEncoded && TryGunzip(pushFrame.Payload, out var gunzippedFramePayload))
+ {
+ AddCandidate(candidates, gunzippedFramePayload);
+ }
}
- var response = ParseResponse(responseBytes);
- return response;
+ DouyinDanmakuEnvelope? bestEnvelope = null;
+ foreach (var candidate in candidates)
+ {
+ if (!TryParseResponse(candidate, out var envelope))
+ {
+ continue;
+ }
+
+ envelope = ApplyPushFrameMetadata(envelope, parsedPushFrame);
+
+ if (bestEnvelope is null ||
+ envelope.Events.Count > bestEnvelope.Events.Count ||
+ (envelope.Events.Count == bestEnvelope.Events.Count &&
+ GetEnvelopeScore(envelope) > GetEnvelopeScore(bestEnvelope)))
+ {
+ bestEnvelope = envelope;
+ }
+
+ if (envelope.Events.Count > 0)
+ {
+ return envelope;
+ }
+ }
+
+ return bestEnvelope ??
+ (parsedPushFrame is { } metadataFrame
+ ? CreateEmptyEnvelope(metadataFrame.Cursor, metadataFrame.InternalExt, metadataFrame.LogId, metadataFrame.PayloadType)
+ : CreateEmptyEnvelope());
}
private static DouyinPushFrame ParsePushFrame(ReadOnlySpan data)
@@ -34,6 +70,8 @@ internal static class DouyinDanmakuProtocol
var payload = ReadOnlySpan.Empty;
var payloadEncoding = string.Empty;
var payloadType = string.Empty;
+ string? logId = null;
+ var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
var index = 0;
while (index < data.Length)
@@ -44,13 +82,24 @@ internal static class DouyinDanmakuProtocol
switch (fieldNumber)
{
+ case 2 when wireType == 0:
+ logId = ReadVarint(data, ref index).ToString();
+ break;
case 5 when wireType == 2:
var headerBytes = ReadLengthDelimited(data, ref index);
var (key, value) = ParseHeader(headerBytes);
- if (string.Equals(key, "compress_type", StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrWhiteSpace(key))
{
- payloadEncoding = value;
+ headers[key] = value;
+ if (string.Equals(key, "compress_type", StringComparison.OrdinalIgnoreCase))
+ {
+ payloadEncoding = value;
+ }
}
+
+ break;
+ case 6 when wireType == 2:
+ payloadEncoding = ReadString(data, ref index);
break;
case 7 when wireType == 2:
payloadType = ReadString(data, ref index);
@@ -69,21 +118,46 @@ internal static class DouyinDanmakuProtocol
payloadEncoding.Contains("gzip", StringComparison.OrdinalIgnoreCase) ||
payloadType.Contains("response", StringComparison.OrdinalIgnoreCase)))
{
- payload = Decompress(payload);
+ payload = TryGunzip(payload, out var decompressedPayload) ? decompressedPayload : payload.ToArray();
}
- return new DouyinPushFrame(payload.ToArray());
+ headers.TryGetValue("im-cursor", out var cursor);
+ headers.TryGetValue("im-internal_ext", out var internalExt);
+
+ return new DouyinPushFrame(
+ payload.ToArray(),
+ payloadEncoding.Contains("gzip", StringComparison.OrdinalIgnoreCase) || LooksLikeGzip(payload),
+ logId,
+ payloadType,
+ cursor,
+ internalExt);
}
- private static DouyinPushFrame TryParsePushFrame(ReadOnlySpan data)
+ private static bool TryParsePushFrame(ReadOnlySpan data, out DouyinPushFrame pushFrame)
{
try
{
- return ParsePushFrame(data);
+ pushFrame = ParsePushFrame(data);
+ return true;
}
catch
{
- return new DouyinPushFrame(Array.Empty());
+ pushFrame = default;
+ return false;
+ }
+ }
+
+ private static bool TryParseResponse(byte[] data, out DouyinDanmakuEnvelope envelope)
+ {
+ try
+ {
+ envelope = ParseResponse(data);
+ return true;
+ }
+ catch
+ {
+ envelope = default!;
+ return false;
}
}
@@ -92,7 +166,11 @@ internal static class DouyinDanmakuProtocol
var events = new List();
string? cursor = null;
string? internalExt = null;
+ string? pushServer = null;
+ string? liveCursor = null;
TimeSpan pollInterval = TimeSpan.FromSeconds(1);
+ TimeSpan heartbeatDuration = TimeSpan.Zero;
+ var needAck = false;
var index = 0;
while (index < data.Length)
@@ -118,18 +196,70 @@ internal static class DouyinDanmakuProtocol
internalExt = Encoding.UTF8.GetString(ReadLengthDelimited(data, ref index));
break;
case 3 when wireType == 0:
- case 8 when wireType == 0:
pollInterval = TimeSpan.FromMilliseconds(Math.Max(500, (long)ReadVarint(data, ref index)));
break;
+ case 8 when wireType == 0:
+ heartbeatDuration = TimeSpan.FromMilliseconds(Math.Max(0, (long)ReadVarint(data, ref index)));
+ break;
+ case 9 when wireType == 0:
+ needAck = ReadVarint(data, ref index) != 0;
+ break;
+ case 10 when wireType == 2:
+ pushServer = Encoding.UTF8.GetString(ReadLengthDelimited(data, ref index));
+ break;
+ case 11 when wireType == 2:
+ liveCursor = Encoding.UTF8.GetString(ReadLengthDelimited(data, ref index));
+ break;
default:
SkipField(data, ref index, wireType);
break;
}
}
- return new DouyinDanmakuEnvelope(events, cursor, internalExt, pollInterval);
+ return new DouyinDanmakuEnvelope(
+ events,
+ cursor,
+ internalExt,
+ pollInterval,
+ needAck,
+ heartbeatDuration,
+ pushServer,
+ liveCursor,
+ null,
+ null);
}
+ private static int GetEnvelopeScore(DouyinDanmakuEnvelope envelope)
+ {
+ var score = 0;
+ if (!string.IsNullOrWhiteSpace(envelope.Cursor))
+ {
+ score += 2;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.InternalExt))
+ {
+ score += 2;
+ }
+
+ if (envelope.PollInterval > TimeSpan.Zero)
+ {
+ score += 1;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.PushServer))
+ {
+ score += 1;
+ }
+
+ return score;
+ }
+
+ public static byte[] CreateHeartbeatFrame() => CreatePushFrame("hb");
+
+ public static byte[] CreateAckFrame(string? internalExt, string? logId) =>
+ CreatePushFrame("ack", string.IsNullOrEmpty(internalExt) ? Array.Empty() : Encoding.UTF8.GetBytes(internalExt), logId);
+
private static DanmakuEvent? ParseEnvelopeMessage(ReadOnlySpan data)
{
string? method = null;
@@ -461,20 +591,131 @@ internal static class DouyinDanmakuProtocol
private static bool LooksLikeGzip(ReadOnlySpan data) =>
data.Length >= 2 && data[0] == 0x1F && data[1] == 0x8B;
- private static byte[] Decompress(ReadOnlySpan data)
+ private static bool TryGunzip(ReadOnlySpan data, out byte[] decompressed)
{
- using var input = new MemoryStream(data.ToArray());
- using var gzip = new GZipStream(input, CompressionMode.Decompress);
- using var output = new MemoryStream();
- gzip.CopyTo(output);
- return output.ToArray();
+ try
+ {
+ using var input = new MemoryStream(data.ToArray());
+ using var gzip = new GZipStream(input, CompressionMode.Decompress);
+ using var output = new MemoryStream();
+ gzip.CopyTo(output);
+ decompressed = output.ToArray();
+ return true;
+ }
+ catch
+ {
+ decompressed = Array.Empty();
+ return false;
+ }
+ }
+
+ private static void AddCandidate(ICollection candidates, byte[] candidate)
+ {
+ if (candidate.Length == 0)
+ {
+ return;
+ }
+
+ if (candidates.Any(existing => existing.AsSpan().SequenceEqual(candidate)))
+ {
+ return;
+ }
+
+ candidates.Add(candidate);
+ }
+
+ private static DouyinDanmakuEnvelope ApplyPushFrameMetadata(
+ DouyinDanmakuEnvelope envelope,
+ DouyinPushFrame? pushFrame)
+ {
+ if (pushFrame is not { } frame)
+ {
+ return envelope;
+ }
+
+ return envelope with
+ {
+ Cursor = string.IsNullOrWhiteSpace(frame.Cursor) ? envelope.Cursor : frame.Cursor,
+ InternalExt = string.IsNullOrWhiteSpace(frame.InternalExt) ? envelope.InternalExt : frame.InternalExt,
+ LogId = frame.LogId,
+ PayloadType = frame.PayloadType
+ };
+ }
+
+ private static DouyinDanmakuEnvelope CreateEmptyEnvelope(
+ string? cursor = null,
+ string? internalExt = null,
+ string? logId = null,
+ string? payloadType = null) =>
+ new([], cursor, internalExt, TimeSpan.FromSeconds(1), false, TimeSpan.Zero, null, null, logId, payloadType);
+
+ private static byte[] CreatePushFrame(string payloadType, byte[]? payload = null, string? logId = null)
+ {
+ var buffer = new List(64);
+ if (ulong.TryParse(logId, out var parsedLogId))
+ {
+ WriteVarintField(buffer, 2, parsedLogId);
+ }
+
+ WriteStringField(buffer, 7, payloadType);
+ if (payload is { Length: > 0 })
+ {
+ WriteBytesField(buffer, 8, payload);
+ }
+
+ return buffer.ToArray();
+ }
+
+ private static void WriteVarintField(ICollection buffer, int fieldNumber, ulong value)
+ {
+ WriteVarint(buffer, (ulong)((fieldNumber << 3) | 0));
+ WriteVarint(buffer, value);
+ }
+
+ private static void WriteStringField(ICollection buffer, int fieldNumber, string value)
+ {
+ var bytes = Encoding.UTF8.GetBytes(value);
+ WriteBytesField(buffer, fieldNumber, bytes);
+ }
+
+ private static void WriteBytesField(ICollection buffer, int fieldNumber, byte[] value)
+ {
+ WriteVarint(buffer, (ulong)((fieldNumber << 3) | 2));
+ WriteVarint(buffer, (ulong)value.Length);
+ foreach (var item in value)
+ {
+ buffer.Add(item);
+ }
+ }
+
+ private static void WriteVarint(ICollection buffer, ulong value)
+ {
+ while (value >= 0x80)
+ {
+ buffer.Add((byte)((value & 0x7F) | 0x80));
+ value >>= 7;
+ }
+
+ buffer.Add((byte)value);
}
internal sealed record DouyinDanmakuEnvelope(
IReadOnlyList Events,
string? Cursor,
string? InternalExt,
- TimeSpan PollInterval);
+ TimeSpan PollInterval,
+ bool NeedAck,
+ TimeSpan HeartbeatDuration,
+ string? PushServer,
+ string? LiveCursor,
+ string? LogId,
+ string? PayloadType);
- private readonly record struct DouyinPushFrame(byte[] Payload);
+ private readonly record struct DouyinPushFrame(
+ byte[] Payload,
+ bool IsGzipEncoded,
+ string? LogId,
+ string? PayloadType,
+ string? Cursor,
+ string? InternalExt);
}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinHttpClient.cs b/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinHttpClient.cs
index 9ae6aff..2246dfd 100644
--- a/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinHttpClient.cs
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinHttpClient.cs
@@ -1,17 +1,22 @@
using System.Net;
+using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Application.Abstractions.Settings;
using LiveRecorder.Application.Models.Settings;
+using LiveRecorder.Infrastructure.Platforms.Douyin.Danmaku;
using LiveRecorder.Infrastructure.Platforms.Douyin.Signing;
using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace LiveRecorder.Infrastructure.Platforms.Douyin;
public sealed class DouyinHttpClient
{
+ private const string DefaultClientName = "douyin";
+ private const string DirectClientName = "douyin-direct";
private const int MaxAttempts = 3;
private const int MsTokenLength = 184;
private static readonly Regex PaceStateRegex = new(
@@ -28,6 +33,26 @@ public sealed class DouyinHttpClient
"\"roomStore\":{[\\s\\S]*?\"roomInfo\":{[\\s\\S]*?\"roomId\":\"(?\\d{8,})\"",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
+ private static readonly Regex HtmlUserUniqueIdRegex = new(
+ "\"userStore\":{[\\s\\S]*?\"odin\":{[\\s\\S]*?\"user_unique_id\":\"(?\\d{8,})\"",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex HtmlStateRoomIdRegex = new(
+ "{\"state\":{[\\s\\S]*?\"roomStore\":{[\\s\\S]*?\"roomInfo\":{[\\s\\S]*?\"roomId\":\"(?[0-9]+?)\"",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex HtmlStateUniqueIdRegex = new(
+ "{\"state\":{[\\s\\S]*?\"userStore\":{[\\s\\S]*?\"odin\":{[\\s\\S]*?\"user_unique_id\":\"(?[0-9]+?)\"",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex GenericRoomIdFieldRegex = new(
+ "\"(?:roomId|room_id|id_str|id)\":\"(?\\d{8,})\"",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex GenericUserUniqueIdFieldRegex = new(
+ "\"(?:user_unique_id|userUniqueId|uniqueId)\":\"(?\\d{8,})\"",
+ RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
private static readonly Regex HtmlTitleRegex = new(
"\"roomStore\":{[\\s\\S]*?\"roomInfo\":{[\\s\\S]*?\"room\":{[\\s\\S]*?\"title\":\"(?[\\s\\S]*?)\"",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
@@ -37,31 +62,34 @@ public sealed class DouyinHttpClient
RegexOptions.Compiled | RegexOptions.CultureInvariant);
private readonly IHttpClientFactory _httpClientFactory;
- private readonly ISystemSettingsService _systemSettingsService;
+ private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly DouyinXBogusSigner _xBogusSigner;
+ private readonly DouyinLiveWsSignatureSigner _liveWsSignatureSigner;
private readonly ILogger _logger;
public DouyinHttpClient(
IHttpClientFactory httpClientFactory,
- ISystemSettingsService systemSettingsService,
+ IServiceScopeFactory serviceScopeFactory,
DouyinXBogusSigner xBogusSigner,
+ DouyinLiveWsSignatureSigner liveWsSignatureSigner,
ILogger logger)
{
_httpClientFactory = httpClientFactory;
- _systemSettingsService = systemSettingsService;
+ _serviceScopeFactory = serviceScopeFactory;
_xBogusSigner = xBogusSigner;
+ _liveWsSignatureSigner = liveWsSignatureSigner;
_logger = logger;
}
public async Task<(Uri? FinalUri, string Body)> ResolvePageAsync(string input, CancellationToken cancellationToken = default)
{
- var settings = await _systemSettingsService.GetAsync(cancellationToken);
+ var settings = await GetSystemSettingsAsync(cancellationToken);
var cookieHeader = await BuildCookieHeaderAsync(settings, cancellationToken);
using var response = await SendWithRetryAsync(
() =>
{
var request = new HttpRequestMessage(HttpMethod.Get, input);
- ApplyDefaultHeaders(request, settings, refererRoomId: null);
+ ApplyHtmlPageHeaders(request, settings, refererRoomId: null);
if (!string.IsNullOrWhiteSpace(cookieHeader))
{
@@ -79,7 +107,7 @@ public sealed class DouyinHttpClient
public async Task GetRoomEnterAsync(string roomId, CancellationToken cancellationToken = default)
{
- var settings = await _systemSettingsService.GetAsync(cancellationToken);
+ var settings = await GetSystemSettingsAsync(cancellationToken);
var cookieHeader = await BuildCookieHeaderAsync(settings, cancellationToken);
using var response = await SendWithRetryAsync(
() =>
@@ -103,7 +131,7 @@ public sealed class DouyinHttpClient
public async Task GetStreamInputHeadersAsync(string roomId, CancellationToken cancellationToken = default)
{
- var settings = await _systemSettingsService.GetAsync(cancellationToken);
+ var settings = await GetSystemSettingsAsync(cancellationToken);
var cookieHeader = await BuildCookieHeaderAsync(settings, cancellationToken);
var additionalHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
@@ -124,8 +152,8 @@ public sealed class DouyinHttpClient
string? internalExt,
CancellationToken cancellationToken = default)
{
- var settings = await _systemSettingsService.GetAsync(cancellationToken);
- var cookieHeader = await BuildCookieHeaderAsync(settings, cancellationToken);
+ var settings = await GetSystemSettingsAsync(cancellationToken);
+ var cookieHeader = await BuildLiveImCookieHeaderAsync(settings, roomId, cancellationToken);
var requestUri = await BuildSignedLiveImUriAsync(
roomId,
userUniqueId,
@@ -163,30 +191,141 @@ public sealed class DouyinHttpClient
string webRoomId,
CancellationToken cancellationToken = default)
{
- var (_, pageHtml) = await ResolvePageAsync($"https://live.douyin.com/{webRoomId}", cancellationToken);
+ var pageHtml = await ResolveDanmakuPageHtmlAsync(webRoomId, cancellationToken);
using var document = await GetRoomEnterAsync(webRoomId, cancellationToken);
var root = document.RootElement;
var room = GetRoomNode(root);
+ var pageInfo = TryParseLivePageInfo(pageHtml);
+ if (pageInfo is null)
+ {
+ _logger.LogWarning(
+ "Douyin live page parsing did not resolve internal room identifiers for web room {WebRoomId}. Falling back to secondary candidate extraction.",
+ webRoomId);
+ }
- var danmakuRoomId =
- TryExtractDanmakuRoomIdFromHtml(pageHtml) ??
- ReadNumericLikeString(room, "id_str") ??
- ReadNumericLikeString(room, "id") ??
- ReadNumericLikeString(room, "room_id") ??
- ReadNumericLikeString(room, "roomId") ??
- FindNumericLikeValue(room, "id_str", "room_id", "roomId", "id") ??
- webRoomId;
+ var fallbackLivePageInfo = TryExtractAnchorAndTitleFromHtml(pageHtml);
+ var anchor = pageInfo?.Anchor ?? fallbackLivePageInfo.Anchor;
+ var title = pageInfo?.Title ?? fallbackLivePageInfo.Title;
- var (anchor, title) = TryExtractAnchorAndTitleFromHtml(pageHtml);
+ var roomCandidates = new List();
+ var userUniqueIdCandidates = new List();
+
+ AddCandidate(roomCandidates, pageInfo?.RoomId);
+ AddCandidates(roomCandidates, BuildDanmakuRoomIdCandidates(webRoomId, root, room, pageHtml));
+ AddCandidate(userUniqueIdCandidates, pageInfo?.UserUniqueId);
+ AddCandidates(userUniqueIdCandidates, BuildUserUniqueIdCandidates(root, room, pageHtml));
+
+ if (roomCandidates.Count == 0)
+ {
+ roomCandidates.Add(webRoomId);
+ }
+
+ if (userUniqueIdCandidates.Count == 0)
+ {
+ userUniqueIdCandidates.Add(CreateAnonymousUserUniqueId());
+ }
+
+ var selectedRoomId = roomCandidates[0];
+ var selectedUserUniqueId = userUniqueIdCandidates[0];
+ string? cursor = null;
+ string? internalExt = null;
+ string? pushServer = null;
+ TimeSpan heartbeatInterval = TimeSpan.Zero;
+ IReadOnlyList initialEvents = [];
+ var bestScore = int.MinValue;
+ Exception? lastWarmupException = null;
+
+ foreach (var roomCandidate in roomCandidates.Take(4))
+ {
+ foreach (var userUniqueIdCandidate in userUniqueIdCandidates.Take(4))
+ {
+ try
+ {
+ var initialPayload = await GetLiveImAsync(
+ roomCandidate,
+ userUniqueIdCandidate,
+ cursor: null,
+ internalExt: null,
+ cancellationToken: cancellationToken);
+ var envelope = DouyinDanmakuProtocol.Parse(initialPayload);
+ var score = ScoreBootstrapEnvelope(envelope, roomCandidate, userUniqueIdCandidate, webRoomId);
+ if (score > bestScore)
+ {
+ bestScore = score;
+ selectedRoomId = roomCandidate;
+ selectedUserUniqueId = userUniqueIdCandidate;
+ cursor = NormalizeOptional(envelope.Cursor);
+ internalExt = NormalizeOptional(envelope.InternalExt);
+ pushServer = NormalizeOptional(envelope.PushServer);
+ heartbeatInterval = envelope.HeartbeatDuration;
+ initialEvents = envelope.Events;
+ }
+
+ if (ShouldAcceptBootstrapEnvelope(envelope, roomCandidate, webRoomId))
+ {
+ goto BootstrapResolved;
+ }
+ }
+ catch (Exception ex)
+ {
+ lastWarmupException = ex;
+ }
+ }
+ }
+
+BootstrapResolved:
+ if (bestScore == int.MinValue && lastWarmupException is not null)
+ {
+ _logger.LogDebug(lastWarmupException, "Douyin danmaku bootstrap warm-up failed for room {WebRoomId}.", webRoomId);
+ }
return new DouyinDanmakuBootstrap(
webRoomId,
- danmakuRoomId,
- null,
- null,
- CreateAnonymousUserUniqueId(),
+ selectedRoomId,
+ cursor,
+ internalExt,
+ selectedUserUniqueId,
anchor,
- title);
+ title,
+ pushServer,
+ heartbeatInterval,
+ initialEvents);
+ }
+
+ public async Task GetDanmakuWebSocketRequestAsync(
+ DouyinDanmakuBootstrap bootstrap,
+ CancellationToken cancellationToken = default)
+ {
+ var userUniqueId = NormalizeOptional(bootstrap.UserUniqueId);
+ if (string.IsNullOrWhiteSpace(userUniqueId))
+ {
+ throw new InvalidOperationException("Douyin danmaku bootstrap did not resolve a user unique id for websocket connection.");
+ }
+
+ var settings = await GetSystemSettingsAsync(cancellationToken);
+ var cookieHeader = await BuildLiveImCookieHeaderAsync(settings, bootstrap.WebRoomId, cancellationToken);
+ var signature = await _liveWsSignatureSigner.SignAsync(bootstrap.DanmakuRoomId, userUniqueId, cancellationToken);
+ var requestUri = BuildDanmakuWebSocketUri(
+ bootstrap,
+ signature,
+ settings.DouyinUserAgent);
+ var referer = string.IsNullOrWhiteSpace(bootstrap.WebRoomId)
+ ? settings.DouyinReferer
+ : $"https://live.douyin.com/{bootstrap.WebRoomId}";
+
+ return new DouyinDanmakuWebSocketRequest(
+ new Uri(requestUri),
+ settings.DouyinUserAgent,
+ referer,
+ string.IsNullOrWhiteSpace(cookieHeader) ? null : cookieHeader,
+ bootstrap.HeartbeatInterval > TimeSpan.Zero ? bootstrap.HeartbeatInterval : TimeSpan.FromSeconds(10));
+ }
+
+ private async Task GetSystemSettingsAsync(CancellationToken cancellationToken)
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var systemSettingsService = scope.ServiceProvider.GetRequiredService();
+ return await systemSettingsService.GetAsync(cancellationToken);
}
private async Task BuildCookieHeaderAsync(SystemSettingsDto settings, CancellationToken cancellationToken)
@@ -212,6 +351,122 @@ public sealed class DouyinHttpClient
return $"{configuredCookie.TrimEnd(';')}; {ttwidCookie}";
}
+ private async Task BuildLiveImCookieHeaderAsync(
+ SystemSettingsDto settings,
+ string roomId,
+ CancellationToken cancellationToken)
+ {
+ var baseCookieHeader = await BuildCookieHeaderAsync(settings, cancellationToken);
+ var supplementalCookieHeader = await FetchSupplementalCookieHeaderAsync(
+ settings,
+ baseCookieHeader,
+ roomId,
+ cancellationToken);
+ return MergeCookieHeaders(baseCookieHeader, supplementalCookieHeader);
+ }
+
+ private async Task FetchSupplementalCookieHeaderAsync(
+ SystemSettingsDto settings,
+ string existingCookieHeader,
+ string? refererRoomId,
+ CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var response = await SendWithRetryAsync(
+ () =>
+ {
+ var request = new HttpRequestMessage(HttpMethod.Head, "https://live.douyin.com/webcast/user/");
+ ApplyDefaultHeaders(request, settings, refererRoomId);
+ request.Headers.TryAddWithoutValidation("X-Secsdk-Csrf-Request", "1");
+ request.Headers.TryAddWithoutValidation("X-Secsdk-Csrf-Version", "1.2.22");
+
+ if (!string.IsNullOrWhiteSpace(existingCookieHeader))
+ {
+ request.Headers.TryAddWithoutValidation("Cookie", existingCookieHeader);
+ }
+
+ return request;
+ },
+ HttpCompletionOption.ResponseHeadersRead,
+ cancellationToken);
+
+ return ExtractCookieHeader(response);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "Fetch supplemental Douyin cookies failed.");
+ return null;
+ }
+ }
+
+ private async Task ResolveDanmakuPageHtmlAsync(string webRoomId, CancellationToken cancellationToken)
+ {
+ var (_, firstPageHtml) = await ResolvePageAsync($"https://live.douyin.com/{webRoomId}", cancellationToken);
+ if (!string.IsNullOrWhiteSpace(TryExtractDanmakuRoomIdFromHtml(firstPageHtml)) &&
+ !string.IsNullOrWhiteSpace(TryExtractUserUniqueIdFromHtml(firstPageHtml)))
+ {
+ return firstPageHtml;
+ }
+
+ var (_, secondPageHtml) = await ResolvePageAsync($"https://live.douyin.com/{webRoomId}", cancellationToken);
+ return string.IsNullOrWhiteSpace(secondPageHtml) ? firstPageHtml : secondPageHtml;
+ }
+
+ private static string? ExtractCookieHeader(HttpResponseMessage response)
+ {
+ if (!response.Headers.TryGetValues("Set-Cookie", out var setCookieHeaders))
+ {
+ return null;
+ }
+
+ var cookiePairs = setCookieHeaders
+ .Select(static header =>
+ header.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).FirstOrDefault())
+ .Where(static header => !string.IsNullOrWhiteSpace(header))
+ .ToArray();
+
+ return cookiePairs.Length == 0 ? null : MergeCookieHeaders(cookiePairs);
+ }
+
+ private static string MergeCookieHeaders(params string?[] cookieHeaders)
+ {
+ var cookies = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var cookieHeader in cookieHeaders)
+ {
+ if (string.IsNullOrWhiteSpace(cookieHeader))
+ {
+ continue;
+ }
+
+ foreach (var segment in cookieHeader.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
+ {
+ var separatorIndex = segment.IndexOf('=');
+ if (separatorIndex <= 0)
+ {
+ continue;
+ }
+
+ var name = segment[..separatorIndex].Trim();
+ if (IsCookieAttribute(name))
+ {
+ continue;
+ }
+
+ cookies[name] = segment[(separatorIndex + 1)..].Trim();
+ }
+ }
+
+ return string.Join("; ", cookies.Select(static item => $"{item.Key}={item.Value}"));
+ }
+
+ private static bool IsCookieAttribute(string name) =>
+ name.Equals("domain", StringComparison.OrdinalIgnoreCase) ||
+ name.Equals("path", StringComparison.OrdinalIgnoreCase) ||
+ name.Equals("expires", StringComparison.OrdinalIgnoreCase) ||
+ name.Equals("max-age", StringComparison.OrdinalIgnoreCase) ||
+ name.Equals("samesite", StringComparison.OrdinalIgnoreCase);
+
private async Task FetchTtwidCookieAsync(SystemSettingsDto settings, CancellationToken cancellationToken)
{
try
@@ -220,7 +475,7 @@ public sealed class DouyinHttpClient
() =>
{
var request = new HttpRequestMessage(HttpMethod.Get, settings.DouyinReferer);
- ApplyDefaultHeaders(request, settings, refererRoomId: null);
+ ApplyHtmlPageHeaders(request, settings, refererRoomId: null);
if (!string.IsNullOrWhiteSpace(settings.DouyinCookie))
{
@@ -262,17 +517,26 @@ public sealed class DouyinHttpClient
HttpCompletionOption completionOption,
CancellationToken cancellationToken)
{
- var client = _httpClientFactory.CreateClient("douyin");
+ var preferDirectConnection = false;
for (var attempt = 1; attempt <= MaxAttempts; attempt++)
{
using var request = requestFactory();
+ var useDirectConnection = preferDirectConnection;
+ var client = _httpClientFactory.CreateClient(useDirectConnection ? DirectClientName : DefaultClientName);
try
{
var response = await client.SendAsync(request, completionOption, cancellationToken);
if (response.IsSuccessStatusCode)
{
+ if (useDirectConnection)
+ {
+ _logger.LogInformation(
+ "Douyin request recovered through direct connection fallback on attempt {Attempt}.",
+ attempt);
+ }
+
return response;
}
@@ -288,13 +552,29 @@ public sealed class DouyinHttpClient
}
catch (Exception ex) when (attempt < MaxAttempts && IsTransientTransportException(ex, cancellationToken))
{
- _logger.LogWarning(ex, "Douyin request transient failure on attempt {Attempt}. Retrying.", attempt);
+ if (!preferDirectConnection)
+ {
+ preferDirectConnection = true;
+ _logger.LogWarning(
+ ex,
+ "Douyin request transient failure on attempt {Attempt}. Retrying via direct connection fallback.",
+ attempt);
+ }
+ else
+ {
+ _logger.LogWarning(
+ ex,
+ "Douyin request transient failure on attempt {Attempt} while using direct connection fallback. Retrying.",
+ attempt);
+ }
+
await DelayForRetryAsync(attempt, cancellationToken);
}
}
using var lastRequest = requestFactory();
- return await client.SendAsync(lastRequest, completionOption, cancellationToken);
+ var finalClient = _httpClientFactory.CreateClient(preferDirectConnection ? DirectClientName : DefaultClientName);
+ return await finalClient.SendAsync(lastRequest, completionOption, cancellationToken);
}
private static void ApplyDefaultHeaders(HttpRequestMessage request, SystemSettingsDto settings, string? refererRoomId)
@@ -311,6 +591,19 @@ public sealed class DouyinHttpClient
: $"https://live.douyin.com/{refererRoomId}");
}
+ private static void ApplyHtmlPageHeaders(HttpRequestMessage request, SystemSettingsDto settings, string? refererRoomId)
+ {
+ request.Version = HttpVersion.Version11;
+ request.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
+ request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8");
+ request.Headers.ConnectionClose = true;
+ request.Headers.UserAgent.ParseAdd(settings.DouyinUserAgent);
+ request.Headers.Referrer = new Uri(
+ string.IsNullOrWhiteSpace(refererRoomId)
+ ? settings.DouyinReferer
+ : $"https://live.douyin.com/{refererRoomId}");
+ }
+
private static void ApplyLiveImHeaders(HttpRequestMessage request, SystemSettingsDto settings, string roomId)
{
ApplyDefaultHeaders(request, settings, roomId);
@@ -348,58 +641,206 @@ public sealed class DouyinHttpClient
string userAgent,
CancellationToken cancellationToken)
{
- var parameters = BuildLiveImParameters(roomId, userUniqueId, cursor, internalExt, userAgent);
- var unencodedQuery = string.Join("&", parameters.Select(p => $"{p.Key}={p.Value}"));
+ var signingParameters = BuildLiveImSigningParameters(roomId, userUniqueId, cursor, internalExt, userAgent);
+ var unencodedQuery = BuildUnencodedQueryString(signingParameters);
var signedValue = await _xBogusSigner.SignAsync(unencodedQuery, userAgent, cancellationToken);
- parameters["a_bogus"] = signedValue;
- return QueryHelpers.AddQueryString("https://live.douyin.com/webcast/im/fetch/", parameters);
+ var requestParameters = BuildLiveImRequestParameters(signingParameters, signedValue);
+ return BuildQueryString("https://live.douyin.com/webcast/im/fetch/", requestParameters);
}
- private static Dictionary BuildLiveImParameters(
+ private static string BuildDanmakuWebSocketUri(
+ DouyinDanmakuBootstrap bootstrap,
+ string signature,
+ string userAgent)
+ {
+ var pushServerHost = ResolveDanmakuPushServerHost(bootstrap.PushServer);
+ var parameters = BuildDanmakuWebSocketParameters(bootstrap, signature, userAgent);
+ return BuildQueryString($"wss://{pushServerHost}/webcast/im/push/v2/", parameters);
+ }
+
+ private static List> BuildLiveImSigningParameters(
string roomId,
string userUniqueId,
string? cursor,
string? internalExt,
string userAgent)
{
- return new Dictionary
+ var browserVersion = GetBrowserVersionParameter(userAgent);
+ return
+ [
+ new("aid", "6383"),
+ new("app_name", "douyin_web"),
+ new("browser_language", "zh-CN"),
+ new("browser_name", "Mozilla"),
+ new("browser_online", "true"),
+ new("browser_platform", "Win32"),
+ new("browser_version", browserVersion),
+ new("cookie_enabled", "true"),
+ new("cursor", cursor ?? string.Empty),
+ new("device_id", string.Empty),
+ new("device_platform", "web"),
+ new("did_rule", "3"),
+ new("endpoint", "live_pc"),
+ new("fetch_rule", "1"),
+ new("identity", "audience"),
+ new("insert_task_id", string.Empty),
+ new("internal_ext", internalExt ?? string.Empty),
+ new("last_rtt", "0"),
+ new("live_id", "1"),
+ new("live_reason", string.Empty),
+ new("need_persist_msg_count", "15"),
+ new("resp_content_type", "protobuf"),
+ new("screen_height", "1080"),
+ new("screen_width", "1920"),
+ new("support_wrds", "1"),
+ new("tz_name", "Asia/Shanghai"),
+ new("version_code", "180800"),
+ new("room_id", roomId),
+ new("user_unique_id", userUniqueId),
+ new("live_pc", roomId)
+ ];
+ }
+
+ private static List> BuildLiveImRequestParameters(
+ IEnumerable> signingParameters,
+ string signedValue)
+ {
+ var requestParameters = new List>
{
- ["aid"] = "6383",
- ["app_name"] = "douyin_web",
- ["browser_language"] = "zh-CN",
- ["browser_name"] = "Mozilla",
- ["browser_online"] = "true",
- ["browser_platform"] = "Win32",
- ["browser_version"] = string.IsNullOrWhiteSpace(userAgent) ? "5.0" : userAgent,
- ["compress"] = "gzip",
- ["cookie_enabled"] = "true",
- ["cursor"] = cursor ?? string.Empty,
- ["device_id"] = string.Empty,
- ["device_platform"] = "web",
- ["did_rule"] = "3",
- ["endpoint"] = "live_pc",
- ["fetch_rule"] = "1",
- ["host"] = "https://live.douyin.com",
- ["identity"] = "audience",
- ["insert_task_id"] = string.Empty,
- ["internal_ext"] = internalExt ?? string.Empty,
- ["last_rtt"] = "0",
- ["live_id"] = "1",
- ["live_pc"] = roomId,
- ["live_reason"] = string.Empty,
- ["msToken"] = CreateMsToken(),
- ["need_persist_msg_count"] = "15",
- ["room_id"] = roomId,
- ["room_id_str"] = roomId,
- ["resp_content_type"] = "protobuf",
- ["screen_height"] = "1080",
- ["screen_width"] = "1920",
- ["support_wrds"] = "1",
- ["tz_name"] = "Asia/Shanghai",
- ["user_unique_id"] = userUniqueId,
- ["version_code"] = "180800",
- ["webcast_sdk_version"] = "1.0.15"
+ new("aid", "6383"),
+ new("app_name", "douyin_web"),
+ new("browser_language", "zh-CN"),
+ new("browser_name", "Mozilla"),
+ new("browser_online", "true"),
+ new("browser_platform", "Win32"),
+ new("browser_version", signingParameters.FirstOrDefault(item => item.Key == "browser_version").Value ?? "5.0"),
+ new("cookie_enabled", "true"),
+ new("cursor", signingParameters.FirstOrDefault(item => item.Key == "cursor").Value ?? string.Empty),
+ new("device_id", string.Empty),
+ new("device_platform", "web"),
+ new("did_rule", "3"),
+ new("endpoint", "live_pc"),
+ new("fetch_rule", "1"),
+ new("identity", "audience"),
+ new("insert_task_id", string.Empty),
+ new("internal_ext", signingParameters.FirstOrDefault(item => item.Key == "internal_ext").Value ?? string.Empty),
+ new("last_rtt", "0"),
+ new("live_id", "1"),
+ new("live_reason", string.Empty),
+ new("need_persist_msg_count", "15"),
+ new("resp_content_type", "protobuf"),
+ new("screen_height", "1080"),
+ new("screen_width", "1920"),
+ new("support_wrds", "1"),
+ new("tz_name", "Asia/Shanghai"),
+ new("version_code", "180800"),
+ new("msToken", CreateMsToken()),
+ new("room_id", signingParameters.FirstOrDefault(item => item.Key == "room_id").Value ?? string.Empty),
+ new("user_unique_id", signingParameters.FirstOrDefault(item => item.Key == "user_unique_id").Value ?? string.Empty),
+ new("live_pc", signingParameters.FirstOrDefault(item => item.Key == "live_pc").Value ?? string.Empty),
+ new("a_bogus", signedValue)
};
+
+ return requestParameters;
+ }
+
+ private static List> BuildDanmakuWebSocketParameters(
+ DouyinDanmakuBootstrap bootstrap,
+ string signature,
+ string userAgent)
+ {
+ var browserVersion = GetBrowserVersionParameter(userAgent);
+ return
+ [
+ new("aid", "6383"),
+ new("app_name", "douyin_web"),
+ new("browser_language", "zh-CN"),
+ new("browser_name", "Mozilla"),
+ new("browser_online", "true"),
+ new("browser_platform", "Win32"),
+ new("browser_version", browserVersion),
+ new("compress", "gzip"),
+ new("cookie_enabled", "true"),
+ new("cursor", bootstrap.Cursor ?? string.Empty),
+ new("device_platform", "web"),
+ new("did_rule", "3"),
+ new("endpoint", "live_pc"),
+ new("heartbeatDuration", "0"),
+ new("host", "https://live.douyin.com"),
+ new("identity", "audience"),
+ new("im_path", "/webcast/im/fetch/"),
+ new("insert_task_id", string.Empty),
+ new("internal_ext", bootstrap.InternalExt ?? string.Empty),
+ new("live_id", "1"),
+ new("live_reason", string.Empty),
+ new("need_persist_msg_count", "15"),
+ new("room_id", bootstrap.DanmakuRoomId),
+ new("screen_height", "1080"),
+ new("screen_width", "1920"),
+ new("signature", signature),
+ new("support_wrds", "1"),
+ new("tz_name", "Asia/Shanghai"),
+ new("update_version_code", DouyinLiveWsSignatureSigner.WebcastSdkVersion),
+ new("user_unique_id", bootstrap.UserUniqueId),
+ new("version_code", "180800"),
+ new("webcast_sdk_version", DouyinLiveWsSignatureSigner.WebcastSdkVersion)
+ ];
+ }
+
+ private static string BuildUnencodedQueryString(IEnumerable> parameters) =>
+ string.Join("&", parameters.Select(item => $"{item.Key}={item.Value ?? string.Empty}"));
+
+ private static string BuildQueryString(string baseUrl, IEnumerable> parameters)
+ {
+ var encodedParameters = string.Join(
+ "&",
+ parameters.Select(item =>
+ $"{Uri.EscapeDataString(item.Key)}={Uri.EscapeDataString(item.Value ?? string.Empty)}"));
+
+ return string.IsNullOrWhiteSpace(encodedParameters)
+ ? baseUrl
+ : $"{baseUrl}?{encodedParameters}";
+ }
+
+ private static string GetBrowserVersionParameter(string userAgent)
+ {
+ if (string.IsNullOrWhiteSpace(userAgent))
+ {
+ return "5.0";
+ }
+
+ return userAgent.StartsWith("Mozilla/", StringComparison.OrdinalIgnoreCase)
+ ? userAgent["Mozilla/".Length..]
+ : userAgent;
+ }
+
+ private static string ResolveDanmakuPushServerHost(string? pushServer)
+ {
+ if (string.IsNullOrWhiteSpace(pushServer))
+ {
+ return "webcast100-ws-web-lq.douyin.com";
+ }
+
+ var normalized = pushServer.Trim();
+ if (Uri.TryCreate(normalized, UriKind.Absolute, out var absoluteUri))
+ {
+ return absoluteUri.Authority;
+ }
+
+ normalized = normalized
+ .Replace("wss://", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace("ws://", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace("https://", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Replace("http://", string.Empty, StringComparison.OrdinalIgnoreCase)
+ .Trim('/');
+
+ var slashIndex = normalized.IndexOf('/');
+ if (slashIndex >= 0)
+ {
+ normalized = normalized[..slashIndex];
+ }
+
+ return string.IsNullOrWhiteSpace(normalized) ? "webcast100-ws-web-lq.douyin.com" : normalized;
}
private static bool IsTransientStatusCode(HttpStatusCode statusCode) =>
@@ -512,6 +953,22 @@ public sealed class DouyinHttpClient
return null;
}
+ private static IReadOnlyList FindNumericLikeValues(JsonElement element, params string[] propertyNames)
+ {
+ var values = new List();
+ foreach (var propertyName in propertyNames)
+ {
+ CollectScalarStringsByPropertyName(element, propertyName, values);
+ }
+
+ return values
+ .Select(NormalizeNumericLikeString)
+ .Where(static value => !string.IsNullOrWhiteSpace(value))
+ .Cast()
+ .Distinct(StringComparer.Ordinal)
+ .ToList();
+ }
+
private static string? FindScalarString(JsonElement element, params string[] propertyNames)
{
foreach (var propertyName in propertyNames)
@@ -563,6 +1020,33 @@ public sealed class DouyinHttpClient
return null;
}
+ private static void CollectScalarStringsByPropertyName(JsonElement element, string propertyName, ICollection values)
+ {
+ if (element.ValueKind == JsonValueKind.Object)
+ {
+ foreach (var property in element.EnumerateObject())
+ {
+ if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
+ {
+ var scalar = ReadScalarString(property.Value);
+ if (!string.IsNullOrWhiteSpace(scalar))
+ {
+ values.Add(scalar);
+ }
+ }
+
+ CollectScalarStringsByPropertyName(property.Value, propertyName, values);
+ }
+ }
+ else if (element.ValueKind == JsonValueKind.Array)
+ {
+ foreach (var item in element.EnumerateArray())
+ {
+ CollectScalarStringsByPropertyName(item, propertyName, values);
+ }
+ }
+ }
+
private static string? ReadScalarString(JsonElement value)
{
return value.ValueKind switch
@@ -613,54 +1097,119 @@ public sealed class DouyinHttpClient
return new string(buffer);
}
+ private static string? TryExtractNormalizedStatePayload(string? html)
+ {
+ if (string.IsNullOrWhiteSpace(html))
+ {
+ return null;
+ }
+
+ var stateMatch = PaceStateRegex.Match(html);
+ if (!stateMatch.Success)
+ {
+ return null;
+ }
+
+ var statePayload = stateMatch.Groups["state"].Value;
+ return string.IsNullOrWhiteSpace(statePayload) ? null : Regex.Unescape(statePayload);
+ }
+
+ private static DouyinLivePageInfo? TryParseLivePageInfo(string? html)
+ {
+ var normalized = TryExtractNormalizedStatePayload(html);
+ if (string.IsNullOrWhiteSpace(normalized))
+ {
+ return null;
+ }
+
+ normalized = EscapedQuoteRegex.Replace(normalized, "\"");
+
+ var roomId = MatchGroupValue(HtmlStateRoomIdRegex, normalized, "id");
+ var userUniqueId = MatchGroupValue(HtmlStateUniqueIdRegex, normalized, "id");
+ if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(userUniqueId))
+ {
+ return null;
+ }
+
+ var anchor = MatchGroupValue(HtmlAnchorRegex, normalized, "nickname");
+ var title = MatchGroupValue(HtmlTitleRegex, normalized, "title");
+
+ return new DouyinLivePageInfo(
+ roomId,
+ userUniqueId,
+ DecodeUnicodeUrl(anchor),
+ DecodeUnicodeUrl(title));
+ }
+
private static string? TryExtractDanmakuRoomIdFromHtml(string html)
{
- if (string.IsNullOrWhiteSpace(html))
+ var pageInfo = TryParseLivePageInfo(html);
+ if (!string.IsNullOrWhiteSpace(pageInfo?.RoomId))
{
- return null;
+ return pageInfo.RoomId;
}
- var stateMatch = PaceStateRegex.Match(html);
- if (!stateMatch.Success)
+ foreach (var text in EnumerateCandidateHtmlPayloads(html))
{
- return null;
+ var roomIdMatch = HtmlRoomIdNewRegex.Match(text);
+ if (!roomIdMatch.Success)
+ {
+ roomIdMatch = HtmlRoomIdRegex.Match(text);
+ }
+
+ if (!roomIdMatch.Success)
+ {
+ roomIdMatch = GenericRoomIdFieldRegex.Match(text);
+ }
+
+ if (roomIdMatch.Success)
+ {
+ return roomIdMatch.Groups["id"].Value;
+ }
}
- var statePayload = stateMatch.Groups["state"].Value;
- if (string.IsNullOrWhiteSpace(statePayload))
- {
- return null;
- }
-
- var normalized = Regex.Unescape(statePayload);
- var roomIdMatch = HtmlRoomIdNewRegex.Match(normalized);
- if (!roomIdMatch.Success)
- {
- roomIdMatch = HtmlRoomIdRegex.Match(normalized);
- }
- return roomIdMatch.Success ? roomIdMatch.Groups["id"].Value : null;
+ return null;
}
+
+ private static string? TryExtractUserUniqueIdFromHtml(string? html)
+ {
+ var pageInfo = TryParseLivePageInfo(html);
+ if (!string.IsNullOrWhiteSpace(pageInfo?.UserUniqueId))
+ {
+ return pageInfo.UserUniqueId;
+ }
+
+ foreach (var text in EnumerateCandidateHtmlPayloads(html))
+ {
+ var match = HtmlUserUniqueIdRegex.Match(text);
+ if (!match.Success)
+ {
+ match = GenericUserUniqueIdFieldRegex.Match(text);
+ }
+
+ if (match.Success)
+ {
+ return match.Groups["id"].Value;
+ }
+ }
+
+ return null;
+ }
+
private static (string? Anchor, string? Title) TryExtractAnchorAndTitleFromHtml(string? html)
{
- if (string.IsNullOrWhiteSpace(html))
+ var pageInfo = TryParseLivePageInfo(html);
+ if (!string.IsNullOrWhiteSpace(pageInfo?.Anchor) || !string.IsNullOrWhiteSpace(pageInfo?.Title))
+ {
+ return (pageInfo.Anchor, pageInfo.Title);
+ }
+
+ var normalized = TryExtractNormalizedStatePayload(html);
+ if (string.IsNullOrWhiteSpace(normalized))
{
return (null, null);
}
- var stateMatch = PaceStateRegex.Match(html);
- if (!stateMatch.Success)
- {
- return (null, null);
- }
-
- var statePayload = stateMatch.Groups["state"].Value;
- if (string.IsNullOrWhiteSpace(statePayload))
- {
- return (null, null);
- }
-
- var normalized = Regex.Unescape(statePayload);
-
var anchorMatch = HtmlAnchorRegex.Match(normalized);
var titleMatch = HtmlTitleRegex.Match(normalized);
@@ -669,8 +1218,171 @@ public sealed class DouyinHttpClient
titleMatch.Success ? Regex.Unescape(titleMatch.Groups["title"].Value) : null
);
}
+
+ private static string? MatchGroupValue(Regex regex, string input, string groupName)
+ {
+ var match = regex.Match(input);
+ return match.Success ? match.Groups[groupName].Value : null;
+ }
+
+ private static string? DecodeUnicodeUrl(string? value) =>
+ string.IsNullOrWhiteSpace(value) ? value : value.Replace("\\u0026", "&");
+
+ private static IEnumerable EnumerateCandidateHtmlPayloads(string? html)
+ {
+ if (string.IsNullOrWhiteSpace(html))
+ {
+ yield break;
+ }
+
+ var normalized = TryExtractNormalizedStatePayload(html);
+ if (!string.IsNullOrWhiteSpace(normalized))
+ {
+ yield return normalized;
+ }
+
+ var unescaped = TryUnescapeHtmlPayload(html);
+ if (!string.IsNullOrWhiteSpace(unescaped))
+ {
+ yield return unescaped;
+ }
+
+ yield return html;
+ }
+
+ private static string? TryUnescapeHtmlPayload(string html)
+ {
+ try
+ {
+ return Regex.Unescape(html);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static List BuildDanmakuRoomIdCandidates(string webRoomId, JsonElement root, JsonElement room, string pageHtml)
+ {
+ var candidates = new List();
+ AddCandidate(candidates, TryExtractDanmakuRoomIdFromHtml(pageHtml));
+ AddCandidates(candidates, FindNumericLikeValues(root, "roomId", "room_id", "id_str", "liveRoomId", "live_room_id"));
+ AddCandidates(candidates, FindNumericLikeValues(room, "roomId", "room_id", "id_str", "liveRoomId", "live_room_id", "id"));
+ AddCandidate(candidates, webRoomId);
+
+ return candidates
+ .Where(static candidate => !string.IsNullOrWhiteSpace(candidate))
+ .Distinct(StringComparer.Ordinal)
+ .OrderByDescending(candidate => !string.Equals(candidate, webRoomId, StringComparison.Ordinal))
+ .ThenByDescending(static candidate => candidate.Length)
+ .ToList();
+ }
+
+ private static List BuildUserUniqueIdCandidates(JsonElement root, JsonElement room, string pageHtml)
+ {
+ var anonymousUserUniqueId = CreateAnonymousUserUniqueId();
+ var candidates = new List();
+ AddCandidate(candidates, TryExtractUserUniqueIdFromHtml(pageHtml));
+ AddCandidates(candidates, FindNumericLikeValues(root, "user_unique_id", "userUniqueId", "uniqueId"));
+ AddCandidates(candidates, FindNumericLikeValues(room, "user_unique_id", "userUniqueId", "uniqueId"));
+ AddCandidate(candidates, anonymousUserUniqueId);
+
+ return candidates
+ .Where(static candidate => !string.IsNullOrWhiteSpace(candidate))
+ .Distinct(StringComparer.Ordinal)
+ .OrderByDescending(candidate => !string.Equals(candidate, anonymousUserUniqueId, StringComparison.Ordinal))
+ .ThenByDescending(static candidate => candidate.Length)
+ .ToList();
+ }
+
+ private static int ScoreBootstrapEnvelope(
+ DouyinDanmakuProtocol.DouyinDanmakuEnvelope envelope,
+ string roomIdCandidate,
+ string userUniqueIdCandidate,
+ string webRoomId)
+ {
+ var score = 0;
+ if (!string.Equals(roomIdCandidate, webRoomId, StringComparison.Ordinal))
+ {
+ score += 24;
+ }
+
+ score += envelope.Events.Count * 12;
+ score += envelope.Events.Count(static item => string.Equals(item.Type, "chat", StringComparison.OrdinalIgnoreCase)) * 100;
+ score += envelope.Events.Count(static item => !string.Equals(item.Type, "other", StringComparison.OrdinalIgnoreCase)) * 20;
+
+ if (!string.IsNullOrWhiteSpace(envelope.Cursor))
+ {
+ score += 4;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.InternalExt))
+ {
+ score += 4;
+ }
+
+ if (!string.IsNullOrWhiteSpace(envelope.PushServer))
+ {
+ score += 8;
+ }
+
+ if (envelope.HeartbeatDuration > TimeSpan.Zero)
+ {
+ score += 2;
+ }
+
+ if (userUniqueIdCandidate.Length >= 19)
+ {
+ score += 2;
+ }
+
+ return score;
+ }
+
+ private static bool ShouldAcceptBootstrapEnvelope(
+ DouyinDanmakuProtocol.DouyinDanmakuEnvelope envelope,
+ string roomIdCandidate,
+ string webRoomId)
+ {
+ if (envelope.Events.Any(static item => string.Equals(item.Type, "chat", StringComparison.OrdinalIgnoreCase)))
+ {
+ return true;
+ }
+
+ if (envelope.Events.Any(static item => !string.Equals(item.Type, "other", StringComparison.OrdinalIgnoreCase)))
+ {
+ return true;
+ }
+
+ return envelope.Events.Count >= 3 &&
+ !string.Equals(roomIdCandidate, webRoomId, StringComparison.Ordinal);
+ }
+
+ private static void AddCandidate(ICollection candidates, string? candidate)
+ {
+ if (string.IsNullOrWhiteSpace(candidate))
+ {
+ return;
+ }
+
+ candidates.Add(candidate.Trim());
+ }
+
+ private static void AddCandidates(ICollection candidates, IEnumerable values)
+ {
+ foreach (var value in values)
+ {
+ AddCandidate(candidates, value);
+ }
+ }
}
+internal sealed record DouyinLivePageInfo(
+ string RoomId,
+ string UserUniqueId,
+ string? Anchor,
+ string? Title);
+
public sealed record DouyinDanmakuBootstrap(
string WebRoomId,
string DanmakuRoomId,
@@ -678,4 +1390,14 @@ public sealed record DouyinDanmakuBootstrap(
string? InternalExt,
string? UserUniqueId,
string? Anchor,
- string? Title);
+ string? Title,
+ string? PushServer,
+ TimeSpan HeartbeatInterval,
+ IReadOnlyList InitialEvents);
+
+public sealed record DouyinDanmakuWebSocketRequest(
+ Uri Uri,
+ string UserAgent,
+ string Referer,
+ string? CookieHeader,
+ TimeSpan HeartbeatInterval);
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinLivePlatformAdapter.cs b/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinLivePlatformAdapter.cs
index 1aae5bd..7c85022 100644
--- a/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinLivePlatformAdapter.cs
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/DouyinLivePlatformAdapter.cs
@@ -98,10 +98,12 @@ public sealed class DouyinLivePlatformAdapter : ILivePlatformAdapter
var anchorName = anchorMatch ?? GetNestedString(room, "owner", "nickname")
?? GetNestedString(room, "anchor", "nickname")
?? GetNestedString(room, "user", "nickname");
+ var anchorId = GetAnchorId(room);
+ var avatarUrl = GetAvatarUrl(room);
var coverUrl = GetCoverUrl(room);
var isLive = statusCode == 2 || GetInt(room, "live_status") is 1 or 2;
- return new LiveStatusSnapshot(isLive, title, anchorName, coverUrl, statusCode, statusCode?.ToString());
+ return new LiveStatusSnapshot(isLive, title, anchorName, anchorId, avatarUrl, coverUrl, statusCode, statusCode?.ToString());
}
public async Task GetStreamUrlAsync(
@@ -137,7 +139,8 @@ public sealed class DouyinLivePlatformAdapter : ILivePlatformAdapter
selected.Protocol,
selected.Url,
inputHeaders,
- ordered);
+ ordered,
+ InferVideoCodec(selected));
}
private static string? ExtractRoomId(string? text)
@@ -298,6 +301,25 @@ public sealed class DouyinLivePlatformAdapter : ILivePlatformAdapter
return options[0];
}
+ private static string InferVideoCodec(StreamQualityOption selected)
+ {
+ if (selected.Url.Contains("h265", StringComparison.OrdinalIgnoreCase) ||
+ selected.Url.Contains("hevc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "hevc";
+ }
+
+ if (selected.Url.Contains("h264", StringComparison.OrdinalIgnoreCase) ||
+ selected.Url.Contains("avc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "h264";
+ }
+
+ // Douyin's default web FLV/HLS pull stream is normally AVC/H.264. Keep this
+ // explicit so TS recording can safely convert AVC configuration to Annex B.
+ return "h264";
+ }
+
private static int GetQualityRank(string quality)
{
if (quality.Equals("origin", StringComparison.OrdinalIgnoreCase))
@@ -412,6 +434,73 @@ public sealed class DouyinLivePlatformAdapter : ILivePlatformAdapter
return null;
}
+
+ private static string? GetAvatarUrl(JsonElement room)
+ {
+ return GetImageUrl(room, "owner", "avatar_thumb")
+ ?? GetImageUrl(room, "owner", "avatar_medium")
+ ?? GetImageUrl(room, "owner", "avatar_large")
+ ?? GetImageUrl(room, "anchor", "avatar_thumb")
+ ?? GetImageUrl(room, "anchor", "avatar_medium")
+ ?? GetImageUrl(room, "anchor", "avatar_large")
+ ?? GetImageUrl(room, "user", "avatar_thumb")
+ ?? GetImageUrl(room, "user", "avatar_medium")
+ ?? GetImageUrl(room, "user", "avatar_large");
+ }
+
+ private static string? GetAnchorId(JsonElement room)
+ {
+ return GetNestedScalarString(room, "owner", "id_str")
+ ?? GetNestedScalarString(room, "owner", "web_rid")
+ ?? GetNestedScalarString(room, "owner", "sec_uid")
+ ?? GetNestedScalarString(room, "owner", "id")
+ ?? GetNestedScalarString(room, "anchor", "id_str")
+ ?? GetNestedScalarString(room, "anchor", "web_rid")
+ ?? GetNestedScalarString(room, "anchor", "sec_uid")
+ ?? GetNestedScalarString(room, "anchor", "id")
+ ?? GetNestedScalarString(room, "user", "id_str")
+ ?? GetNestedScalarString(room, "user", "sec_uid")
+ ?? GetNestedScalarString(room, "user", "id");
+ }
+
+ private static string? GetImageUrl(JsonElement element, params string[] path)
+ {
+ if (!TryGetNested(element, out var value, path))
+ {
+ return null;
+ }
+
+ if (value.ValueKind == JsonValueKind.String)
+ {
+ return value.GetString();
+ }
+
+ if (value.ValueKind == JsonValueKind.Object &&
+ TryGetProperty(value, "url_list", out var urlList) &&
+ urlList.ValueKind == JsonValueKind.Array &&
+ urlList.GetArrayLength() > 0 &&
+ urlList[0].ValueKind == JsonValueKind.String)
+ {
+ return urlList[0].GetString();
+ }
+
+ return null;
+ }
+
+ private static string? GetNestedScalarString(JsonElement element, params string[] path)
+ {
+ if (!TryGetNested(element, out var value, path))
+ {
+ return null;
+ }
+
+ return value.ValueKind switch
+ {
+ JsonValueKind.String => value.GetString(),
+ JsonValueKind.Number => value.GetRawText(),
+ _ => null
+ };
+ }
}
file static class RegexExtensions
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/DouyinLiveWsSignatureSigner.cs b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/DouyinLiveWsSignatureSigner.cs
new file mode 100644
index 0000000..8b77639
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/DouyinLiveWsSignatureSigner.cs
@@ -0,0 +1,82 @@
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Text;
+using Microsoft.Extensions.Logging;
+
+namespace LiveRecorder.Infrastructure.Platforms.Douyin.Signing;
+
+public sealed class DouyinLiveWsSignatureSigner
+{
+ public const string WebcastSdkVersion = "1.0.15";
+
+ private readonly ILogger _logger;
+ private readonly string _signerScriptPath;
+
+ public DouyinLiveWsSignatureSigner(ILogger logger)
+ {
+ _logger = logger;
+ _signerScriptPath = Path.Combine(AppContext.BaseDirectory, "Platforms", "Douyin", "Signing", "sign-livews.js");
+ }
+
+ public async Task SignAsync(string roomId, string userUniqueId, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrWhiteSpace(roomId))
+ {
+ throw new ArgumentException("Room id cannot be empty.", nameof(roomId));
+ }
+
+ if (string.IsNullOrWhiteSpace(userUniqueId))
+ {
+ throw new ArgumentException("User unique id cannot be empty.", nameof(userUniqueId));
+ }
+
+ if (!File.Exists(_signerScriptPath))
+ {
+ throw new FileNotFoundException("Douyin live websocket signer script is missing.", _signerScriptPath);
+ }
+
+ var stub = CreateMsStub(roomId, userUniqueId);
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = "node",
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ };
+ startInfo.ArgumentList.Add(_signerScriptPath);
+ startInfo.ArgumentList.Add(stub);
+
+ using var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true };
+ process.Start();
+
+ var standardOutputTask = process.StandardOutput.ReadToEndAsync(cancellationToken);
+ var standardErrorTask = process.StandardError.ReadToEndAsync(cancellationToken);
+ await process.WaitForExitAsync(cancellationToken);
+
+ var standardOutput = (await standardOutputTask).Trim();
+ var standardError = (await standardErrorTask).Trim();
+
+ if (process.ExitCode != 0 || string.IsNullOrWhiteSpace(standardOutput))
+ {
+ _logger.LogWarning(
+ "Douyin live websocket signing failed. ExitCode={ExitCode}; Error={Error}",
+ process.ExitCode,
+ standardError);
+ throw new InvalidOperationException(
+ string.IsNullOrWhiteSpace(standardError)
+ ? $"Douyin live websocket signer exited with code {process.ExitCode}."
+ : $"Douyin live websocket signer failed: {standardError}");
+ }
+
+ return standardOutput;
+ }
+
+ private static string CreateMsStub(string roomId, string userUniqueId)
+ {
+ var stubSource =
+ $"live_id=1,aid=6383,version_code=180800,webcast_sdk_version={WebcastSdkVersion},room_id={roomId},sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id={userUniqueId},device_platform=web,device_type=,ac=,identity=audience";
+ var hash = MD5.HashData(Encoding.UTF8.GetBytes(stubSource));
+ return Convert.ToHexString(hash).ToLowerInvariant();
+ }
+}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/mssdk.js b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/mssdk.js
new file mode 100644
index 0000000..a4589d4
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/mssdk.js
@@ -0,0 +1,2 @@
+/** 1.0.0.53 */
+;if (!window.byted_acrawler) {function w_0x25f3(_0x545d0a,_0xb73ac6){var _0x4173a9=w_0x42f5();return w_0x25f3=function(_0x138003,_0x35a375){_0x138003=_0x138003-(-0x1*-0xd15+-0x1044+0x48d);var _0x484578=_0x4173a9[_0x138003];return _0x484578;},w_0x25f3(_0x545d0a,_0xb73ac6);}(function(_0x2afeae,_0x11644f){var _0x34f31d=w_0x25f3,_0x222a7c=_0x2afeae();while(!![]){try{var _0x5a962=-parseInt(_0x34f31d(0x31b))/(-0x1c*0xac+0x22*0x121+0x1*-0x1391)*(-parseInt(_0x34f31d(0x26a))/(0xa27*-0x1+0x683*-0x5+0x4*0xaae))+parseInt(_0x34f31d(0x296))/(-0x18c1+0xa9a+0x715*0x2)+-parseInt(_0x34f31d(0x1f3))/(0x446*0x4+0x649+-0x175d)*(parseInt(_0x34f31d(0x31c))/(0x3*-0xbbf+0xf67*0x1+-0x12b*-0x11))+parseInt(_0x34f31d(0x347))/(-0x102f*0x2+0x9e*0x19+0x10f6)*(-parseInt(_0x34f31d(0x28f))/(-0x1*0x1817+-0x1*-0x184d+-0x2f))+-parseInt(_0x34f31d(0x241))/(0x779*-0x1+-0x1e1d+0x259e)*(-parseInt(_0x34f31d(0x2b9))/(-0x435+-0x8c9*0x2+0x1*0x15d0))+-parseInt(_0x34f31d(0x295))/(0x246e+0x1*-0x1c79+0x7eb*-0x1)*(-parseInt(_0x34f31d(0x291))/(-0x1b67+-0x3c3*-0x3+0x1029))+parseInt(_0x34f31d(0x307))/(0x1c3d+-0xaa0+0x3*-0x5db)*(-parseInt(_0x34f31d(0x39d))/(-0x18de+-0x3*-0x6f7+0x406));if(_0x5a962===_0x11644f)break;else _0x222a7c['push'](_0x222a7c['shift']());}catch(_0x4c4ab0){_0x222a7c['push'](_0x222a7c['shift']());}}}(w_0x42f5,-0x131739+0x156382+-0x7*-0x144df));function w_0x42f5(){var _0x458f30=['\x20can\x27t\x20have\x20a\x20.','484e4f4a403f5243001f3009ad9ffc90000000dc0b1204fb00000477110001033f2e17000135491102004a120000110001110001031a2747000503414500201100010334274700050347450012110001033e2747000603041d45000303111d184301421101021400020211000211000103182c43010211000211000103122c43011802110002110001030c2c4301180211000211000103062c43011802110002110001430118421100011401010211010311000103022c430142110101031c2b11000103042d2f1400021100011401010211010311000243014202110103110101031a2b11000103062d2f4301021101021100014301184205000000003b0114000205000000473b01140003050000008b3b01140004050000009e3b0114000505000000be3b0114000603001400010300140007030014000811010144004a12000143000403e81b03002d14000911010212000232330033021101030211010303001100090700031843021101041200044a12000511010412000612000703021843014302050000fff11c140008110009110008050000fff11a3103002d4a1200080302430114000a11000a14000b11000a12000703202947001811000a4a12000511000a120007032019430114000b45004511000a12000703202747003907000314000c030014000d11000d032011000a120007192747001411000c0700091817000c354917000d214945ffdc11000c11000b1814000b07000a11000b18140007021101051100070302430214000702110103030011000707000318430214000e02110106430014000f11011807000b25470004014500010011000f07000c1607000314001011011612000d3300131101074a12000e11011612000d430107000f2647006503001400111101161200104700290211010803001101074a12000e0211010911011612000d1101161200104302430143021400114500200211010803001101074a12000e0211010a11011612000d43014301430214001107001111001118070012181400100211010b110116120013430114001211011612001447001511010c4a12001511001211011612001443024500031100121400121100100211010d110012430118140010110010070016180211010e1101161200134301180700121814001011001007001718070018181400100211010f11000f4301140013110102120002323300060211011043001400141101021200023233001811011112001934000f021101120211011307001a430143011400150211000411000743010211000511000706001b1b03002d4301180211000611001411000731430118021100040211010311000e1101021200023233000611011412001c4a12000843004302050000fff11c03102b0211010311000e110010070003184302050000fff11c2f4301180211000511001303082b11010212001d03042b2f110007314301180211000311000843011814001602110006030043014911001547000a1100161100151814001607001e1100161814001702110108030011001743024a120008031043011400181100184a12001f1100181200070302191100181200074302140019110017110019181400171100174200200c6b7f62604e656c7f4e626968076a6879596460680b6962604362795b6c6164690004657f686b097e786f7e797f64636a087d7f6279626e6261066168636a79650879625e797f64636a013d0e3c3d3d3d3d3d3d3d3c3c3d3d3d3d076b627f7f686c610a69647f686e795e646a63046f626974097e797f64636a646b740276700b6f6269745b6c613f7e797f0a6f62697452656c7e6530012b03787f61057c78687f740a6c7e626169527e646a63097d6c7965636c606830097979527a686f646930062b78786469300e526f74796869527e686e52696469077979527e6e64690a393f3439343b3a3f343b09787e687f4c6a686379096b685b687f7e6462630e523d3f4f39573b7a623d3d3d3d3c057e61646e68','484e4f4a403f5243003c01321067d4bc00000824ebfd74540000087f0211010311000111000243024a12000505000000213b0105000001533b014302421100011200064701251100011200073300191100011200074a12000811030112000912000a430103011d2634000c0211030211000112000743014700f111000112000b4a12000c07000d43011400021100024700d902110303110001120007430114000311000311030412000e2547005511000211030515000f1100031103051500100211030607000f110002430249021103071100024301491100031101032947001f1103051200111200120300294700100211030811030903020403e81a4302494500161101031103051200102a47000911000211030515000f1101031103041200132533000c110305120011120012030a274700361103051200114a120014110002430149110305120011120012030125470017021103071100024301490211030607000f110002430249110001421100014008421100023400010d14000211020a33000711000111020b3714000307001514000407001614000507001514000611020c33000711000111020d374701c411000112000a14000411000212001747000f1100021200174a12001843004500030700161400050211020e1100044301330011110005070016253400071100050700192547017d11020512001014000711020512001a1400081100080700152347000f07000f11020512000f0c000245001207000f11020512000f07001a1100080c00041400090211020f021102101100044301110009430214000a0211021111000a430114000b0211021211000b11000212001b430214000c0211020f11000a11010111000c0c0002430214000d07001514000e11021312001c47000911000d14000e4500b10d021102140211000d43020e000714000f110005070019254700710211021511000111000243024a12001d07001e43010300134a12001f430014000602110216110006430147003b0211021711000f11000611000212001b4303490211021811000f0807002043031400100211020f11000d1101021100100c0002430214000e45000611000d14000e4500250211021811000f0807002043031400110211020f11000d1101021100110c0002430214000e1102131200214700130211021a430011000212000b110219120022160211010411000e1100021100074303421100034701e91100011200071400041100011200174700091100011200174500030700161400050211020e1100044301330011110005070016253400071100050700192547019811020512001014001211020512001a1400131100130700152347000f07000f11020512000f0c000245001207000f11020512000f07001a1100130c00041400140211020f021102101100044301110014430214001502110211110015430114001611000112000b1400171102131200214700161100174a1200231102191200220211021a4300430249110005070019254700480211021511000111000243024a12001d07001e43010300134a12001f43001400061100014a12002443004a12002543004a12000505000007293b01050000081e3b014302424500bd021102121100160243021400180211020f1100151101011100180c000243021400190d021102140211001943020e000714001a0211021811001a08070020430314001b0211020f11001911010211001b0c0002430214001c11020b11001c0d1100170e000b080e001b1100011200260e00261100011200270e00271100011200280e00281100011200290e002911000112002a0e002a11000112002b0e002b11000112002c0e002c440214001d0211010411001d110002110012430342021101031100011100024302424501df11000212000b324700070d11000215000b11000114000411000212001747000f1100021200174a12001843004500030700161400050211020e1100044301330011110005070016253400071100050700192547017d11020512001014001e11020512001a14001f11001f0700152347000f07000f11020512000f0c000245001207000f11020512000f07001a11001f0c00041400200211020f02110210110004430111002043021400210211021111002143011400220211021211002211000212001b43021400230211020f1100211101011100230c0002430214002407001514002511021312001c4700091100241400254500b10d021102140211002443020e0007140026110005070019254700710211021511000111000243024a12001d07001e43010300134a12001f430014000602110216110006430147003b0211021711002611000611000212001b430349021102181100260807002043031400270211020f1100241101021100270c00024302140025450006110024140025450025021102181100260807002043031400280211020f1100241101021100280c000243021400251102131200214700130211021a430011000212000b110219120022160211010411002511000211001e4303420211010311000111000243024208420700151400020211031211011611000143021400030211030f1101151102011100030c000243021400040211031611010643014700490d021103140211000443020e000714000502110317110005110106110001430349021103181100050807002043031400060211030f1100041102021100060c0002430214000245000611000414000211030b1100020d1101011200170e00171101170e000b1100010e001b1101011200260e00261101011200270e00271101011200280e00281101011200290e002911010112002a0e002a11010112002b0e002b11010112002c0e002c44021400070211020411000711010211011243034211000140084205000000003b0314000405000001593b021400050700001400010700011400020211010043003247000208421101011200024700020842001101011500021101011200031400031100031101011500041100051101011500030842002d0754214e636b797f0a537f656b626d78797e691653536d6f53656278697e6f697c786968536a69786f64056a69786f6406536a69786f64047864696202636703797e60076562686974436a0860636f6d7865636204647e696a0764696d68697e7f036b69780a7421617f217863676962037f696f07617f586367696208617f5f786d78797f0e617f42697b586367696240657f78066069626b78640465626578047c797f6400034b4958066169786463680b7863597c7c697e4f6d7f69045c435f580b53536d6f5378697f786568046e636875017a057f7c60657801370b786340637b697e4f6d7f69076a637e7e696d60037f68650d7f696f45626a6344696d68697e037f6978056f606362690478697478087e696a697e7e697e0e7e696a697e7e697e5c6360656f7504616368690b6f7e6968696278656d607f056f6d6f6469087e6968657e696f7809656278696b7e657875','own','Super\x20expression\x20must\x20either\x20be\x20null\x20or\x20a\x20function','getTimezoneOffset','array','toDataURL','enumerable','setPrototypeOf','field','WEBGL','wID','illegal\x20catch\x20attempt','TouchEvent','3160hBpQVk','484e4f4a403f5243000d0a13c08652000000000f3be74070000003930211021611010111000143024908421101003300031101013300031101023247000208420d0700000e000103040e00021101181200000e00030d0700040e000103030e00021101030e00050d0700060e000103030e00021101040e00050d0700070e000103030e00021101050e00050d0700080e000103030e00021101030e00050d0700090e000103000e00020d07000a0e000103000e00020d07000b0e000103000e00020d07000c0e000103000e00020d07000d0e000103000e00020d07000e0e000103030e00021101060e00050d07000f0e000103030e00021101070e00050d0700100e000103010e00020d0700110e000103010e00020d0700120e000103010e00020d0700130e000103000e00020d0700140e000103030e00021101080e000503010e00150d0700160e000103030e00021101090e00050d0700170e000103030e000211010a0e00050d0700180e000103030e00021101030e00050d0700190e000103030e000211010b0e00050d07001a0e000103030e000211010c0e00050d07001b0e000103030e000211010d0e00050d07001c0e000103030e00021101030e00050d07001d0e000103000e00020d07001e0e000103030e000211010e0e000507001f0e00200d0700210e000103030e000211010f0e00050d0700220e000103030e00021101100e00050d0700230e000103030e00021101110e000503010e00150d0700240e000103010e00020d0700250e000103040e00021101121200260e00030d0700270e000103030e00021101130e00050d0700280e000103030e00021101030e00050d0700290e000103040e00020c00221400010c0000140002030014000311000311000112002a274700eb110001110003131200020300480013030148002f0302480045030348005b494500be0211011411010011000111000313120001134301110001110003131500034500a011010111000111000313120001131100011100031315000345008511010211000111000313120001131100011100031315000345006a110001110003131200154700321101153a07002b264700241100024a12002c110001110003131200054a12002d110001110003131200204301430149450025110001110003131200054a12002d0211000111000313120020430211000111000313150003450003450000170003214945ff081101153a07002b2647001d1101154a12002e11000243014a12002f05000000003b0143014945000a02110116110001430149084200300349414c0146014e015a095b5c495a5c7c41454d015c09494a4144415c414d5b064b49465e495b0a5c41454d5b5c49455819085844495c4e475a451340495a4c5f495a4d6b47464b5d5a5a4d464b510c4c4d5e414b4d654d45475a51084449464f5d494f4d094449464f5d494f4d5b0a5a4d5b47445d5c4147460f495e4941447a4d5b47445d5c414746095b4b5a4d4d467c47580a5b4b5a4d4d46644d4e5c104c4d5e414b4d7841504d447a495c41470a585a474c5d4b5c7b5d4a074a495c5c4d5a510158095c475d4b4061464e47085c41454d5247464d0a5c41454d5b5c4945581a074f585d61464e470b425b6e47465c5b64415b5c0b58445d4f41465b64415b5c0a5c41454d5b5c4945581b095d5b4d5a694f4d465c0a4d5e4d5a6b474743414d075c5c775b4b414c01450b5b51465c49506d5a5a475a0c46495c415e4d644d464f5c40055a5c4b61780844474b495c414746094e587e4d5a5b4147460b77775e4d5a5b4147467777084b44414d465c614c0a5c41454d5b5c4945581c0b4d505c4d464c6e414d444c06444d464f5c40095d464c4d4e41464d4c04585d5b40044b49444403494444045c404d46','rewriteUrl\x20','setTTWid','height','product','Vrinda','X-Mssdk-Info','arrayBuffer','vibrate','sendBeacon','0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ','canvas','Character\x20outside\x20valid\x20Unicode\x20range:\x200x','join','throw','iterator\x20result\x20is\x20not\x20an\x20object','DEPTH_BITS','compatMode','forEach','Attempted\x20to\x20access\x20private\x20element\x20on\x20non-instance','unsupport\x20type','attempted\x20to\x20call\x20addInitializer\x20after\x20decoration\x20was\x20finished','pageXOffset','length','method','bytes','charAt','Sylfaen','toElementDescriptor','base64','createElement','private','ttcid','msStatus','MAX_VARYING_VECTORS','elements','484e4f4a403f524300232a0d2ebaf9a00000000042410a740000019d110100002347000200421101011200004700020042070001110102364700351101024a12000111010143011400011100014a120002070000430103002a34000f1100014a120002070003430103002a470002004211010333000611010312000433000911010312000412000533000c1101031200041200051200064700213e000414000c413d00171101031200041200054a1200064300082547000200424107000707000807000907000a07000b07000c07000d07000e07000f0700100700110c000b1400020700120700130700140c000314000303001400041100041100031200152747001e11000311000413140005110103110005134700020042170004214945ffd503001400061100061100021200152747002111000211000613140007110103120016110007134700020042170006214945ffd21101024a1200171101031200164301140008030014000911000814000a11000911000a1200152747003911000a1100091314000b11000b4a1200181101050700194401430133000e11010312001611000b1307001a134700020042170009214945ffba0142001b096d7f787e68736c7f68137d7f6e556d744a68756a7f686e63547b777f690773747e7f62557c09767b747d6f7b7d7f690679726875777f07686f746e73777f07797574747f796e1445456d7f787e68736c7f68457f6c7b766f7b6e7f134545697f767f74736f77457f6c7b766f7b6e7f1b45456d7f787e68736c7f6845697968736a6e457c6f74796e7375741745456d7f787e68736c7f6845697968736a6e457c6f74791545456d7f787e68736c7f6845697968736a6e457c741345457c627e68736c7f68457f6c7b766f7b6e7f1245457e68736c7f68456f746d687b6a6a7f7e1545456d7f787e68736c7f68456f746d687b6a6a7f7e1145457e68736c7f68457f6c7b766f7b6e7f144545697f767f74736f77456f746d687b6a6a7f7e1445457c627e68736c7f68456f746d687b6a6a7f7e0945697f767f74736f770c797b7676497f767f74736f771645497f767f74736f7745535e5f45487f7975687e7f6806767f747d6e72087e75796f777f746e04717f636905777b6e79720a463e417b3760477e794506797b79727f45','pop','showOffsetX','MAX_TEXTURE_IMAGE_UNITS','2571598OPFKfY','webgl','appendChild','outerHeight','screenX','kind','navigator','attempted\x20to\x20use\x20private\x20field\x20on\x20non-instance','cookie','AsyncIterator','crypto','buffer','availHeight','The\x20property\x20descriptor\x20of\x20a\x20field\x20descriptor','resolve','react.element','close','monospace','stun:stun.l.google.com:19302','acc','BLUE_BITS','Arguments','style','nextLoc','pageYOffset','72px','webkitRequestAnimationFrame','hidden','MAX_FRAGMENT_UNIFORM_VECTORS','node','Parchment','kWebsocket','xmst','number','altKey','A\x20method\x20descriptor','Leelawadee','6300OtYFrs','href','1155KnHwvu','storage','sTm','setMonth','20690wmYtVy','266076LNZfld','Malformed\x20string','setConfig','touchmove','rval','unload','from','Image','tryLoc','ActiveXObject','assign','\x20property.','_sent','mmmmmmmmmmlli','offsetHeight','slice','getOwnPropertyDescriptor','mhe','utf8','Object\x20is\x20not\x20async\x20iterable','reset','raw','constructor','attempted\x20to\x20','Invalid\x20attempt\x20to\x20iterate\x20non-iterable\x20instance.\x0aIn\x20order\x20to\x20be\x20iterable,\x20non-array\x20objects\x20must\x20have\x20a\x20[Symbol.iterator]()\x20method.','isArray','chargingTime','__await','484e4f4a403f524300071336bc6677450000001613b112b6000003090b4a12000911021607000a07000b4402070001430242070000140001110115082633000511011502263300071101150700012647001d3e000a140029070002140001413d000d021101001101154301140001411101013234000611010212000347000b001401010211010343004902110104430049110105120004140002110106120005140003030214000411000414000503401400060211010011011443011400071101074a120006021101001101074a1200061100074301430143011400081101074a120006021101001101074a1200061100014301430143011400091101081200071200083247001005000000003b0011010812000715000811010912000c14000a11000a33000811000a3a07000d2547000c11000a4a120008430014000a0211010a110003110002430214000b0211010b11000b11000a430214000c0211010c11000c07000e430214000d1101074a1200060211010011000d4301430114000e11010d44004a12000f43000403e81b14000f0211010e43001400101100061400111100030401001b1400121100030401001c140013110002140014110008030e13140015110008030f13140016110009030e13140017110009030f1314001811000e030e1314001911000e030f1314001a11000f03182c0400ff2e14001b11000f03102c0400ff2e14001c11000f03082c0400ff2e14001d11000f03002c0400ff2e14001e11001003182c0400ff2e14001f11001003102c0400ff2e14002011001003082c0400ff2e14002111001003002c0400ff2e140022110011110012311100133111001431110015311100163111001731110018311100193111001a3111001b3111001c3111001d3111001e3111001f311100203111002131110022311400230400ff1400240211010f11001111001311001511001711001911001b11001d11001f11002111002311001211001411001611001811001a11001c11001e11002011002243131400250211010b0211011011002443011100254302140026021101111100051100241100264303140027021101121100270700104302140028110028420011201c4c491c401b1c41401e48481a4a484c1d414048484141401d1b1e404c4a4f1d00201e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e010e060d1a1b171c1d071d160e1b171c1d061c1d1b171c1d09080a170c170c01081d040c0a1115070a1d0814191b1d212623240b240d3e3d3e3e2400394825530423240b240d3e3d3e3e2400394825535c011f090d0b1d0a391f1d160c060b0c0a11161f020b48071f1d0c2c11151d020b4a','Arial\x20Hebrew','msToken','An\x20element\x20descriptor\x27s\x20.placement\x20property\x20must\x20be\x20one\x20of\x20\x22static\x22,\x20\x22prototype\x22\x20or\x20\x22own\x22,\x20but\x20a\x20decorator\x20created\x20an\x20element\x20descriptor\x20with\x20.placement\x20\x22','visible','decode','concat','30762fvQkGV','hash','STENCIL_BITS','configurable','\x20private\x20field\x20on\x20non-instance','T_MOVE','clientX','images','version','renderer','removeItem','catchLoc','indexOf','msHidden','isView','toLocaleString','https://mssdk.bytedance.com','B4Z6wo','dispatchException','msvisibilitychange','\x20decorators\x20must\x20return\x20a\x20function\x20or\x20void\x200','Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe','Cannot\x20convert\x20undefined\x20or\x20null\x20to\x20object','initializer','awrap','continue','mousedown','mousemove','update','__ac_blank','0123456789abcdef','getItem','Futura','MAX_RENDERBUFFER_SIZE','GPUINFO','host','484e4f4a403f524300010a1106afb0650000000079a66ec20000008c1101001200004a12000143001400011100014a120002070003430103002a470002014211010307000444011400021101013300061101011200053300091101011200051200064700411101011200051200061400031100034a120002070007430103002534000f1100034a120002070008430103002534000c1100024a120009110003430147000200420142000a093b3d2b3c0f292b203a0b3a210221392b3c0d2f3d2b0727202a2b360128082b222b2d3a3c21204a10263a3a3e3d71741261126166157e637713357f627d33661260157e637713357f627d3367357d3332152f63287e637713357f627a336674152f63287e637713357f627a3367357933670822212d2f3a27212004263c2b28042827222b10263a3a3e74616122212d2f2226213d3a043a2b3d3a','BluetoothUUID','decorateClass','mozRTCPeerConnection','defineClassElement','credentials','writable','value','WEBKIT_EXT_texture_filter_anisotropic','JS_MD5_NO_ARRAY_BUFFER','Metadata\x20keys\x20must\x20be\x20symbols,\x20received:\x20','getReferer','indexDB','__proto__','Object','splice','symbol','offsetWidth','executing','mozBattery','normal','kFakeOperations','reverse','finisher','createOffer','addEventListener','Cannot\x20call\x20a\x20class\x20as\x20a\x20function','\x20must\x20be\x20a\x20function','getContext','referrer','afterLoc','has','return','kNoMove','netscape','MAX_CUBE_MAP_TEXTURE_SIZE','bind','width','valueOf','off','JS_MD5_NO_ARRAY_BUFFER_IS_VIEW','innerWidth','257232gkndOM','null','isSecureContext','pixelDepth','.initializer\x20has\x20been\x20renamed\x20to\x20.init\x20as\x20of\x20March\x202022','/web/report','removeChild','[object\x20Boolean]','getSupportedExtensions','error','GeneratorFunction','message','initializeInstanceElements','systemLanguage','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/','tt_webid','sqrt','__ac_referer','blocks','MOZ_EXT_texture_filter_anisotropic','1hcbEHL','308350OyvEoV','484e4f4a403f5243003a20169967f185000000000b49c93c000000761101001200004a12000143001400011100014a120002070003430103002a47000201421101013a070004263300191101021200051200064a12000711010112000843010700092534002b1101033a0700042547000607000445000902110104110103430107000a2533000a11010312000b07000c2542000d09282e382f1c3a3833290b293211322a382f1e3c2e38073433393825123b083831383e292f323309283339383b34333839092d2f32293229242d380829320e292f34333a043e3c3131072d2f323e382e2e1006323f37383e297d2d2f323e382e2e0006323f37383e290529342931380433323938','for','break','construct','Constantia','webkitvisibilitychange','activeState','toClassDescriptor','layers','bogusIndex','arg','screen','buffer8','getOwnPropertyNames','get','prev','buildID','lastByteIndex','\x27\x20method','Bad\x20UTF-8\x20encoding\x200x','[object\x20Array]','add','484e4f4a403f5243000027194f9666590000000044a16fed000000270700001400013e000a140002070001140001413d000d0211010011010243011400014111000142000200200d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d','call','characterSet','bodyVal2str','tryEntries','\x22\x20is\x20read-only','tt_webid_v2','locationbar','maxTouchPoints','string','addElementPlacement','try\x20statement\x20without\x20catch\x20or\x20finally','mozVisibilityState','showColor','name','split','xmstr','prototype','AcroPDF.PDF.1','Tunga','4218tDAtHd','AVENIR','getMetadata','track','touchEvent','decorateConstructor','now','failed\x20to\x20set\x20property','484e4f4a403f524300263203ec75a3740000000817718683000003051100011100022e4211011507000013002547000a070001110115070002160d03000e0003000e00040c00000e00050c00000e0006010e0007010e00000700080e0002010e00090d0305033c1a0e000a03020e000b0305033c1a0e000c0e000d0700080e000e000e000f03030e00101400011101004a1200111100011101154302491100011200030300253400161101014a120012110001120003430111000112000326470009110102070013440140110001120014330007110001120015324700091101020700164401401101031200174a12001811000112000343014911010412000303002547000c1100011200031101041500031100011200043247009c110001120002070008254700091101020700194401401100011200020700012447000911010207001a44014011000112000211010415000202110105110001430111010415001b021101061101071100011200100403e81a43024911000112001c0826330005110001022647002e11010412001d4a12001811000112001c43014911010412001d4a12001e05000000003b024301323211010415001c11000112000d4700a60011010415001f11010847006411000112000d12000a33001311000112000d12000a11010412000d12000a2947003f021101091101084301491101004a1200110d11010412000d11000112000d430311010415000d0211010a11010b11010412000d12000a0403e81a43021401084500351101004a1200110d11010412000d11000112000d430311010415000d0211010a11010b11010412000d12000a0403e81a430214010811000112002047001c1100011200201101041500200211010611010c03050403e81a4302491100010b1500210211010d4300490211010e1100011200054301490211010f1100011200064301490211011043004911011132330006110001120007470020001401111100011200071101041500070211010611011203050403e81a43024911000112000f4700241101041200223247001a0011010415002202110106110113030a0403e81a11000143034900110104150023084200240350524402575a064651535d5b5a03555d50055d4767707f0e515a555658516455405c785d47400f414658665143465d405166415851470347505d0003505142035246510a415a5d4075595b415a4008415a5d40605d595105404655575f04595b5051044c4c56530450504640065547475d535a0552585b5b461e5b44405d5b5a14555d501c7d5a40515351461d145d47145a51515051501503565b5107565b517c5b474024565b517c5b474014594147401456511444465b425d505150145d5a14565b5114595b505107555d50785d4740044441475c0f4651535d5b5a145d47145a41585815124651535d5b5a145d47145d5a4255585d50150a4651535d5b5a775b5a520142106b515a55565851675d535a5540414651064651504157510b515a55565851604655575f0444514652075b44405d5b5a47046b5052440b5d5a5d405d55585d4e5150','appMinorVersion','Attempted\x20to\x20decorate\x20a\x20public\x20method/accessor\x20that\x20has\x20the\x20same\x20name\x20as\x20a\x20previously\x20decorated\x20public\x20method/accessor.\x20This\x20is\x20not\x20currently\x20supported\x20by\x20the\x20decorators\x20plugin.\x20Property\x20name\x20was:\x20','hex','dischargingTime','PLUGIN','T_KEYBOARD','ret_code','\x22.\x20Please\x20configure\x20the\x20dynamicRequireTargets\x20or/and\x20ignoreDynamicRequires\x20option\x20of\x20@rollup/plugin-commonjs\x20appropriately\x20for\x20this\x20require\x20call\x20to\x20work.','@@toPrimitive\x20must\x20return\x20a\x20primitive\x20value.','battery','Generator\x20is\x20already\x20running','headers','md5','setter','=;\x20expires=Mon,\x2020\x20Sep\x202010\x2000:00:00\x20UTC;\x20path=/;','_raw_sec_did','hardwareConcurrency','script','fontSize','_byted_sec_did','keydown','__private_','screenY','Duplicated\x20element\x20(','484e4f4a403f524300211209597bcccc0000053aae77fba6000005e50b1200093247004e0b12000a4a12000b0d0700050e000c1100000e000d43014911021607000e07000f44024a120010110001430147001f1100024a12001143004a12001243004a12001307001443010300130b1500151101054a1200160b1100004302421100000b1500171101074a1200160b1100004302420c00000b15000a0b12000a4a12000b0d0700040e000c1100000e000d4301491100014a12001843000b1500191100020b15001a1101044a1200160b1100004302421101094a1200240b120019430103011d26140002021102010b12001a43013300031100024702fe0b12001a4a120024070025430103011d2947000e1101064a1200160b1100004302421100010b1500260b1200271400030b12001b1400040b12001c1400050b12001d1400060b12001e1400070b12001f1400080b1200201400090b12002114000a0d14000b030014000c11000c1101081200282747001f0b12002911010811000c131311000b11010811000c131617000c214945ffd411020212002a14000d11020212002b14000e11000e07002c2347000f07002d11020212002d0c000245001207002d11020212002d07002b11000e0c000414000f02110203021102040b12001a430111000f4302140010021102051100104301140011021102061100110b1200264302140012021102031100101101011100120c0002430214001307002c14001411020712002e4700091100131400144500910d021102080211001343020e002f1400150b12001907002325470050021102090b120015430147003a0211020a1100150b1200150b1200264303490211020b110015080700304303140016021102031100131101021100160c000243021400144500061100131400144500250211020b110015080700304303140017021102031100131101021100170c000243021400140b12000a33000f0b12000a03001307000c130700042647000202420b12000a14001803001400191100191100181200282747005d11001903002547002d1100141100181100191312000d030116000b1500091101044a1200160b1100181100191312000d43024945001f0b1100181100191307000c13134a1200160b1100181100191312000d430249170019214945ff960b1200174700100b1200074a1200160b0b1200174302490b07000a39491102071200314700140b4a12000511020c1200320211020d43004302491100030b1500271100040b15001b1100050b15001c05000003ed3b010b15001d1100070b15001e1100080b15001f1100090b15002011000a0b150021030014001a11001a1101081200282747001f11000b11010811001a13130b12002911010811001a131617001a214945ffd41101064a1200160b11000043024203001400020b1200333400040b12001a34000307002c1400030211030e110003430147000503011400021100034a120024110300120034120035430103011d2647000503021400021100020300294700ea0b4a12003607003743011400041100044700d70211030f0b12001a43011400051100051103101200382547005511000411030215002d11000511030215002a0211031107002d1100044302490211031211000443014911000511010d2947001f1103021200391200280300294700100211031311031403020403e81a43024945001611010d11030212002a2a47000911000411030215002d11010d11031012003a2533000c110302120039120028030a274700361103021200394a12000b110004430149110302120039120028030125470017021103121100044301490211031107002d11000443024911010647000a02110106110001430149084207000014000107000114000211010012000212000314000311000312000414000411000312000514000511000312000614000611000312000714000711000312000847000208420011000315000805000000003b0211000315000505000000643b0011000315000705000000793b0211000315000407001b07001c07001d07001e07001f0700200700210c00071400080700220700230c000214000905000000ba3b011100031500060842003b0755204f626a787e0a527e646a636c79787f680e5540414579797d5f687c78687e79097d7f62796279747d6804627d6863107e68795f687c78687e7945686c69687f047e68636910627b687f7f6469684064606859747d680f526c6e52646379687f6e687d79686905527e68636915526f7479686952646379687f6e687d795261647e79047d787e65046b78636e096c7f6a78606863797e0e536e6263796863792079747d682901640479687e790879625e797f64636a0b796241627a687f4e6c7e68057e7d61647901360e526f74796869526e626379686379056c7d7d61741552627b687f7f6469684064606859747d684c7f6a7e0b7962587d7d687f4e6c7e680d526f74796869526068796562690a526f7479686952787f610762636c6f627f79076263687f7f627f06626361626c6909626361626c696863690b626361626c697e796c7f790a62637d7f626a7f687e7e09626379646068627879034a4859045d425e59076463696875426b0b527e646a636c79787f68300b526f74796869526f6269741262637f686c69747e796c79686e656c636a68066168636a796506787d61626c6908607e5e796c79787e0b52526c6e5279687e7964690007607e5962666863017b03787f61076b627f7f686c61037e69640d7e686e44636b6245686c69687f0b7f687e7d62637e68585f410861626e6c796462630465627e79116a68795f687e7d62637e6845686c69687f0a7520607e207962666863037e686e0e607e43687a596266686341647e790464636479','round','innerHTML','appCodeName','defineProperties','Could\x20not\x20dynamically\x20require\x20\x22','push','__destrObj','toElementDescriptors','defaultProps','fromElementDescriptor','documentMode','Object.keys\x20called\x20on\x20non-object','WEBGL_debug_renderer_info','reduce','replace','setUserMode','changedTouches','JS_MD5_NO_WINDOW','[object\x20Object]','item','beforeunload','%27','enableTrack','envcode','gpu','ubcode','getParameter','undefined','start','isGeneratorFunction','completion','getOwnPropertySymbols','next','availWidth','values','oscpu','Tw\x20Cen\x20MT','sort','bluetooth','requestMediaKeySystemAccess','keyboardList','key','window','Unfinished\x20UTF-8\x20octet\x20sequence','setMetadata','static','debug','languages','toolbar','msDoNotTrack','reject','finishers','208XZrBOJ','UNMASKED_RENDERER_WEBGL','external','shadowBlur','POST','484e4f4a403f5243003e0d23e5c579310000006248d1745c000002cc0d140001110200070000131400021100020700012447000a11000211000107000016110200070002131400031100030700012447000a11000311000107000316110200070004131400041100040700012447000a110004110001070005161100014205000000003b001400010114000211010f3247000911010112000614010f11010f110101120007254700040014000211010244004a12000843001400030d1101001200094a12000a030043010e000b11010012000c4a12000a030043010e000d11010012000e4a12000a030043010e000f1101001200104a12000a030043010e001114000411000412000b12001203002533000c11000412000d12001203002533000c11000412000f12001203002533000c110004120011120012030025470002084211000412000b12001203101a11000412000d120012030c1a1811000412000f12001203041a1811000412001112001203081a181400051100031101031200131101041200141200150403e81a182747003a1101031200161101041200141200170404001a27470020110103120016110005181101030700163549021101054300490014000245000045001d11000311010315001311000511010315001602110105430049001400021100024700f703021400060d1100040e00181100060e00191400070d11000707001a1611010412001b11000707001a1307001b1607000111010244004a12000843001811000707001a1307001c1611010012001d11000707001a1307001d16030011000707001a1307001e160d11000707001f161101064a12002011000707001f13021100014300430249021101071101081200210211010911010a4a120022110007430111010b120023430243021400081101041200240700251314000911000932470002084211010f110101120026254700190211010c110009110008430214000a11000a3247000045000f0211010d1100091100080d0043044908420027052121223c31000821210a2230373c310721210230371c310b21210a2230373c310a23670921210230373c3103670727203b3b3c3b3205333920263d07323021013c383008383a2330193c2621062625393c3630063730183a23300936393c363e193c262107373016393c363e0c3e302c373a342731193c26210a37301e302c373a3427310b3436213c233006213421300b223c3b313a2206213421300639303b32213d0326013805212734363e08203b3c21013c3830033436360a203b3c2114383a203b210837303d34233c3a2707382632012c253003221c1103343c3109213c3830262134382507343c31193c26210b25273c2334362c183a313006362026213a38063426263c323b0f0210170a1110031c16100a1c1b131a092621273c3b323c332c043f263a3b0a2730323c3a3b163a3b33092730253a272100273904302d3c21','metadata','productSub','mark','\x20is\x20not\x20an\x20object.','substr','Invalid\x20attempt\x20to\x20spread\x20non-iterable\x20instance.\x0aIn\x20order\x20to\x20be\x20iterable,\x20non-array\x20objects\x20must\x20have\x20a\x20[Symbol.iterator]()\x20method.','Gulim','experimental-webgl','setRequestHeader','charging','484e4f4a403f52430031032581faca6c0000000093505d60000001c60700001400010d1400020700011100020700021607000311000207000416070005110002070006161100021101021314000307000714000403001400061101011200081100060303182a4700b11101014a1200091700062143010400ff2e03102b1101014a1200091700062143010400ff2e03082b2f1101014a1200091700062143010400ff2e2f1400051100041100034a12000a1100050500fc00002e03122c43011817000435491100041100034a12000a110005050003f0002e030c2c43011817000435491100041100034a12000a110005040fc02e03062c43011817000435491100041100034a12000a110005033f2e430118170004354945ff3f110101120008110006190300294700b41101014a1200091700062143010400ff2e03102b110101120008110006294700161101014a12000911000643010400ff2e03082b45000203002f1400051100041100034a12000a1100050500fc00002e03122c43011817000435491100041100034a12000a110005050003f0002e030c2c4301181700043549110004110101120008110006294700161100034a12000a110005040fc02e03062c430145000311000118170004354911000411000118170004354911000442000b011441686b6a6d6c6f6e616063626564676679787b7a7d7c7f7e717073484b4a4d4c4f4e414043424544474659585b5a5d5c5f5e51505319181b1a1d1c1f1e1110020614025a19416d424d594e411d73625a786b111906644f5f5e1a1f7160187b1b1c027e7c68456c401e67654b4658707d66795c53446f4363475b505110617f6e4a487a5d6a4c14025a18416d424d594e411d73625a786b111906644f5f5e1a1f7160187b1b1c047e7c68456c401e67654b4658707d66795c53446f4363475b505110617f6e4a487a5d6a4c14025a1b0006454c474e5d410a4a41485b6a464d4c685d064a41485b685d','toString','xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx','warn','parse','sent','iterator','PDF.PdfCtrl.','__ac_testid','clickList','create','hasOwnProperty','init','An\x20initializer','clientHeight','extras','__esModule','sessionStorage','kKeyboardFast','devicePixelRatio',';\x20path=/;','location','GREEN_BITS','484e4f4a403f524300192d11257a6fc000000000d4cf00750000006703011400011101004a12000011010603062b1100012f43011400021101004a1200001101014a1200011101014a12000243000401001a4301430114000302110102110003110105430214000411000211000318110004181400050211010311000507000343024200040c5f4b56547a51584b7a565d5c055f5556564b064b58575d5654024a08','Castellar','toGMTString','mozHidden','Set','[native\x20code]','complete','484e4f4a403f52430012270f0b8aa329000000005cddda270000008a0211010043003247007e1101014a12000007000143011400011100011200024a12000343004a120004110104070005070006440207000743024a120008070009430103002734002c1101021200034a12000343004a120004110104070005070006440207000743024a120008070009430103002734001011010212000a4a120003430007000b26420142000c0d18091e1a0f1e3e171e161e150f06181a150d1a08090f143f1a0f1a2e2937080f14280f0912151c07091e0b171a181e03270851011c000712151f1e03341d0a151a0f120d1e18141f1e070b170e1c12150814201419111e180f5b2b170e1c12153a09091a0226','filter','runClassFinishers','webkitVisibilityState','2.11.0','\x20after\x20decoration\x20was\x20finished','suffixes','completed','src','Cannot\x20instantiate\x20an\x20arrow\x20function','getTime','resultName','children','accessor.get','484e4f4a403f52430024153c4037f00000000009d722c1e5000000d200110208150002084202110100430047001c1101014a120000070001430114000105000000003b001100011500030211010243004700553e002b140002110002120004110104070005132533000c11010312000612000703002547000700110108150002413d00241101031200064a12000807000907000a4302491101031200064a12000b0700094301494102110105430047002311010312000c3233000f11010312000d34000611010312000e4700070011010815000211010612000f11010812000203022b2f11010607000f35490842001004637c69620478697f780965626f636b62657863076362697e7e637e046f636869125d5943584d5349544f494948494853495e5e0e7f697f7f6563625f78637e6d6b69066069626b7864077f697845786961107f63616947697544697e694e75786968000a7e6961637a69457869610965626869746968484e0c5c63656278697e497a6962780e415f5c63656278697e497a6962780769627a6f636869','https://mssdk.bytedance.com/websdk/v1/getInfo','?q=','484e4f4a403f5243003e19390bcd41790000000084fc29f10000006111010012000033000d1101001200001200010700022347000303014211010112000311010112000412000326470003030142110101120005110101120006264700030301421101011200071200081101021200071200082447000303014203024200090c3125363a32123b323a3239230723363019363a32061e1105161a12083b383436233e3839062736253239230424323b3103233827063125363a3224063b323930233f','defineProperty','[object\x20Generator]','Wingdings','hasInstance','SHADING_LANGUAGE_VERSION','platform','484e4f4a403f52430007152cc2c1a53c00000061235466970000007f1100010700022534000711000107000325340007110001070004253400071100010700052547000200423e0004140002413d002b1102021100011333001b11020211000113120006082634000c11020211000113120007082647000200424108421101014a12000011010243014a12000105000000003b0143011401000842000813717362596178466479667364626f58777b73650465797b7308757370457e77646608557370457e77646605737977667f16737941737454647961657364527f65667762757e73640f747f787259747c73756257656f78750e7f65535941737454647961657364','asyncIterator','object','colorDepth','keys','wrapped','getter','finalized','all','appName','regionConf','application/x-www-form-urlencoded','attempted\x20to\x20get\x20private\x20field\x20on\x20non-instance','createEvent','body','toElementFinisherExtras','open','displayName','_urlRewriteRules','random','font','484e4f4a403f52430031033191576c4000000000940c5eb50000005302110100430032470047070000110101363234000b110101120000110102373234000707000111010336340007070002110103363400070700031101033634000f0700041101033607000511010336274201420006077d61786a64637e08527d656c637962600b6e6c61615d656c637962600b525263646a6579606c7f68054c78696462184e6c637b6c7e5f686369687f64636a4e6263796875793f49','discharingTime','SimSun-ExtB','fromClassDescriptor','versions','charCodeAt','fillText','fetch','freeze','Create\x20WebSocket','webkitRTCPeerConnection','digest','class','MAX_VERTEX_UNIFORM_VECTORS','filename','first','visibilitychange','A\x20class\x20descriptor','clientWidth','frontierSign','msVisibilityState','antialias','484e4f4a403f5243002a3d04fa03273900000000b93145d7000004061101001200004a12000143001400011101001200024a120001430014000203001400030301140004030214000503031400060304140007030514000811000814000907000314000a07000414000b07000514000c07000614000d07000714000e07000814000f07000914001007000a1400111100014a12000b07000c430103002a34000f1100014a12000b07000d430103002a4700091100071400094500de1100014a12000b11000a430103002a4700091100031400094500c31100014a12000b11000c430103002a4700091100041400094500a81100014a12000b11000d430103002a34000f1100014a12000b07000e430103002a34000f1100014a12000b07000f430103002a4700091100051400094500691100014a12000b11000e430103002a34000f1100014a12000b11000f430103002a34000f1100014a12000b110010430103002a34000f1100014a12000b070010430103002a34000f1100014a12000b070011430103002a4700091100061400094500061100081400091100024a12000b11000b430103002a33000711000911000326470005004245012c1100024a12000b11000d430103002a34000f1100024a12000b11000c430103002a34000f1100024a12000b070012430103002a330007110009110005263300071100091100042647000500424500dd1100024a12000b110011430103002a34000f1100024a12000b11000f430103002a34000f1100024a12000b110010430103002a34000f1100024a12000b11000e430103002a3300071100091100072633000711000911000626470005004245007c1100024a12000b11000b430103002733000f1100024a12000b11000d430103002733000f1100024a12000b110011430103002733000f1100024a12000b11000e430103002733000f1100024a12000b11000f430103002733000f1100024a12000b1100104301030027140012110012110009110008252647000200420300140013030114001403021400150303140016030414001703051400181100181400191100014a12000b070013430103002a47000911001514001945008a1100014a12000b070014430103002a34000f1100014a12000b070015430103002a34000c1100014a12000b070016430147000911001414001945004e1100014a12000b070017430103002a4700091100131400194500331100014a12000b070018430103002a34000f1100014a12000b070019430103002a4700091100171400194500061100181400190211010143004a120001430014001a110019110013243300071100191100142433002111010212001a34001811010012001b4a12001c43004a12000b07001d430103002a4700020042110019110013243300071100191100142433000f11001a4a12000b07001a430103002a47000200420142001e090b0d1b0c3f191b100a0b0a113211091b0c3d1f0d1b080e121f0a18110c13070917101a11090d03091710071f101a0c11171a051217100b0606170e1611101b04170e1f1a04170e111a03131f1d0717101a1b06311809131f1d17100a110d160c131f1d210e11091b0c0e1d57041d0c110d03064f4f051d0c17110d05180617110d040e17151b0818170c1b1811065106110e1b0c1f51055e110e0c51055e110e0a51071d160c11131b51080a0c171a1b100a5104130d171b061d160c11131b06081b101a110c080a112d0a0c1710190639111119121b','setTTWebid','Buffer','_enablePathListRegex','outerWidth','type','decorateElement','484e4f4a403f524300040e131c85d3950000064d665eaab9000007ca05000001d03b0014000105000003953b00140002050000046a3b001400031102084400140004021100024300490211000343004907004b07004c07004d07004e07004f07005007005107005207005307005407005507005607005707005807005907005a07005b07005c0c001214000702110209110200110007030043031400051100050211020911020007005d1307005e0c000111000712005f43032f17000535490700600c00011400080211020911020607006113110008030043031400060d1400090211020a4300110009070062160211020b4300110009070063160211020c43001100090700641607001811020844004a1200654300181100090700661611020d4a1200671100044a12006843001d033c1b4301110009070069160211020e430011000907006a160211020f43004a120008430011000907001d1611000511000907006b1611000611000907006c1602110210430011000907006d1602110001430011000907006e1602110211430011000907006f16030114000a11021212007011000907007016021102130700714301110009070072160211021307007343011100090700741611000a1100090700751603001100090700761611021412007711000907007716110009423e000714000a030042413d01b6030014000111030007000013340014110301070001134a07000213070003430103002a47000607000445000203001400020700051103023a24470006070006450002030014000311030307000713070008134a0700091311030007000a1343014a0700021307000b430103002934002e11030007000c1333000b11030007000c1307000d1333001607000e11030007000c1307000d134a0700081343002534000711030007000f131400041100044700060700104500020300140004110004330011110301070001134a0700111307001243014700060700134500020300140005110300070014133300041100023247000607001545000203001400060211030443001400071100073233000711030007001613470006070017450002030014000807001814000911000247000b11000103012f170001354911000347000e110001030103012b2f170001354911000847000e110001030103022b2f170001354911000747000e110001030103032b2f170001354911000647000e110001030103042b2f170001354911000547000e110001030103052b2f170001354911000447000e110001030103062b2f17000135491100014241084211030512001907001a133247000c030011030512001907001a163e0010140003030111030512001907001a16413d004911030007001b1347003e11030007001b1344001400011103064a07001c1307001d43014a07001e1307001f430114000205000004103b0011000107002316070024110001070025164108423e0010140002030111040512001907001a16413d00421101024a070020131101010300030043034903001101024a0700211303000300030103014304070022130303132514000103021100011811040512001907001a164108420c000014000107002607002707002807002907002a07002b07002c07002d07002e07002f0700300700310700320700330700340700350700360700370700380700390c001414000211030107003a133247000e07003b11030512001907003c35423e001214000507003d11030512001907003c3542413d003b05000005203c021400031100024a0700481305000005c63b0243011400041103074a0700491311000443014a0700401305000005d33b0043014941084211050107003a134a07003e130d1100010e003f43014a0700401305000005523b0143014a07004513050000059e3b014301421100010700411307004248001007004348001607004448001c49450024030111030111010216450021030211030111010216450015030011030111010216450009030511030111010216084203011d110001070046134a0700021307004743012647000503044500020303110301110102160842021101031100011100024302421101014a07004a13070018430111040512001907003c35420d140001110214070078131400021100020700182447000a11000211000107007816110214070079131400031100030700182447000a11000311000107007a1611021407007b131400041100040700182447000a11000411000107007c161100014205000000003b0014000105000005eb3b0014000202110115430049021101164300490211011743004902110118430049021101194300491101034a12007d1101051200190211000143004302491101034a12007d11010512007e0211011a43004302491101034a12007d11010512007f0211011b43004302491101034a12007d1101051200800211000243004302491101141200814a120082030043011400030d1100030e00831400040700841400050211011c0211011d1100054301030a430214000611000647000e110006030118170006354945000503011400060211011e11000511000643024911000611010507001913070085161101034a12007d1100041101054302490211011f1101204a1200861100044301110121120087430214000702110122110123120088110007430214000811011212008907008a1314000911000932470002084211012447001b1101244a120040021101251100091100080d00430443014945000f021101251100091100080d004304490842008b051b0411061509010711063513111a00071d1a10110c3b1205543b24265b053b0411061509011a1011121d1a111007321d0611121b0c0904061b001b000d041108001b2700061d1a1304171518180b3c20393831181119111a000b371b1a0700060117001b060607151215061d100401071c3a1b001d121d1715001d1b1a212f1b161e1117005427151215061d2611191b00113a1b001d121d1715001d1b1a290f350404181124150d271107071d1b1a0627151215061d05191500171c0537061d3b270a371c061b1911543d3b2706171c061b191106371c061b19110a27000d18113911101d1504311013110003033d3004181b1510053d191513110d17061115001131181119111a000617151a0215070a131100371b1a00110c0002461009100615033d191513110c1311003d19151311301500150410150015061b1a181b15104e101500154e1d191513115b131d124f16150711424058264418333b30181c35253536353d35353535353535245b5b5b0d3c41363531353535353538353535353535363535313535353d3626353543030706170b13111b181b1715001d1b1a0d1a1b001d121d1715001d1b1a07040401071c04191d101d061715191106150a191d17061b041c1b1a1107070411151f11060b1011021d1711591d1a121b0f1615171f13061b011a1059070d1a170916180111001b1b001c12041106071d0700111a005907001b06151311141519161d111a0059181d131c005907111a071b060d151717111811061b191100110609130d061b07171b04110c1915131a11001b19110011060917181d04161b150610141517171107071d161d181d000d591102111a00070e17181d04161b15061059061115100f17181d04161b1506105903061d00110f04150d19111a00591c151a101811060b041106191d07071d1b1a070142031a1504014305050111060d041a15191104001c111a0507001500110604061b190400071306151a0011100610111a1d111005171500171c0719110707151311301d07541a1b005415540215181d1054111a0119540215180111541b1254000d041154241106191d07071d1b1a3a1519110319150403151818041e1b1d1a0e2c301b19151d1a261105011107000b170611150011241b040104130611191b02113102111a00381d0700111a11060d13181b16151827001b061513110c1b04111a3015001516150711091d1a10110c111030360b15000015171c3102111a000d3517001d02112c3b161e1117000d101d07041500171c3102111a000b15101036111c15021d1b06101510103102111a00381d0700111a11060b10110015171c3102111a0009121d06113102111a001039010015001d1b1a3b16071106021106133c20393839111a013d00111931181119111a00093d1a004c350606150d0b041b0700391107071513110d050111060d2711181117001b060b041106121b0619151a1711031a1b030618111a13001c0b171b1a00110c0039111a010f101b170119111a0031181119111a000c1a15001d021138111a13001c0b1e07321b1a0007381d07000b070d1a00150c3106061b0607131100201d191109001d191107001519040512181b1b0611131100201d19110e1b1a113b121207110008001d19110e1b1a11051915131d17060324061b0407061024061b0407031e07020b16061b03071106200d0411061d120615191103151d10050000171d100617181d111a000700002b07171d1005001b1f111a07190713200d04110b04061d0215170d391b101107151d10381d0700050000031d100800002b0311161d100700002311163d100b00002b0311161d102b02460900002311161d102246061507071d131a07041801131d1a070607170611111a06170107001b190e19073a1103201b1f111a381d0700060704181d171109001b1f111a381d0700040c19071d051d1a10110c090700061d1a131d120d041e071b1a0f2331362b3031223d37312b3d3a323b0a0611131d1b1a371b1a12090611041b0600210618','dev','[object\x20Function]','substring','_invoke','getBattery','boeHost','asgw','Generator','boe','decorators','exports','accessor','plugins','this\x20hasn\x27t\x20been\x20initialised\x20-\x20super()\x20hasn\x27t\x20been\x20called','delegate','attempted\x20to\x20set\x20read\x20only\x20static\x20private\x20field','callback=','MAX_TEXTURE_MAX_ANISOTROPY_EXT','toStringTag',';\x20expires=','match','root','setItem','getContextAttributes','withCredentials','Class\x20\x22','mozvisibilitychange','userLanguage','createHash','map','Cannot\x20destructure\x20','hBytes','[object\x20Number]','floor','access','484e4f4a403f5243001a3309b621c6a00000000048c0ec7f000000650d14000111010012000047000c1101001200001400014500090211010143001400011101024a1200014300110001150002021101030304430114000211000202110104021101051101064a12000311000143011100024302070004430218140003110003420005077563656f6860690348495109524f4b435552474b56095552544f48414f405f40676465626360616e6f6c6d6a6b686976777475727370717e7f7c474445424340414e4f4c4d4a4b484956575455525350515e5f5c16171415121310111e1f0b08','MAX_COMBINED_TEXTURE_IMAGE_UNITS','default','public','vendorSub','touchstart','getExtension','Aparajita','finalize','propertyIsEnumerable','end','localStorage','@@iterator','deviceMemory','kHttp','@@toStringTag','cookieEnabled','fromCharCode','done','set','createDataChannel','stringify','async','Descriptor','hashed','ontouchstart','onicegatheringstatechange','getOwnPropertyDescriptors','then','function','CordiaUPC','T_CLICK','EXT_texture_filter_anisotropic','MS\x20Outlook','80pSJSjK','Jokerman','byted_acrawler','visibilityState','span','isWebmssdk','__web_idontknowwhyiwriteit__','cpuClass','serif','initialized','accessor\x20decorators\x20must\x20return\x20an\x20object\x20with\x20get,\x20set,\x20or\x20init\x20properties\x20or\x20void\x200','[object\x20HTMLAllCollection]','Decorating\x20class\x20property\x20failed.\x20Please\x20ensure\x20that\x20proposal-class-properties\x20is\x20enabled\x20and\x20runs\x20after\x20the\x20decorators\x20transform.','\x27\x20to\x20be\x20a\x20function','VERSION','toLowerCase','MAX_TEXTURE_SIZE','triggerUnload','MAX_VERTEX_TEXTURE_IMAGE_UNITS','element','apply','document','exec','send',')\x20can\x27t\x20be\x20decorated.','min','484e4f4a403f5243002814122ddd79950000009eb285a1cb000000e811000114000402110201110001430147007c1102021200041400051100050700052347000f0700061102021200060c00024500120700061102021200060700041100050c0004140006021102030211020411000143011100064302140007021102051100074301140008021102061100080700054302140009021102031100071101011100090c000243021400040211010211000411000211000343034205000000003b03140003070000140001110100120001082334000611010012000247000208421101001200011400021100021101001500030011010015000211000311010015000108420007070d78173a322026043a25303b150a0a34360a3c3b2130273630252130310a3a25303b050a3a25303b0b0a0a34360a213026213c3100073826013a3e303b','vivobrowser','descriptor','language','484e4f4a403f524300023a25866a0150000000002b0aa01b000001541101001200004a12000143001400011100014a120002070003430103002a47000201420700041400021101013a070004254700060700044500090211010211010143011100022534000d1101014a1200054300070006263400161101031200071200054a12000811010143010700062634001e1101043a07000425470006070004450009021101021101044301110002253400151101044a12000543004a120002070009430103002734001e1101003a070004254700060700044500090211010211010043011100022534000d1101004a120005430007000a263400121101001200004a12000207000b430103002a34001e1101053a07000425470006070004450009021101021101054301110002254700020042021101064300324700331101073a070004254700060700044500090211010211010743011100022534000d1101074a120005430007000c2647000200420142000d096f697f685b7d7f746e0b6e7556756d7f68597b697f0773747e7f62557c087f767f796e687574096f747e7f7c73747f7e086e75496e6873747d0f417578707f796e3a4d73747e756d47096a68756e756e636a7f04797b7676085e75796f777f746e12417578707f796e3a547b6c737d7b6e7568470570697e757710417578707f796e3a5273696e75686347','vendor','level','attempted\x20to\x20call\x20','placement','JS_MD5_NO_NODE_JS','initializeClassElements','indexedDB','perf','disallowProperty','lime','484e4f4a403f524300341b3e336a785800000000dbd5951f000001b50114000111010012000000254700070014000145001b1101001200000125470007011400014500090211010143001400010d010e0001010e0002010e00031100010e0004010e0005010e0006010e0007010e0008010e0009010e000a010e000b000e000c1400020211010243001100021500051100021200053247005c021101031100024301490211010411000243014902110105430011000215000702110106430011000215000802110107430011000215000902110108430011000215000b0211010943001100021500030211010a4300110002150002030014000311000303012f170003354911000311000212000b03012b2f170003354911000311000212000a03022b2f170003354911000311000212000903032b2f170003354911000311000212000803042b2f170003354911000311000212000703052b2f17000335491100031100020700061303062b2f170003354911000311000212000503072b2f17000335491100031100020700041303082b2f170003354911000311000212000303092b2f1700033549110003110002120002030a2b2f170003354911010b12000d1100032f11010b07000d354911000242000e0e547b6a796a66587c627f686344650a6f62796e687f58626c650a6864657862787f6e657f086764686a7f62646506787c627f6863036f6466086f6e697e6c6c6e790465646f6e077b636a657f6466097c6e696f79627d6e7909626568646c65627f640463646460047f6e787f076e657d68646f6e','moveList','MYRIAD\x20PRO','An\x20element\x20descriptor','finallyLoc','head','innerHeight','ORIGIN:\x20','region','addInitializer','[object\x20SafariRemoteNotification]','candidate','content-type','userAgent','webkitHidden','test','getPrototypeOf','setDate','shiftKey','accessor.init','@@asyncIterator','accessor.set'];w_0x42f5=function(){return _0x458f30;};return w_0x42f5();}function w_0x5c3140(_0x41676a,_0x3f9548,_0x39b0b8){var _0x2145df=w_0x25f3;function _0x467cb0(_0x174e0d,_0x4ea737){var _0x569e4c=w_0x25f3,_0x3ed868=parseInt(_0x174e0d[_0x569e4c(0x2a5)](_0x4ea737,_0x4ea737+(0x30*0x1a+0x1*0x1424+-0xc2*0x21)),-0x253b+-0x1ce1+-0x974*-0x7);return _0x3ed868>>>0x23ca+0x882+0x2c45*-0x1==0x11cb+-0x5c7*0x3+0x2*-0x3b?[-0xc*0x300+0x1*0x1067+0x139a,_0x3ed868]:_0x3ed868>>>-0x3*0x3b9+0x4a*0x75+-0x16a1==-0x742+-0x429+0xb6d?(_0x3ed868=(-0x1eee+0x1175+0xdb8&_0x3ed868)<<0x2*0x108d+0x2*-0x5e6+0x1*-0x1546,[-0x949+0xe2f+-0x4*0x139,_0x3ed868+=parseInt(_0x174e0d['slice'](_0x4ea737+(0x5*-0x643+-0x1*0x95+-0x1fe6*-0x1),_0x4ea737+(0x9b6+0x8*0x24a+-0x1c02)),-0xa6*0x11+-0x3f*-0x1+0x6f*0x19)]):(_0x3ed868=(0x1af1+0x1442+-0x964*0x5&_0x3ed868)<<0x3f3+-0xd93+0x9b0,[-0x13*-0x192+-0x1588+0xc1*-0xb,_0x3ed868+=parseInt(_0x174e0d[_0x569e4c(0x2a5)](_0x4ea737+(-0x8*0x39c+0x59*0x35+0x1*0xa75),_0x4ea737+(0x1*-0xad6+-0x326+0x16*0xa3)),0x20b0+-0x7d+-0x1b1*0x13)]);}var _0x450bf1,_0x55d729=0x242c+0x150c*0x1+-0x4*0xe4e,_0x26f45f=[],_0x408609=[],_0x5e254d=parseInt(_0x41676a[_0x2145df(0x2a5)](-0x43*0x7a+0xbac*-0x1+0x2*0x15cd,-0x18c6+-0x1515+0x2de3),-0x17e6+0x4*-0x269+0x1fa*0x11),_0x56844b=parseInt(_0x41676a[_0x2145df(0x2a5)](0x262*-0x3+0x19e0+-0x12b2,0x5*0x3e+0xd*-0x2ec+-0x73*-0x52),0x1*0xd69+0x1*0xb9+-0xe12);if(-0x5eec40a*0xd+0x353b2368+0x60332064!==_0x5e254d||0x7b7*0x44245+-0x712c7271+0x1ce9b3ad*0x5!==_0x56844b)throw new Error(_0x2145df(0x2a7));if(0x2*0xae1+-0x1e47+0x885!==parseInt(_0x41676a[_0x2145df(0x2a5)](0x1*0x45+0x149*-0x1+0x114,-0x1d69*-0x1+0xf47*0x1+-0x2c9e),-0x59*0x4b+-0xfdb+-0xd7*-0x32))throw new Error('ve');for(_0x450bf1=0x2*0xe7d+-0xdf*-0x19+0x3d*-0xd5;_0x450bf1<-0x22db*-0x1+-0xf98*-0x1+-0x1*0x326f;++_0x450bf1){_0x55d729+=(-0x1a7*0x3+0x17*-0x47+0xb59&parseInt(_0x41676a[_0x2145df(0x2a5)](0x751*-0x4+0x1d*0xfd+0xb3+(-0x44b*0x7+-0x100+0x1f0f)*_0x450bf1,0x8*0x3ae+0x128d+-0xd*0x3af+(0x2*-0x80f+-0xd*0x24a+0x2de2)*_0x450bf1),-0x14d6+-0x255+0x173b))<<(-0x1ce2*-0x1+0x11e3+-0x2ec3)*_0x450bf1;}var _0x537efa=parseInt(_0x41676a[_0x2145df(0x2a5)](0x13*-0x209+0x116b+0xc*0x1c8,-0x26a0+0x10d*-0x12+0x39b2),0x154e+-0x25e*-0xf+0xe3*-0x40),_0x3d66d8=(0x1bdb*0x1+-0x4e9*0x3+-0xd1e)*parseInt(_0x41676a[_0x2145df(0x2a5)](-0x1*-0x24f5+0x2279+0x67a*-0xb,-0x2*-0x1083+-0x5*0x185+0xef*-0x1b),-0x82+0x8e6+0x4*-0x215);for(_0x450bf1=-0x2471+-0x116*-0x7+0x1d0f*0x1;_0x450bf1<_0x3d66d8+(-0x1c*-0x106+0x370+-0x8*0x3fc);_0x450bf1+=0x13ac*0x1+0x9bd*0x1+-0x1*0x1d67)_0x26f45f[_0x2145df(0x36e)](parseInt(_0x41676a[_0x2145df(0x2a5)](_0x450bf1,_0x450bf1+(0x7*0x539+0x3cb*-0x2+-0x1*0x1cf7)),0x23fc+-0xa45+-0x3*0x88d));var _0x50f001=_0x3d66d8+(-0x1429+-0x69d*-0x1+0x371*0x4),_0x23f32e=parseInt(_0x41676a[_0x2145df(0x2a5)](_0x50f001,_0x50f001+(-0x179c+-0x3f+-0x61*-0x3f)),-0x2a*0x65+-0x20e5+0x3187);for(_0x50f001+=-0x20c6+0x3*-0x1a5+0x25b9,_0x450bf1=-0x2*-0x766+0x127e+-0x214a;_0x450bf1<_0x23f32e;++_0x450bf1){var _0x37a7c1=_0x467cb0(_0x41676a,_0x50f001);_0x50f001+=(0xcfd+0x399*-0x3+-0x7*0x50)*_0x37a7c1[-0x14ee+-0x912+0x1e00];for(var _0x13d988='',_0x4b0c3a=-0x1bcf+0x16cf+0x10*0x50;_0x4b0c3a<_0x37a7c1[-0x476*0x3+-0x112b+0x2*0xf47];++_0x4b0c3a){var _0x49abb4=_0x467cb0(_0x41676a,_0x50f001);_0x13d988+=String[_0x2145df(0x1e2)](_0x55d729^_0x49abb4[0x18f1*-0x1+-0x17e5*0x1+-0x30d7*-0x1]),_0x50f001+=(0x2dd*-0xd+-0x8*0x227+0x3673)*_0x49abb4[-0xd9+0x1702+-0x763*0x3];}_0x408609['push'](_0x13d988);}return _0x3f9548['p']=null,function _0x970e7(_0x3e87c6,_0x536685,_0xecd4ef,_0x5acf67,_0x560641){var _0x261736=_0x2145df,_0x1fc1ed,_0x281ec0,_0x5c0f33,_0xd55c82,_0x37b70b,_0x12cd72=-(0x1bdd+-0x8*0x116+-0x3*0x664),_0x27fcf3=[],_0x42ef1a=[-0x5*-0x2b6+0x1d6c+-0x2afa,null],_0x428c87=null,_0x4444cf=[_0x536685];for(_0x281ec0=Math['min'](_0x536685[_0x261736(0x259)],_0xecd4ef),_0x5c0f33=-0x90b+-0x1*0x1e2f+0x273a;_0x5c0f33<_0x281ec0;++_0x5c0f33)_0x4444cf[_0x261736(0x36e)](_0x536685[_0x5c0f33]);_0x4444cf['p']=_0x5acf67;for(var _0x4b7ccd=[];;)try{var _0x5cd5b9=_0x26f45f[_0x3e87c6++];if(_0x5cd5b9<-0x788+0x18c7+0x1*-0x1118){if(_0x5cd5b9<0x208d+-0x1031+-0x1049*0x1){if(_0x5cd5b9<0x1a1c+-0xbff*0x3+0x27a*0x4)_0x5cd5b9<0x3ee+0x1*0x21c3+-0x2e6*0xd?_0x27fcf3[++_0x12cd72]=_0x5cd5b9<-0x61*0x61+0x1115+0x13ad||-0x1d*-0x4f+0x11c9+0x3*-0x8e9!==_0x5cd5b9&&null:_0x5cd5b9<-0x783+0xa0d+-0x285?-0xb*0x24f+0x141a+0x54e===_0x5cd5b9?(_0x1fc1ed=_0x26f45f[_0x3e87c6++],_0x27fcf3[++_0x12cd72]=_0x1fc1ed<<0x12b*0x1f+0x5*0x70c+-0x4759>>-0x352+0x7*0x58f+-0x237f):(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0xb02+0x2af+-0xda9)+_0x26f45f[_0x3e87c6+(0x220e+0x1*-0x1175+-0x1098)],_0x3e87c6+=-0x325*0x3+0xafe+-0x18d,_0x27fcf3[++_0x12cd72]=_0x1fc1ed<<-0xe7c*-0x1+-0xe17*-0x2+-0x2a9a>>-0x23ae*0x1+-0x1f8f+-0x434d*-0x1):-0xa*0x15c+0x24bc+-0x1*0x171f===_0x5cd5b9?(_0x1fc1ed=((_0x1fc1ed=((_0x1fc1ed=_0x26f45f[_0x3e87c6++])<<-0x1f87+-0xda8+0x2d37)+_0x26f45f[_0x3e87c6++])<<-0x1d32+-0x2*0x124+0x1f82)+_0x26f45f[_0x3e87c6++],_0x27fcf3[++_0x12cd72]=(_0x1fc1ed<<0xc*-0x8+-0x25f7+0x265f)+_0x26f45f[_0x3e87c6++]):(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x1bf7+0x4e9*0x7+-0xc76*0x5)+_0x26f45f[_0x3e87c6+(0xba7+-0x1611+0xa6b)],_0x3e87c6+=0xa1e+-0x1a21+0x557*0x3,_0x27fcf3[++_0x12cd72]=+_0x408609[_0x1fc1ed]);else{if(_0x5cd5b9<0x369*-0x9+0x20de+-0x220)_0x5cd5b9<-0x2626+0x336*-0x5+0x363f?0x12b*-0x15+-0x2*-0x11f5+0x5ae*-0x2===_0x5cd5b9?(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x8*0x2+0x35*0x18+-0x500)+_0x26f45f[_0x3e87c6+(-0x330+-0xec*-0x5+-0x16b)],_0x3e87c6+=0x224+-0x19*-0xb3+-0x1*0x139d,_0x27fcf3[++_0x12cd72]=_0x408609[_0x1fc1ed]):_0x27fcf3[++_0x12cd72]=void(0x5*0x1ef+0x2493*-0x1+-0x35d*-0x8):0x1abb+0x191*0xd+-0x3*0xfaf===_0x5cd5b9?_0x27fcf3[++_0x12cd72]=_0x560641:(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<-0x20b0+-0x3da+0x2492)+_0x26f45f[_0x3e87c6+(-0x129d*0x1+0x1*-0x2316+0x35b4)],_0x3e87c6+=-0xcb8+0x1bb*-0xa+-0x2*-0xf04,_0x12cd72=_0x12cd72-_0x1fc1ed+(0xb3*0x1+-0x2018*0x1+0x1*0x1f66),_0x281ec0=_0x27fcf3[_0x261736(0x2a5)](_0x12cd72,_0x12cd72+_0x1fc1ed),_0x27fcf3[_0x12cd72]=_0x281ec0);else{if(_0x5cd5b9<0x2*-0xf38+0xdd0+0x10b1)-0x15d2+0x26f0+0x11*-0x101===_0x5cd5b9?_0x27fcf3[++_0x12cd72]={}:(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<-0x9ac+0x1201*0x1+-0x84d)+_0x26f45f[_0x3e87c6+(0x1ecb+-0x1bdd+0x7*-0x6b)],_0x3e87c6+=0x6a4+-0x922+0x280,_0x281ec0=_0x408609[_0x1fc1ed],_0x5c0f33=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72][_0x281ec0]=_0x5c0f33);else{if(0x17*-0xc3+0x1da5+0x3*-0x405===_0x5cd5b9){for(_0x281ec0=_0x26f45f[_0x3e87c6++],_0x5c0f33=_0x26f45f[_0x3e87c6++],_0xd55c82=_0x4444cf;_0x281ec0>-0x3*0x4ba+-0x12*0xb1+0x1aa0;--_0x281ec0)_0xd55c82=_0xd55c82['p'];_0x27fcf3[++_0x12cd72]=_0xd55c82[_0x5c0f33];}else _0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x15c3+-0x453+-0x1168)+_0x26f45f[_0x3e87c6+(-0x1f3f*0x1+0x1f6a+0x6*-0x7)],_0x3e87c6+=0x1*-0x1f4e+-0x118f*0x1+0x30df,_0x281ec0=_0x408609[_0x1fc1ed],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72][_0x281ec0];}}}}else{if(_0x5cd5b9<-0xf5c+-0x6d7+0x164e){if(_0x5cd5b9<0xc8*-0x2f+0xee*-0x9+-0xf*-0x303){if(_0x5cd5b9<0x1*-0x2426+0x224e+0x11*0x1d){if(-0xb15+0x2d5*0x1+-0x853*-0x1===_0x5cd5b9)_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72][_0x281ec0];else{for(_0x281ec0=_0x26f45f[_0x3e87c6++],_0x5c0f33=_0x26f45f[_0x3e87c6++],_0xd55c82=_0x4444cf;_0x281ec0>0x2*-0x11b8+0x240a+-0x9a;--_0x281ec0)_0xd55c82=_0xd55c82['p'];_0xd55c82[_0x5c0f33]=_0x27fcf3[_0x12cd72--];}}else 0x1*0x17f1+0x115*-0x9+0x2d3*-0x5===_0x5cd5b9?(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x16ff+0x158a+-0x2c81*0x1)+_0x26f45f[_0x3e87c6+(0x88f*-0x3+0x1c82+0x16a*-0x2)],_0x3e87c6+=0x1*-0xa7+0x18d4+0x1*-0x182b,_0x281ec0=_0x408609[_0x1fc1ed],_0x5c0f33=_0x27fcf3[_0x12cd72--],_0xd55c82=_0x27fcf3[_0x12cd72--],_0x5c0f33[_0x281ec0]=_0xd55c82):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x5c0f33=_0x27fcf3[_0x12cd72--],_0xd55c82=_0x27fcf3[_0x12cd72--],_0x5c0f33[_0x281ec0]=_0xd55c82);}else{if(_0x5cd5b9<-0x19f3+0x1c6b+-0x25f){if(0x1b*-0xbd+-0x84a*-0x2+-0x126*-0x3===_0x5cd5b9){for(_0x281ec0=_0x26f45f[_0x3e87c6++],_0x5c0f33=_0x26f45f[_0x3e87c6++],_0xd55c82=_0x4444cf,_0xd55c82=_0x4444cf;_0x281ec0>0x3*-0x65a+0xe28+0x4e6;--_0x281ec0)_0xd55c82=_0xd55c82['p'];_0x27fcf3[++_0x12cd72]=_0xd55c82,_0x27fcf3[++_0x12cd72]=_0x5c0f33;}else _0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]+=_0x281ec0;}else 0x44b*-0x9+0x61*-0xa+-0x2a86*-0x1===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]-=_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]*=_0x281ec0);}}else _0x5cd5b9<0x3*-0x7ce+0x907*-0x1+-0x1*-0x2094?_0x5cd5b9<0xa68+-0xc5f+0x214?0x3e*-0x7d+0x13d1+0xa90===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]/=_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]%=_0x281ec0):-0x171d*-0x1+-0x6a2*-0x3+-0x2ae6===_0x5cd5b9?_0x27fcf3[_0x12cd72]=-_0x27fcf3[_0x12cd72]:(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x5c0f33=_0x27fcf3[_0x12cd72--],_0x27fcf3[++_0x12cd72]=_0x5c0f33[_0x281ec0]++):_0x5cd5b9<0x2565+-0x2*-0xd76+-0x1bc*0x25?0x951+0x13da+-0x1d08===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]==_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]!=_0x281ec0):-0x144c*-0x1+0xd01+-0x2128===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]===_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]!==_0x281ec0);}}else{if(_0x5cd5b9<-0x3bb+0xdb6+-0x9c2*0x1)_0x5cd5b9<-0xe16+-0x2*-0x409+0x633?_0x5cd5b9<0x1*0xeea+-0x3*0x863+0x2b*0x3e?_0x5cd5b9<-0x2f*-0x15+0x185*0x2+-0x35e*0x2?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]<_0x281ec0):-0x2152+-0x1853+0x39ce===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]>_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]>=_0x281ec0):_0x5cd5b9<0x3*-0x822+0xc76+0x1bb*0x7?0x20ae+-0x21ca+-0x6d*-0x3===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]<<_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]>>_0x281ec0):-0x10*0x1a+-0x1431+0xa*0x233===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]>>>_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]&_0x281ec0):_0x5cd5b9<0x1*0x1ca7+0x1ea1*0x1+-0x31c*0x13?_0x5cd5b9<-0xa63*-0x2+-0x114d+-0x1*0x347?-0x12b5+-0x153*0x4+0x8*0x306===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]|_0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]^_0x281ec0):-0xab+0x10c1+0x9*-0x1c4===_0x5cd5b9?_0x27fcf3[_0x12cd72]=!_0x27fcf3[_0x12cd72]:(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x9*-0xd0+-0x1*-0xa04+0x2*-0x156)+_0x26f45f[_0x3e87c6+(0xa6a+0x113c+0x1ba5*-0x1)])<<0x2*-0x41b+-0x1bf1+0x7f*0x49>>-0x1bc0+0x232e+-0x75e,_0x3e87c6+=-0x62*0x31+0x26b*0x7+0x1d7*0x1,_0x27fcf3[_0x12cd72]?--_0x12cd72:_0x3e87c6+=_0x1fc1ed):_0x5cd5b9<0x8*0x493+-0x6c*0x49+0x1a*-0x37?-0x21*0x1+-0x3*-0xc19+0x2*-0x11fb===_0x5cd5b9?(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<-0x2*0x11ff+0x9d3+0x1a33)+_0x26f45f[_0x3e87c6+(0x1ba6+-0x134f*-0x1+-0x2ef4)])<<0x10dc*-0x1+-0x1130+0x221c>>-0x5*0x685+0x1a23+0x686,_0x3e87c6+=0x4*0xf9+-0x21c5+0x1de3,_0x27fcf3[_0x12cd72]?_0x3e87c6+=_0x1fc1ed:--_0x12cd72):(_0x281ec0=_0x27fcf3[_0x12cd72--],(_0x5c0f33=_0x27fcf3[_0x12cd72--])[_0x281ec0]=_0x27fcf3[_0x12cd72]):-0x5*-0x65c+0x15*0x17b+-0x3ead===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]in _0x281ec0):(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]=_0x27fcf3[_0x12cd72]instanceof _0x281ec0);else{if(_0x5cd5b9<0x52+0x7*0x102+-0x2*0x38f){if(_0x5cd5b9<-0x1887+-0x303+0x1bc7)_0x5cd5b9<0x2132+-0x592*0x2+-0x15d3?-0x136b+-0x21cd+0x3571*0x1===_0x5cd5b9?(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x5c0f33=_0x27fcf3[_0x12cd72--],_0x27fcf3[++_0x12cd72]=delete _0x5c0f33[_0x281ec0]):_0x27fcf3[_0x12cd72]=typeof _0x27fcf3[_0x12cd72]:0xeed*0x1+-0xa1e+0x2*-0x24a===_0x5cd5b9?(_0x1fc1ed=_0x26f45f[_0x3e87c6++],_0x281ec0=_0x27fcf3[_0x12cd72--],(_0x5c0f33=function _0x3b7712(){var _0x295f58=_0x3b7712['_u'],_0x29116a=_0x3b7712['_v'];return _0x295f58(_0x29116a[0x28c*0x4+-0x6*0x1d6+0xd4],arguments,_0x29116a[0x1d72*0x1+0x7b*-0x1b+-0x1078],_0x29116a[0x11e6+0x1a1f*-0x1+0x83b],this);})['_v']=[_0x281ec0,_0x1fc1ed,_0x4444cf],_0x5c0f33['_u']=_0x970e7,_0x27fcf3[++_0x12cd72]=_0x5c0f33):(_0x1fc1ed=_0x26f45f[_0x3e87c6++],_0x281ec0=_0x27fcf3[_0x12cd72--],(_0xd55c82=[_0x5c0f33=function _0x465123(){var _0x2f6d57=_0x465123['_u'],_0x399ae1=_0x465123['_v'];return _0x2f6d57(_0x399ae1[-0x113b+0xb*0x71+0xc60],arguments,_0x399ae1[0xa13+0x24b0+-0x9*0x532],_0x399ae1[0x1*0x463+-0x56*-0x49+-0x1ce7],this);}])['p']=_0x4444cf,_0x5c0f33['_v']=[_0x281ec0,_0x1fc1ed,_0xd55c82],_0x5c0f33['_u']=_0x970e7,_0x27fcf3[++_0x12cd72]=_0x5c0f33);else{if(_0x5cd5b9<0x62*-0xd+0xae3+-0x5a9)0x9d1*-0x1+0x2425+-0x1*0x1a17===_0x5cd5b9?(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0xd1*0x2+-0xe5*0x7+0x4a9)+_0x26f45f[_0x3e87c6+(0x33a+-0xb14+0x7db*0x1)])<<-0xb*-0xde+0x1199+-0x1b13>>-0xa3d+0xc*0x2aa+-0x15ab,_0x3e87c6+=-0x2f*0x3+0x2*0x9a3+-0x12b7,(_0x281ec0=_0x4b7ccd[_0x4b7ccd['length']-(0x427+0xa54+-0xda*0x11)])[0x5d4+0x14f2+-0x4d*0x59]=_0x3e87c6+_0x1fc1ed):(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x349*-0x2+0xa4*-0x17+0x1556)+_0x26f45f[_0x3e87c6+(-0x86*-0x2+-0x1c9+-0xbe*-0x1)])<<0x14bf+-0x1aec+-0x1*-0x63d>>0x2*0x17b+-0x1e3c+0x1b56,_0x3e87c6+=-0x1*-0x1052+0x10*-0x17e+0x790,(_0x281ec0=_0x4b7ccd[_0x4b7ccd['length']-(-0x1ad2+0x1b9*0x2+0x39*0x69)])&&!_0x281ec0[-0x3*0xe8+-0x5*-0x142+-0x1*0x391]?(_0x281ec0[0x25*-0x7a+0x21ec+-0x104a]=0x7bf+-0x3*-0xa43+-0x2685,_0x281ec0[_0x261736(0x36e)](_0x3e87c6)):_0x4b7ccd[_0x261736(0x36e)]([-0x1d*0x82+-0x11f4+0x20af,0x15ac+-0x399*-0x9+-0x360d,_0x3e87c6]),_0x3e87c6+=_0x1fc1ed);else{if(0x2263*-0x1+-0x689+0x292c===_0x5cd5b9)throw _0x281ec0=_0x27fcf3[_0x12cd72--];if(_0x5c0f33=(_0x281ec0=_0x4b7ccd['pop']())[0x1*0x6a1+0x1ceb+-0x238c],_0xd55c82=_0x42ef1a[-0x1b8+0x1d25+-0x3eb*0x7],0x751*0x1+-0xa4b+0x7*0x6d===_0x5c0f33)_0x3e87c6=_0x281ec0[0x1*0x8e+0x128c+-0x1319];else{if(0x11f4+0x3*0x95+-0x13b3===_0x5c0f33){if(-0x2607+-0xdc8+-0x1145*-0x3===_0xd55c82)_0x3e87c6=_0x281ec0[-0x262+-0x12bf*-0x2+0x13*-0x1d9];else{if(0x6d*0x2+0x2ff*-0x9+0x1a1e!==_0xd55c82)throw _0x42ef1a[0x3*0xced+0x1dd7+0x16df*-0x3];if(!_0x428c87)return _0x42ef1a[0x21b7+0x20a5*-0x1+-0x5b*0x3];_0x3e87c6=_0x428c87[-0x4bd+-0x3*-0x353+0x53b*-0x1],_0x560641=_0x428c87[0x149e+0x1*-0x751+-0xd4b],_0x4444cf=_0x428c87[-0x2*0x94d+-0x7bf*-0x1+0x2*0x56f],_0x4b7ccd=_0x428c87[0x15c3*0x1+-0x4*0x82e+-0x35*-0x35],_0x27fcf3[++_0x12cd72]=_0x42ef1a[0x3*0xaf3+-0x983+-0x1755],_0x42ef1a=[-0x1c1c+-0x14c*0x1a+0x3dd4,null],_0x428c87=_0x428c87[0x1a3+0x1*-0x1e23+0x1c80];}}else _0x3e87c6=_0x281ec0[-0x1*-0x953+0x159*0x1+-0xaaa],_0x281ec0[0x26ad*-0x1+0x2*-0xe4b+0x4343]=0x100d+-0x19cc+0x5*0x1f3,_0x4b7ccd[_0x261736(0x36e)](_0x281ec0);}}}}else{if(_0x5cd5b9<0x195+-0x3b*-0x9d+-0x3*0xc7f){if(_0x5cd5b9<0xd*-0x1+0x229+-0x1d8){if(-0x949+0x13f8+-0xa6d===_0x5cd5b9){for(_0x281ec0=_0x27fcf3[_0x12cd72--],_0x5c0f33=null;_0xd55c82=_0x4b7ccd[_0x261736(0x267)]();)if(0x1e97+0x31*-0x5e+-0xc97===_0xd55c82[0x150+0x6*0x645+-0x26ee]||0xb29*-0x3+-0x333*0x3+0x2b17===_0xd55c82[0x1*-0x22c2+-0x187b+0x3b3d]){_0x5c0f33=_0xd55c82;break;}if(_0x5c0f33)_0x42ef1a=[-0x11*0x14d+0x1492*0x1+0x18c,_0x281ec0],_0x3e87c6=_0x5c0f33[0xc28*-0x3+-0x8*0x4e1+0x4b82],_0x5c0f33[0x51e*0x1+0x755*-0x5+0x1f8b]=0x1875*0x1+0x146+-0x19bb,_0x4b7ccd[_0x261736(0x36e)](_0x5c0f33);else{if(!_0x428c87)return _0x281ec0;_0x3e87c6=_0x428c87[0x1*0x15be+-0x2639+0x107c],_0x560641=_0x428c87[-0x1*0xe1e+0x2042+0x2*-0x911],_0x4444cf=_0x428c87[0x25fd+0x1cfe+-0x42f8],_0x4b7ccd=_0x428c87[0x3*0xa76+0x17ce+-0x372c],_0x27fcf3[++_0x12cd72]=_0x281ec0,_0x42ef1a=[-0x7*0x4a6+-0x12d3+-0x3*-0x111f,null],_0x428c87=_0x428c87[-0x29*0x1e+0x9d*-0x2f+0x21a1];}}else _0x12cd72-=_0x1fc1ed=_0x26f45f[_0x3e87c6++],_0x5c0f33=_0x27fcf3['slice'](_0x12cd72+(0xf4f*-0x1+0x1af9*0x1+-0xba9),_0x12cd72+_0x1fc1ed+(0x230+0x1e17*-0x1+0x6fa*0x4)),_0x281ec0=_0x27fcf3[_0x12cd72--],_0xd55c82=_0x27fcf3[_0x12cd72--],_0x281ec0['_u']===_0x970e7?(_0x281ec0=_0x281ec0['_v'],_0x428c87=[_0x428c87,_0x3e87c6,_0x560641,_0x4444cf,_0x4b7ccd],_0x3e87c6=_0x281ec0[0x190c+0x3*0x89b+0x32dd*-0x1],null==_0xd55c82&&(_0xd55c82=(function(){return this;}())),_0x560641=_0xd55c82,(_0x4444cf=[_0x5c0f33]['concat'](_0x5c0f33))[_0x261736(0x259)]=Math[_0x261736(0x20c)](_0x281ec0[-0x1*0x170b+-0x936+0x1*0x2042],_0x1fc1ed)+(0x3*-0x4fa+0x21ed+-0x12fe),_0x4444cf['p']=_0x281ec0[-0x16*-0x1af+0x35f*0x1+0x2867*-0x1],_0x4b7ccd=[]):_0x27fcf3[++_0x12cd72]=_0x281ec0[_0x261736(0x207)](_0xd55c82,_0x5c0f33);}else{if(0x60a+-0xf05*-0x1+0x14cb*-0x1===_0x5cd5b9){for(_0x1fc1ed=_0x26f45f[_0x3e87c6++],_0xd55c82=[void(-0x1b5d+0x22a1+0x26c*-0x3)],_0x37b70b=_0x1fc1ed;_0x37b70b>0x1*0xd7e+-0x25f1+0x1873;--_0x37b70b)_0xd55c82[_0x37b70b]=_0x27fcf3[_0x12cd72--];_0x5c0f33=_0x27fcf3[_0x12cd72--],_0x281ec0=Function['bind'][_0x261736(0x207)](_0x5c0f33,_0xd55c82),_0x27fcf3[++_0x12cd72]=new _0x281ec0();}else _0x3e87c6+=(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x1f7+0x1*-0x7d0+0x7*0xd7)+_0x26f45f[_0x3e87c6+(0x264a+0xc3c+-0x3285)])<<0x4*0x3ee+0x45*0x51+-0x15*0x1c9>>0x5*0x199+-0x24f*-0x2+-0xc8b)+(-0x1f3*0x6+0x24f7+-0x1943);}}else _0x5cd5b9<-0x45d*-0x2+-0x1e51+0x15e0?0x125b+0x198a+-0x2b9e===_0x5cd5b9?(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<-0x269*-0xe+0x1*-0x225+-0x1f91)+_0x26f45f[_0x3e87c6+(-0x23bd*-0x1+0x1ce2+0x12*-0x397)])<<0xc61+-0x133b+0x6ea>>0x10dc+-0x30a*0x1+-0xdc2,_0x3e87c6+=-0xc5*-0x9+-0x252f+0x1e44,(_0x281ec0=_0x27fcf3[_0x12cd72--])||(_0x3e87c6+=_0x1fc1ed)):(_0x1fc1ed=(_0x1fc1ed=(_0x26f45f[_0x3e87c6]<<0x55*0x14+0x1*0x1e18+-0x3*0xc3c)+_0x26f45f[_0x3e87c6+(-0x22a3+-0x5bf*-0x2+0x1726)])<<-0x98e*0x4+0x8a1*0x1+0x1da7>>-0x72*-0x26+0x2b*-0x51+-0x341,_0x3e87c6+=0x47*0x1d+0x137*-0x1d+0x1b32,_0x281ec0=_0x27fcf3[_0x12cd72--],_0x27fcf3[_0x12cd72]===_0x281ec0&&(--_0x12cd72,_0x3e87c6+=_0x1fc1ed)):-0xb*0x33d+0x23*-0xf2+0x44fe===_0x5cd5b9?--_0x12cd72:(_0x281ec0=_0x27fcf3[_0x12cd72],_0x27fcf3[++_0x12cd72]=_0x281ec0);}}}}catch(_0x111ea4){for(_0x42ef1a=[-0x392*-0x4+0x1a0+-0xfe8,null];(_0x1fc1ed=_0x4b7ccd[_0x261736(0x267)]())&&!_0x1fc1ed[0x126a+-0x1*0x227c+0x1012*0x1];);if(!_0x1fc1ed){_0x489bfb:for(;_0x428c87;){for(_0x281ec0=_0x428c87[0x10da*-0x1+-0x539+0x1617*0x1];_0x1fc1ed=_0x281ec0[_0x261736(0x267)]();)if(_0x1fc1ed[0xaf4*-0x2+-0x2*0x132d+-0x2*-0x1e21])break _0x489bfb;_0x428c87=_0x428c87[0x1381+0x193e+-0x4f*0x91];}if(!_0x428c87)throw _0x111ea4;_0x3e87c6=_0x428c87[0xb*-0x277+0x14ac+-0x226*-0x3],_0x560641=_0x428c87[0x153e+0x1fd*-0xf+0x897],_0x4444cf=_0x428c87[-0x1*-0xa2e+0xcd6+0x3*-0x7ab],_0x4b7ccd=_0x428c87[-0x2*0x1145+0x2*0x293+-0x3ad*-0x8],_0x428c87=_0x428c87[-0x8e7+0x247f+-0x1b98];}-0xe*0x277+-0x3db+-0x665*-0x6===(_0x281ec0=_0x1fc1ed[-0xe65+0x11d1+-0x36c])?(_0x3e87c6=_0x1fc1ed[-0x7*-0x1c+0x2fe*0xa+-0x1eae],_0x1fc1ed[0x3f5*0x1+0x9e4+0x1*-0xdd9]=-0x1622*-0x1+-0x1*-0x1736+-0x2d58,_0x4b7ccd['push'](_0x1fc1ed),_0x27fcf3[++_0x12cd72]=_0x111ea4):0x1a52+-0xfc4+0x2d*-0x3c===_0x281ec0?(_0x3e87c6=_0x1fc1ed[-0x19*0xfe+0x1f98+-0x6c8],_0x1fc1ed[0x5*-0x33f+0x11d*-0x1+0x1158]=0x3ac*0x1+0x2*-0x1300+0x152*0x1a,_0x4b7ccd['push'](_0x1fc1ed),_0x42ef1a=[0x8f*0x1d+-0x1c4b+0xc1b,_0x111ea4]):(_0x3e87c6=_0x1fc1ed[0x31f*-0x5+-0xae7+-0x1*-0x1a85],_0x1fc1ed[0x27*-0xb3+-0x186*0xa+0x2a81]=0xf9+0x164a+-0x1741,_0x4b7ccd[_0x261736(0x36e)](_0x1fc1ed),_0x27fcf3[++_0x12cd72]=_0x111ea4);}}(_0x537efa,[],-0x1e+0x2631+-0x2613,_0x3f9548,_0x39b0b8);}!function(_0xe67213,_0x19937e){var _0x40472c=w_0x25f3;'object'==typeof exports&&_0x40472c(0x384)!=typeof module?_0x19937e(exports):_0x40472c(0x1ee)==typeof define&&define['amd']?define([_0x40472c(0x1b8)],_0x19937e):_0x19937e((_0xe67213=_0x40472c(0x384)!=typeof globalThis?globalThis:_0xe67213||self)[_0x40472c(0x1f5)]={});}(this,function(_0x1d18f2){'use strict';var _0x5612de=w_0x25f3;function _0x137ba2(_0x383b51){var _0x382940=w_0x25f3,_0x2e23ce,_0x2c2004;function _0x3a4f3f(_0x5a726e,_0x520718){var _0x480686=w_0x25f3;try{var _0x4a8345=_0x383b51[_0x5a726e](_0x520718),_0x8d9d0a=_0x4a8345[_0x480686(0x2e4)],_0x32f534=_0x8d9d0a instanceof _0x59d886;Promise[_0x480686(0x278)](_0x32f534?_0x8d9d0a['v']:_0x8d9d0a)[_0x480686(0x1ed)](function(_0x5047be){var _0x2a7bf3=_0x480686;if(_0x32f534){var _0x1c15d7='return'===_0x5a726e?_0x2a7bf3(0x2fd):_0x2a7bf3(0x389);if(!_0x8d9d0a['k']||_0x5047be['done'])return _0x3a4f3f(_0x1c15d7,_0x5047be);_0x5047be=_0x383b51[_0x1c15d7](_0x5047be)[_0x2a7bf3(0x2e4)];}_0x396815(_0x4a8345[_0x2a7bf3(0x1e3)]?_0x2a7bf3(0x2fd):_0x2a7bf3(0x2f1),_0x5047be);},function(_0x55c002){var _0x11b83e=_0x480686;_0x3a4f3f(_0x11b83e(0x250),_0x55c002);});}catch(_0x5e07d1){_0x396815(_0x480686(0x250),_0x5e07d1);}}function _0x396815(_0x189a93,_0x2988d2){var _0x557617=w_0x25f3;switch(_0x189a93){case _0x557617(0x2fd):_0x2e23ce[_0x557617(0x278)]({'value':_0x2988d2,'done':!(-0x252f+0x1b60+0x9cf)});break;case'throw':_0x2e23ce[_0x557617(0x39b)](_0x2988d2);break;default:_0x2e23ce[_0x557617(0x278)]({'value':_0x2988d2,'done':!(-0x18ec+-0x12dc+0x2bc9)});}(_0x2e23ce=_0x2e23ce['next'])?_0x3a4f3f(_0x2e23ce[_0x557617(0x392)],_0x2e23ce[_0x557617(0x327)]):_0x2c2004=null;}this['_invoke']=function(_0xd1d2b8,_0x168b0a){return new Promise(function(_0x1a939a,_0x9b7633){var _0x1f7808=w_0x25f3,_0x270c76={'key':_0xd1d2b8,'arg':_0x168b0a,'resolve':_0x1a939a,'reject':_0x9b7633,'next':null};_0x2c2004?_0x2c2004=_0x2c2004[_0x1f7808(0x389)]=_0x270c76:(_0x2e23ce=_0x2c2004=_0x270c76,_0x3a4f3f(_0xd1d2b8,_0x168b0a));});},_0x382940(0x1ee)!=typeof _0x383b51[_0x382940(0x2fd)]&&(this[_0x382940(0x2fd)]=void(-0xe73+-0xe*0x24b+0x2e8d));}function _0x59d886(_0x53e3a8,_0x594492){this['v']=_0x53e3a8,this['k']=_0x594492;}function _0x1d9867(_0x38463f,_0x559d1b,_0x1b2c54,_0x595501){return{'getMetadata':function(_0x397823){var _0x2d4b6b=w_0x25f3;_0x1fca4f(_0x595501,_0x2d4b6b(0x349)),_0x525dc3(_0x397823);var _0x18296f=_0x38463f[_0x397823];if(void(-0x15d1+0x4d8+-0xb*-0x18b)!==_0x18296f){if(-0x1fa5*-0x1+0x6b8+-0x265c===_0x559d1b){var _0x192f00=_0x18296f[_0x2d4b6b(0x1d4)];if(void(-0x1*0x46d+-0x74*0x6+0x1f*0x3b)!==_0x192f00)return _0x192f00[_0x1b2c54];}else{if(0x409*0x8+0x53*-0x3e+-0xc2c===_0x559d1b){var _0x6d3fe1=_0x18296f[_0x2d4b6b(0x261)];if(void(0x1c7c+0x8b0+-0x7a*0x4e)!==_0x6d3fe1)return _0x6d3fe1[_0x2d4b6b(0x32b)](_0x1b2c54);}else{if(Object[_0x2d4b6b(0x3b8)][_0x2d4b6b(0x334)](_0x18296f,_0x2d4b6b(0x2ac)))return _0x18296f[_0x2d4b6b(0x2ac)];}}}},'setMetadata':function(_0x3ee519,_0x4e53ba){var _0x22e2d1=w_0x25f3;_0x1fca4f(_0x595501,_0x22e2d1(0x395)),_0x525dc3(_0x3ee519);var _0x39b490=_0x38463f[_0x3ee519];if(void(0x181*-0x1+0x102*0x9+-0x791)===_0x39b490&&(_0x39b490=_0x38463f[_0x3ee519]={}),-0x97e+-0x39*-0x22+0x11*0x1d===_0x559d1b){var _0x38c721=_0x39b490[_0x22e2d1(0x1d4)];void(-0x2f9*-0x7+0x8fe+-0x1dcd*0x1)===_0x38c721&&(_0x38c721=_0x39b490[_0x22e2d1(0x1d4)]={}),_0x38c721[_0x1b2c54]=_0x4e53ba;}else{if(-0x4dc+0xbb0+-0x6d2===_0x559d1b){var _0x4ca087=_0x39b490['priv'];void(0x29*0xb9+-0x24a+-0x1b57)===_0x4ca087&&(_0x4ca087=_0x39b490[_0x22e2d1(0x261)]=new Map()),_0x4ca087[_0x22e2d1(0x1e4)](_0x1b2c54,_0x4e53ba);}else _0x39b490[_0x22e2d1(0x2ac)]=_0x4e53ba;}}};}function _0x43bce4(_0x282431,_0x336297){var _0xd4a703=w_0x25f3,_0x2b5c4b=_0x282431[Symbol['metadata']||Symbol[_0xd4a703(0x31e)]('Symbol.metadata')],_0x191a7e=Object[_0xd4a703(0x388)](_0x336297);if(-0x22cd+-0x378+-0x65*-0x61!==_0x191a7e[_0xd4a703(0x259)]){for(var _0x434093=0x1d66+-0x2*0xa03+-0x960;_0x434093<_0x191a7e[_0xd4a703(0x259)];_0x434093++){var _0x720e9a=_0x191a7e[_0x434093],_0x14e145=_0x336297[_0x720e9a],_0x2965bc=_0x2b5c4b?_0x2b5c4b[_0x720e9a]:null,_0x530b43=_0x14e145[_0xd4a703(0x1d4)],_0x36f2c2=_0x2965bc?_0x2965bc[_0xd4a703(0x1d4)]:null;_0x530b43&&_0x36f2c2&&Object['setPrototypeOf'](_0x530b43,_0x36f2c2);var _0x1732ba=_0x14e145[_0xd4a703(0x261)];if(_0x1732ba){var _0x550c9f=Array[_0xd4a703(0x29c)](_0x1732ba['values']()),_0x5cb426=_0x2965bc?_0x2965bc[_0xd4a703(0x261)]:null;_0x5cb426&&(_0x550c9f=_0x550c9f[_0xd4a703(0x2b8)](_0x5cb426)),_0x14e145[_0xd4a703(0x261)]=_0x550c9f;}_0x2965bc&&Object[_0xd4a703(0x23b)](_0x14e145,_0x2965bc);}_0x2b5c4b&&Object['setPrototypeOf'](_0x336297,_0x2b5c4b),_0x282431[Symbol[_0xd4a703(0x3a3)]||Symbol['for']('Symbol.metadata')]=_0x336297;}}function _0x26f5a8(_0x4a45da,_0x3cff50){return function(_0x346098){var _0x33211b=w_0x25f3;_0x1fca4f(_0x3cff50,'addInitializer'),_0x3ccc16(_0x346098,_0x33211b(0x3ba)),_0x4a45da[_0x33211b(0x36e)](_0x346098);};}function _0x2bb58f(_0x256829,_0x48125d,_0x3ef27c,_0x2a2bd4,_0x3688dc,_0x348e43,_0x559d92,_0x5a7ba4,_0x9da1fe){var _0x2a79c7=w_0x25f3,_0x2e3577;switch(_0x348e43){case 0xf31+-0x3*0x2dd+-0x699:_0x2e3577=_0x2a79c7(0x1b9);break;case 0x269d+-0x1291+-0x140a:_0x2e3577=_0x2a79c7(0x25a);break;case 0x24f*-0x9+0x12b3+0x217:_0x2e3577=_0x2a79c7(0x181);break;case 0xee9+-0xe3b*0x1+-0xaa:_0x2e3577=_0x2a79c7(0x35d);break;default:_0x2e3577=_0x2a79c7(0x23c);}var _0x12a0c0,_0x433c00,_0x37f6a8={'kind':_0x2e3577,'name':_0x5a7ba4?'#'+_0x48125d:_0x48125d,'isStatic':_0x559d92,'isPrivate':_0x5a7ba4},_0x71731a={'v':!(-0x1b16*-0x1+0x1*0x1fe1+-0x2*0x1d7b)};if(0x15e0+-0xdfe+-0x7e2!==_0x348e43&&(_0x37f6a8['addInitializer']=_0x26f5a8(_0x3688dc,_0x71731a)),_0x5a7ba4){_0x12a0c0=-0x2*0xd69+0x2a1*0x1+0x1833,_0x433c00=Symbol(_0x48125d);var _0x18857b={};0x2b+0x9ef*0x1+-0x1*0xa1a===_0x348e43?(_0x18857b[_0x2a79c7(0x32b)]=_0x3ef27c[_0x2a79c7(0x32b)],_0x18857b['set']=_0x3ef27c['set']):-0x2*0xd06+0x1816+0x1f8===_0x348e43?_0x18857b[_0x2a79c7(0x32b)]=function(){var _0x14604b=_0x2a79c7;return _0x3ef27c[_0x14604b(0x2e4)];}:(0x6bc+0x1*0x2095+0x10*-0x275!==_0x348e43&&-0x1207+0x31f+0x13*0xc9!==_0x348e43||(_0x18857b[_0x2a79c7(0x32b)]=function(){var _0x290422=_0x2a79c7;return _0x3ef27c[_0x290422(0x32b)][_0x290422(0x334)](this);}),0x1296+-0x1481+0x1ec!==_0x348e43&&-0x1892+-0xfa7+0x283d*0x1!==_0x348e43||(_0x18857b[_0x2a79c7(0x1e4)]=function(_0x4e18b8){var _0x7e7883=_0x2a79c7;_0x3ef27c['set'][_0x7e7883(0x334)](this,_0x4e18b8);})),_0x37f6a8[_0x2a79c7(0x1d0)]=_0x18857b;}else _0x12a0c0=0xca0*0x1+0x83+-0x52*0x29,_0x433c00=_0x48125d;try{return _0x256829(_0x9da1fe,Object[_0x2a79c7(0x2a0)](_0x37f6a8,_0x1d9867(_0x2a2bd4,_0x12a0c0,_0x433c00,_0x71731a)));}finally{_0x71731a['v']=!(0x6ad+-0x17a9+0x10fc);}}function _0x1fca4f(_0x322983,_0x544667){var _0x43acb9=w_0x25f3;if(_0x322983['v'])throw new Error(_0x43acb9(0x214)+_0x544667+_0x43acb9(0x168));}function _0x525dc3(_0x3ef577){var _0x42d9e9=w_0x25f3;if(_0x42d9e9(0x2ed)!=typeof _0x3ef577)throw new TypeError(_0x42d9e9(0x2e7)+_0x3ef577);}function _0x3ccc16(_0x56cd04,_0x56ab29){var _0x12ff08=w_0x25f3;if('function'!=typeof _0x56cd04)throw new TypeError(_0x56ab29+_0x12ff08(0x2f8));}function _0x32dfd4(_0x43869e,_0x434307){var _0x3fc8b9=w_0x25f3,_0xd7b002=typeof _0x434307;if(-0x1116+0x183f+0x2*-0x394===_0x43869e){if(_0x3fc8b9(0x17d)!==_0xd7b002||null===_0x434307)throw new TypeError(_0x3fc8b9(0x1fd));void(-0x39e*-0x9+-0x1*0x1241+-0x7*0x20b)!==_0x434307[_0x3fc8b9(0x32b)]&&_0x3ccc16(_0x434307[_0x3fc8b9(0x32b)],_0x3fc8b9(0x170)),void(0xeff*0x1+0x2673+0x1*-0x3572)!==_0x434307[_0x3fc8b9(0x1e4)]&&_0x3ccc16(_0x434307[_0x3fc8b9(0x1e4)],_0x3fc8b9(0x231)),void(-0x1558+-0x1b41*0x1+0x57*0x8f)!==_0x434307['init']&&_0x3ccc16(_0x434307['init'],_0x3fc8b9(0x22f)),void(-0x148f+0xfa3*0x1+0x4ec)!==_0x434307[_0x3fc8b9(0x2d0)]&&_0x3ccc16(_0x434307[_0x3fc8b9(0x2d0)],'accessor.initializer');}else{if(_0x3fc8b9(0x1ee)!==_0xd7b002)throw new TypeError((0xf1a+0xce*0x1f+-0x280c===_0x43869e?_0x3fc8b9(0x23c):0x2428+-0x6*-0x635+-0x495c===_0x43869e?_0x3fc8b9(0x19c):_0x3fc8b9(0x25a))+_0x3fc8b9(0x2cd));}}function _0x372120(_0x40b476){var _0x3e830c=w_0x25f3,_0x49c5be;return null==(_0x49c5be=_0x40b476['init'])&&(_0x49c5be=_0x40b476[_0x3e830c(0x2d0)])&&_0x3e830c(0x384)!=typeof console&&console[_0x3e830c(0x3b0)](_0x3e830c(0x30b)),_0x49c5be;}function _0x481bfe(_0x40006c,_0xdbd444,_0x2c21df,_0x2a4c98,_0x438b52,_0x13e34d,_0x5ae545,_0xa813f0,_0x4c4bfe){var _0x2ee2fe=w_0x25f3,_0x2f4463,_0x470ce0,_0x5e0119,_0x2b261c,_0x52c40b,_0x4e02cf,_0x1fa0af=_0x2c21df[-0x987+0x27*-0xdf+-0x40*-0xae];if(_0x5ae545?_0x2f4463=-0xa89*0x1+-0xd71+0x17fa===_0x438b52||-0x272*-0xd+-0x1c*0xb2+-0xc51===_0x438b52?{'get':_0x2c21df[0xefd+0x232b+-0x3225],'set':_0x2c21df[0x21b5*-0x1+-0x1015+0x31ce]}:0x17ef+-0x1d3+0x1619*-0x1===_0x438b52?{'get':_0x2c21df[-0x1e92+0xff*-0xe+0x2c87]}:-0x2211+0x1568+0xcad===_0x438b52?{'set':_0x2c21df[-0xcd2+0x126e+0x1*-0x599]}:{'value':_0x2c21df[0x2293+-0x1815+-0xa7b*0x1]}:0x23f1+-0x93b*-0x4+0x1*-0x48dd!==_0x438b52&&(_0x2f4463=Object[_0x2ee2fe(0x2a6)](_0xdbd444,_0x2a4c98)),-0x9a5+0x2cc*-0x5+-0xb*-0x226===_0x438b52?_0x5e0119={'get':_0x2f4463['get'],'set':_0x2f4463[_0x2ee2fe(0x1e4)]}:-0xb5c+0x210d+-0x1ab*0xd===_0x438b52?_0x5e0119=_0x2f4463[_0x2ee2fe(0x2e4)]:-0x12e2*-0x2+0x1*0x815+-0x2dd6===_0x438b52?_0x5e0119=_0x2f4463['get']:0x6*0x182+0x15fd+-0x1f05*0x1===_0x438b52&&(_0x5e0119=_0x2f4463[_0x2ee2fe(0x1e4)]),_0x2ee2fe(0x1ee)==typeof _0x1fa0af)void(0x12b3*-0x2+0x1229*0x1+0x133d)!==(_0x2b261c=_0x2bb58f(_0x1fa0af,_0x2a4c98,_0x2f4463,_0xa813f0,_0x4c4bfe,_0x438b52,_0x13e34d,_0x5ae545,_0x5e0119))&&(_0x32dfd4(_0x438b52,_0x2b261c),-0x655+-0xb75+0x9*0x1fa===_0x438b52?_0x470ce0=_0x2b261c:0x2239+-0x9*-0x42b+-0x47bb===_0x438b52?(_0x470ce0=_0x372120(_0x2b261c),_0x52c40b=_0x2b261c[_0x2ee2fe(0x32b)]||_0x5e0119[_0x2ee2fe(0x32b)],_0x4e02cf=_0x2b261c['set']||_0x5e0119['set'],_0x5e0119={'get':_0x52c40b,'set':_0x4e02cf}):_0x5e0119=_0x2b261c);else for(var _0x5801c7=_0x1fa0af['length']-(0x2435+0x18d0+0x16*-0x2c6);_0x5801c7>=0x3ab+0x58e*-0x6+0x1da9;_0x5801c7--){var _0x2c9ee5;void(0x11*-0x22+-0x29*-0x45+0x8cb*-0x1)!==(_0x2b261c=_0x2bb58f(_0x1fa0af[_0x5801c7],_0x2a4c98,_0x2f4463,_0xa813f0,_0x4c4bfe,_0x438b52,_0x13e34d,_0x5ae545,_0x5e0119))&&(_0x32dfd4(_0x438b52,_0x2b261c),-0x220e+0x3*0x4d5+0x138f===_0x438b52?_0x2c9ee5=_0x2b261c:-0x2*-0x1281+0x48f*0x7+-0x44ea===_0x438b52?(_0x2c9ee5=_0x372120(_0x2b261c),_0x52c40b=_0x2b261c[_0x2ee2fe(0x32b)]||_0x5e0119[_0x2ee2fe(0x32b)],_0x4e02cf=_0x2b261c[_0x2ee2fe(0x1e4)]||_0x5e0119[_0x2ee2fe(0x1e4)],_0x5e0119={'get':_0x52c40b,'set':_0x4e02cf}):_0x5e0119=_0x2b261c,void(0x204c+-0x2618+0x7*0xd4)!==_0x2c9ee5&&(void(0x3b3*0x9+-0x2709+0x69*0xe)===_0x470ce0?_0x470ce0=_0x2c9ee5:_0x2ee2fe(0x1ee)==typeof _0x470ce0?_0x470ce0=[_0x470ce0,_0x2c9ee5]:_0x470ce0['push'](_0x2c9ee5)));}if(-0x236c+0xbb8+0x17b4===_0x438b52||0x1*0x13e5+-0x73*-0xd+-0x1*0x19bb===_0x438b52){if(void(0x213e+0xef+-0x222d)===_0x470ce0)_0x470ce0=function(_0x2f3a47,_0x29873e){return _0x29873e;};else{if(_0x2ee2fe(0x1ee)!=typeof _0x470ce0){var _0x41d3b0=_0x470ce0;_0x470ce0=function(_0x434b4b,_0x5f49e4){var _0x463980=_0x2ee2fe;for(var _0x99d6c0=_0x5f49e4,_0x29ba97=-0x120f*0x1+0x1241+-0x1*0x32;_0x29ba97<_0x41d3b0['length'];_0x29ba97++)_0x99d6c0=_0x41d3b0[_0x29ba97][_0x463980(0x334)](_0x434b4b,_0x99d6c0);return _0x99d6c0;};}else{var _0x58c0cd=_0x470ce0;_0x470ce0=function(_0x15ad86,_0x1885c8){return _0x58c0cd['call'](_0x15ad86,_0x1885c8);};}}_0x40006c[_0x2ee2fe(0x36e)](_0x470ce0);}0x1*-0x241+0xf4f+-0xd0e!==_0x438b52&&(0x1*0x10e7+-0x47c+-0x2*0x635===_0x438b52?(_0x2f4463[_0x2ee2fe(0x32b)]=_0x5e0119[_0x2ee2fe(0x32b)],_0x2f4463[_0x2ee2fe(0x1e4)]=_0x5e0119[_0x2ee2fe(0x1e4)]):-0x53*0x6b+0x16e*0x8+0x1743===_0x438b52?_0x2f4463[_0x2ee2fe(0x2e4)]=_0x5e0119:0x4*-0x4cd+-0x1*-0x211a+0x1*-0xde3===_0x438b52?_0x2f4463['get']=_0x5e0119:-0x1d4b*0x1+0x1853+0x4fc===_0x438b52&&(_0x2f4463[_0x2ee2fe(0x1e4)]=_0x5e0119),_0x5ae545?0x5*-0x28+0xcfd+-0xc34*0x1===_0x438b52?(_0x40006c[_0x2ee2fe(0x36e)](function(_0x21a97b,_0x5eb78e){var _0x40880e=_0x2ee2fe;return _0x5e0119[_0x40880e(0x32b)][_0x40880e(0x334)](_0x21a97b,_0x5eb78e);}),_0x40006c[_0x2ee2fe(0x36e)](function(_0x2fa7d1,_0x120ab2){var _0x14b22a=_0x2ee2fe;return _0x5e0119[_0x14b22a(0x1e4)][_0x14b22a(0x334)](_0x2fa7d1,_0x120ab2);})):-0x4*0x64c+-0x1*0xe54+0x13c3*0x2===_0x438b52?_0x40006c['push'](_0x5e0119):_0x40006c[_0x2ee2fe(0x36e)](function(_0xcb08af,_0x156de1){var _0x327ef4=_0x2ee2fe;return _0x5e0119[_0x327ef4(0x334)](_0xcb08af,_0x156de1);}):Object[_0x2ee2fe(0x175)](_0xdbd444,_0x2a4c98,_0x2f4463));}function _0xa6bc9e(_0x4ceb2c,_0x1b99e7,_0xa8afec,_0x19fdc6,_0x501635){var _0x4c0532=w_0x25f3;for(var _0xcd77fa,_0x29d8ee,_0x1865b0=new Map(),_0x4b9277=new Map(),_0xbb3086=0x8*0x5e+0x1f79+-0x17*0x17f;_0xbb3086<_0x501635[_0x4c0532(0x259)];_0xbb3086++){var _0x58c865=_0x501635[_0xbb3086];if(Array['isArray'](_0x58c865)){var _0x203b06,_0x4069ae,_0x479925,_0x2ccf6e=_0x58c865[-0x3f4+-0x1cc4+-0x1*-0x20b9],_0x138f67=_0x58c865[0x17b7+0x1*0x1525+-0x2cda],_0xf00e58=_0x58c865[_0x4c0532(0x259)]>-0x9f5+-0xd*-0x103+-0x5*0xa3,_0x356344=_0x2ccf6e>=-0x14cc+-0xe02+0x6f7*0x5;if(_0x356344?(_0x203b06=_0x1b99e7,_0x4069ae=_0x19fdc6,0x7*-0x3c7+-0x225e+0x3ccf!=(_0x2ccf6e-=-0xdf*0x17+-0xb0c+0x1f1a)&&(_0x479925=_0x29d8ee=_0x29d8ee||[])):(_0x203b06=_0x1b99e7[_0x4c0532(0x344)],_0x4069ae=_0xa8afec,-0xc0c+0x1a8e+-0x2*0x741!==_0x2ccf6e&&(_0x479925=_0xcd77fa=_0xcd77fa||[])),-0x135b+0xba6+-0x7b5*-0x1!==_0x2ccf6e&&!_0xf00e58){var _0x5ef9f5=_0x356344?_0x4b9277:_0x1865b0,_0x4ff161=_0x5ef9f5[_0x4c0532(0x32b)](_0x138f67)||0x2*0x27b+0x137*0x1d+-0x2831;if(!(-0xea4+0x2178+0x96a*-0x2)===_0x4ff161||-0x1dce+0x245d+-0x346*0x2===_0x4ff161&&-0x6*-0x38f+-0x4*0x179+0xf72*-0x1!==_0x2ccf6e||0x1265+-0x40*0x6a+-0x21*-0x3f===_0x4ff161&&-0x14bf+-0x1fa6+0x3468!==_0x2ccf6e)throw new Error(_0x4c0532(0x351)+_0x138f67);!_0x4ff161&&_0x2ccf6e>-0x895*0x1+0x9fa+-0x163?_0x5ef9f5[_0x4c0532(0x1e4)](_0x138f67,_0x2ccf6e):_0x5ef9f5['set'](_0x138f67,!(-0x1a*0x12+0xa1*0x1d+-0x1069*0x1));}_0x481bfe(_0x4ceb2c,_0x203b06,_0x58c865,_0x138f67,_0x2ccf6e,_0x356344,_0xf00e58,_0x4069ae,_0x479925);}}_0x3657e2(_0x4ceb2c,_0xcd77fa),_0x3657e2(_0x4ceb2c,_0x29d8ee);}function _0x3657e2(_0x27f72c,_0x8206e1){var _0x4690f1=w_0x25f3;_0x8206e1&&_0x27f72c[_0x4690f1(0x36e)](function(_0x5a459a){var _0x25d832=_0x4690f1;for(var _0x3abe8d=-0x2d8+-0x38c+0x664;_0x3abe8d<_0x8206e1['length'];_0x3abe8d++)_0x8206e1[_0x3abe8d][_0x25d832(0x334)](_0x5a459a);return _0x5a459a;});}function _0x5f3676(_0x5c13b5,_0x4779ee,_0x3479c8,_0x3cb840){var _0x5df6f5=w_0x25f3;if(_0x3cb840['length']>-0x1744+-0xfb*0xd+0x2403){for(var _0x3c4c21=[],_0x504436=_0x4779ee,_0x1b5d8b=_0x4779ee['name'],_0x38689d=_0x3cb840[_0x5df6f5(0x259)]-(-0x1f5e+0x1e2c*0x1+0x133);_0x38689d>=0x878+-0x1269*0x2+0x1c5a;_0x38689d--){var _0x2bbf37={'v':!(0x1*-0x17dc+-0x1f4*-0x10+-0x3d*0x1f)};try{var _0x175a61=Object['assign']({'kind':_0x5df6f5(0x19c),'name':_0x1b5d8b,'addInitializer':_0x26f5a8(_0x3c4c21,_0x2bbf37)},_0x1d9867(_0x3479c8,-0x1dfa+-0x1517+0x3311,_0x1b5d8b,_0x2bbf37)),_0x23fff7=_0x3cb840[_0x38689d](_0x504436,_0x175a61);}finally{_0x2bbf37['v']=!(0x1*-0x1e5d+-0xba7*0x2+0x35ab);}void(-0x2bf+-0x103d*-0x1+-0xd7e)!==_0x23fff7&&(_0x32dfd4(0xcf3+-0x9f8+-0x2f1,_0x23fff7),_0x504436=_0x23fff7);}_0x5c13b5[_0x5df6f5(0x36e)](_0x504436,function(){var _0x52b1c0=_0x5df6f5;for(var _0x4f0e95=0x18*0x57+0x9f2*0x3+-0x25fe;_0x4f0e95<_0x3c4c21[_0x52b1c0(0x259)];_0x4f0e95++)_0x3c4c21[_0x4f0e95][_0x52b1c0(0x334)](_0x504436);});}}function _0x51be3f(_0x44772e,_0x5243ca,_0x398fa6){var _0x234a54=w_0x25f3,_0x13ba59=[],_0x3f8aa9={},_0x129506={};return _0xa6bc9e(_0x13ba59,_0x44772e,_0x129506,_0x3f8aa9,_0x5243ca),_0x43bce4(_0x44772e[_0x234a54(0x344)],_0x129506),_0x5f3676(_0x13ba59,_0x44772e,_0x3f8aa9,_0x398fa6),_0x43bce4(_0x44772e,_0x3f8aa9),_0x13ba59;}function _0x281b3b(){function _0x556aac(_0xe6f04c,_0x2cf155){return function(_0x4ecc37){var _0x253daa=w_0x25f3;!function(_0x109a83,_0x5991fe){var _0x11a2fc=w_0x25f3;if(_0x109a83['v'])throw new Error(_0x11a2fc(0x257));}(_0x2cf155),_0x3020d2(_0x4ecc37,_0x253daa(0x3ba)),_0xe6f04c[_0x253daa(0x36e)](_0x4ecc37);};}function _0x193050(_0xc0995a,_0x3ee2bb,_0x30e0b6,_0xba208c,_0x262641,_0x4fa001,_0x1bb0d5,_0xa6e531){var _0x208bac=w_0x25f3,_0x59dc77;switch(_0x262641){case 0x1af6+-0xd13+-0xde2:_0x59dc77='accessor';break;case 0xfda+-0x760+-0x878:_0x59dc77=_0x208bac(0x25a);break;case 0x1a*0xb9+0xc3f+-0x1f06:_0x59dc77='getter';break;case-0xb42*0x3+-0x12d9+0x34a3:_0x59dc77='setter';break;default:_0x59dc77=_0x208bac(0x23c);}var _0x1b169f,_0x3fad4b,_0x1050cb={'kind':_0x59dc77,'name':_0x1bb0d5?'#'+_0x3ee2bb:_0x3ee2bb,'static':_0x4fa001,'private':_0x1bb0d5},_0x252772={'v':!(0x5*0x4d5+-0x1*0x1d6e+0x546)};-0x21ff+-0x127a+-0x3479*-0x1!==_0x262641&&(_0x1050cb['addInitializer']=_0x556aac(_0xba208c,_0x252772)),-0x2c9+-0x27+0x2f0===_0x262641?_0x1bb0d5?(_0x1b169f=_0x30e0b6['get'],_0x3fad4b=_0x30e0b6[_0x208bac(0x1e4)]):(_0x1b169f=function(){return this[_0x3ee2bb];},_0x3fad4b=function(_0x417555){this[_0x3ee2bb]=_0x417555;}):0x1*-0x1d61+-0x1*0x841+-0xc8c*-0x3===_0x262641?_0x1b169f=function(){var _0x195f9b=_0x208bac;return _0x30e0b6[_0x195f9b(0x2e4)];}:(0x1*-0x1e49+0x1*0x1eef+0x37*-0x3!==_0x262641&&0x1*0x204a+-0x137*-0xb+-0x2da4!==_0x262641||(_0x1b169f=function(){var _0x3ad118=_0x208bac;return _0x30e0b6[_0x3ad118(0x32b)][_0x3ad118(0x334)](this);}),0x729+-0x7ae+0x1*0x86!==_0x262641&&-0xa*-0x161+-0x512+-0x2*0x45a!==_0x262641||(_0x3fad4b=function(_0x37b522){var _0x599416=_0x208bac;_0x30e0b6[_0x599416(0x1e4)]['call'](this,_0x37b522);})),_0x1050cb[_0x208bac(0x1d0)]=_0x1b169f&&_0x3fad4b?{'get':_0x1b169f,'set':_0x3fad4b}:_0x1b169f?{'get':_0x1b169f}:{'set':_0x3fad4b};try{return _0xc0995a(_0xa6e531,_0x1050cb);}finally{_0x252772['v']=!(-0x1*0x1ecf+0x1cfc+0x1d3);}}function _0x3020d2(_0x5684cc,_0x512b77){var _0x3b7e7a=w_0x25f3;if(_0x3b7e7a(0x1ee)!=typeof _0x5684cc)throw new TypeError(_0x512b77+'\x20must\x20be\x20a\x20function');}function _0x13d7df(_0x22787a,_0x4a8dfe){var _0x994eff=w_0x25f3,_0x751eff=typeof _0x4a8dfe;if(-0x690+0x133f*-0x2+0x2d0f===_0x22787a){if(_0x994eff(0x17d)!==_0x751eff||null===_0x4a8dfe)throw new TypeError(_0x994eff(0x1fd));void(-0x2af+0x838+-0xd*0x6d)!==_0x4a8dfe[_0x994eff(0x32b)]&&_0x3020d2(_0x4a8dfe[_0x994eff(0x32b)],_0x994eff(0x170)),void(0x1b*-0x43+-0x24c8+0x2bd9)!==_0x4a8dfe[_0x994eff(0x1e4)]&&_0x3020d2(_0x4a8dfe[_0x994eff(0x1e4)],_0x994eff(0x231)),void(-0x2a4+0x35*-0x1d+0x8a5)!==_0x4a8dfe[_0x994eff(0x3b9)]&&_0x3020d2(_0x4a8dfe[_0x994eff(0x3b9)],_0x994eff(0x22f));}else{if('function'!==_0x751eff)throw new TypeError((-0x800+0xd*0x75+0x20f===_0x22787a?_0x994eff(0x23c):0x13ad+0x1db0+-0x3153===_0x22787a?_0x994eff(0x19c):_0x994eff(0x25a))+'\x20decorators\x20must\x20return\x20a\x20function\x20or\x20void\x200');}}function _0x3345ab(_0xc8cad8,_0x5d015f,_0x32f33f,_0x5f5169,_0x23451f,_0x2595f8,_0xb8bdee,_0x2c3fb2){var _0x1d74f6=w_0x25f3,_0x584e33,_0x103b18,_0x5d6523,_0x2d18a1,_0x2b9fd3,_0x11479e,_0x4ef3d6=_0x32f33f[0x23c3+-0x1a88+0x1*-0x93b];if(_0xb8bdee?_0x584e33=0x7ed*0x1+0xac9+-0x12b6===_0x23451f||0x4*0x7f5+0x11ab+0x712*-0x7===_0x23451f?{'get':_0x32f33f[0x265e*0x1+-0x23e1+-0x1*0x27a],'set':_0x32f33f[0x108e+-0x2551+0x1b*0xc5]}:0x19bc+0x1c0d*0x1+0x35c6*-0x1===_0x23451f?{'get':_0x32f33f[-0x7fd*0x4+0x1b6c+0x48b]}:0x149a+0x2522+-0x39b8===_0x23451f?{'set':_0x32f33f[-0x1*0x3ad+-0xb4e*0x2+-0x9*-0x2ec]}:{'value':_0x32f33f[0x21a6+-0x7+-0x219c]}:0x12ab*0x1+-0x1f41+0xc96!==_0x23451f&&(_0x584e33=Object[_0x1d74f6(0x2a6)](_0x5d015f,_0x5f5169)),0x11*-0xf7+-0x4*0x7be+0x2f60===_0x23451f?_0x5d6523={'get':_0x584e33[_0x1d74f6(0x32b)],'set':_0x584e33['set']}:0x1e3+0x2*0x11ef+-0x25bf===_0x23451f?_0x5d6523=_0x584e33[_0x1d74f6(0x2e4)]:-0x6b+0x1ff8+-0xfc5*0x2===_0x23451f?_0x5d6523=_0x584e33['get']:0x2577+-0x2272+-0x301===_0x23451f&&(_0x5d6523=_0x584e33[_0x1d74f6(0x1e4)]),'function'==typeof _0x4ef3d6)void(0x1bdc*-0x1+0x2343+-0x1*0x767)!==(_0x2d18a1=_0x193050(_0x4ef3d6,_0x5f5169,_0x584e33,_0x2c3fb2,_0x23451f,_0x2595f8,_0xb8bdee,_0x5d6523))&&(_0x13d7df(_0x23451f,_0x2d18a1),-0x5*-0x27e+0x6a*0x1e+-0x18e2===_0x23451f?_0x103b18=_0x2d18a1:0x23f1+-0x4*0x8bb+0x14*-0xd===_0x23451f?(_0x103b18=_0x2d18a1['init'],_0x2b9fd3=_0x2d18a1[_0x1d74f6(0x32b)]||_0x5d6523[_0x1d74f6(0x32b)],_0x11479e=_0x2d18a1[_0x1d74f6(0x1e4)]||_0x5d6523[_0x1d74f6(0x1e4)],_0x5d6523={'get':_0x2b9fd3,'set':_0x11479e}):_0x5d6523=_0x2d18a1);else for(var _0x4328fb=_0x4ef3d6[_0x1d74f6(0x259)]-(0xe31+-0xc5b+-0x1d5);_0x4328fb>=0x87a+-0xf7c+0x702;_0x4328fb--){var _0x5c4cc8;void(0x83b+-0x8af+0x74)!==(_0x2d18a1=_0x193050(_0x4ef3d6[_0x4328fb],_0x5f5169,_0x584e33,_0x2c3fb2,_0x23451f,_0x2595f8,_0xb8bdee,_0x5d6523))&&(_0x13d7df(_0x23451f,_0x2d18a1),-0x4*0x511+-0xc0a*-0x1+0x83a===_0x23451f?_0x5c4cc8=_0x2d18a1:-0x22*-0x4d+-0x135d*-0x2+0x1051*-0x3===_0x23451f?(_0x5c4cc8=_0x2d18a1[_0x1d74f6(0x3b9)],_0x2b9fd3=_0x2d18a1[_0x1d74f6(0x32b)]||_0x5d6523[_0x1d74f6(0x32b)],_0x11479e=_0x2d18a1[_0x1d74f6(0x1e4)]||_0x5d6523[_0x1d74f6(0x1e4)],_0x5d6523={'get':_0x2b9fd3,'set':_0x11479e}):_0x5d6523=_0x2d18a1,void(0x23b9*0x1+0x2*-0xccb+-0x5*0x207)!==_0x5c4cc8&&(void(-0x15ac+0x376+0x7e*0x25)===_0x103b18?_0x103b18=_0x5c4cc8:_0x1d74f6(0x1ee)==typeof _0x103b18?_0x103b18=[_0x103b18,_0x5c4cc8]:_0x103b18['push'](_0x5c4cc8)));}if(-0x11a3+0xd*-0x2f2+-0x67*-0x8b===_0x23451f||0xeab*-0x1+0x752*-0x1+-0x15fe*-0x1===_0x23451f){if(void(-0x4a8*0x1+0xfb0+-0xb08)===_0x103b18)_0x103b18=function(_0x1682da,_0x55e4de){return _0x55e4de;};else{if(_0x1d74f6(0x1ee)!=typeof _0x103b18){var _0x278a7c=_0x103b18;_0x103b18=function(_0x1c32a2,_0x442b39){var _0x56cde3=_0x1d74f6;for(var _0x200043=_0x442b39,_0x52eaec=0xc8a+0x26f6+0x20*-0x19c;_0x52eaec<_0x278a7c[_0x56cde3(0x259)];_0x52eaec++)_0x200043=_0x278a7c[_0x52eaec]['call'](_0x1c32a2,_0x200043);return _0x200043;};}else{var _0x120537=_0x103b18;_0x103b18=function(_0x5a0b99,_0x523966){var _0x2de49d=_0x1d74f6;return _0x120537[_0x2de49d(0x334)](_0x5a0b99,_0x523966);};}}_0xc8cad8['push'](_0x103b18);}0x3ac*-0x8+0x915+0x144b!==_0x23451f&&(-0xcde+-0x1f3f+0x2c1e===_0x23451f?(_0x584e33[_0x1d74f6(0x32b)]=_0x5d6523['get'],_0x584e33[_0x1d74f6(0x1e4)]=_0x5d6523['set']):-0x31*-0xc1+-0x1*-0x2388+-0x4877===_0x23451f?_0x584e33[_0x1d74f6(0x2e4)]=_0x5d6523:-0x4*0x899+0x1944+0x923===_0x23451f?_0x584e33[_0x1d74f6(0x32b)]=_0x5d6523:-0x64e*0x5+0x152f+0xa5b===_0x23451f&&(_0x584e33[_0x1d74f6(0x1e4)]=_0x5d6523),_0xb8bdee?0x186b+0x3*0x7e9+-0x1ed*0x19===_0x23451f?(_0xc8cad8[_0x1d74f6(0x36e)](function(_0x1c7663,_0x210e49){var _0xb35933=_0x1d74f6;return _0x5d6523['get'][_0xb35933(0x334)](_0x1c7663,_0x210e49);}),_0xc8cad8[_0x1d74f6(0x36e)](function(_0x9ce4a0,_0xa2a6a5){var _0x548bf5=_0x1d74f6;return _0x5d6523[_0x548bf5(0x1e4)][_0x548bf5(0x334)](_0x9ce4a0,_0xa2a6a5);})):-0x2*0x91d+-0x226e+0x282*0x15===_0x23451f?_0xc8cad8['push'](_0x5d6523):_0xc8cad8[_0x1d74f6(0x36e)](function(_0x5b9e9b,_0x4ef49d){return _0x5d6523['call'](_0x5b9e9b,_0x4ef49d);}):Object[_0x1d74f6(0x175)](_0x5d015f,_0x5f5169,_0x584e33));}function _0xbbd38b(_0x42478e,_0x1ef3db){var _0x15d90a=w_0x25f3;_0x1ef3db&&_0x42478e[_0x15d90a(0x36e)](function(_0xdfc40e){var _0x10548c=_0x15d90a;for(var _0x3e6f2c=-0x349*-0xa+0xe*0x5b+-0x25d4;_0x3e6f2c<_0x1ef3db[_0x10548c(0x259)];_0x3e6f2c++)_0x1ef3db[_0x3e6f2c][_0x10548c(0x334)](_0xdfc40e);return _0xdfc40e;});}return function(_0x2327f5,_0x28ca06,_0x53ed99){var _0x253a06=[];return function(_0x1816e5,_0x29e49e,_0x48cb46){var _0x5efef1=w_0x25f3;for(var _0x5857a9,_0x48222e,_0x2f7979=new Map(),_0x2e733e=new Map(),_0x4b6d1a=0x1*-0xec3+0x1*0x6a2+0x821;_0x4b6d1a<_0x48cb46[_0x5efef1(0x259)];_0x4b6d1a++){var _0x58c6d3=_0x48cb46[_0x4b6d1a];if(Array['isArray'](_0x58c6d3)){var _0x124b63,_0x4324aa,_0x8c2a39=_0x58c6d3[-0x1d3a+-0x24ff+0x423a],_0x8fabbd=_0x58c6d3[0x1b14+-0x1*-0x1f0c+-0x1d0f*0x2],_0x249da3=_0x58c6d3[_0x5efef1(0x259)]>0x1*0xb42+-0x1c1*0xe+-0x1*-0xd4f,_0x11d04c=_0x8c2a39>=-0x12b3*0x1+0x10e6+0x1d2;if(_0x11d04c?(_0x124b63=_0x29e49e,-0x15b*-0x5+0x373+-0xa3a!=(_0x8c2a39-=-0x24b2+-0x12b*0x17+0x3f94)&&(_0x4324aa=_0x48222e=_0x48222e||[])):(_0x124b63=_0x29e49e['prototype'],0x11fa+-0x1859+0x65f!==_0x8c2a39&&(_0x4324aa=_0x5857a9=_0x5857a9||[])),-0x11df+-0x205d*0x1+0x323c!==_0x8c2a39&&!_0x249da3){var _0x3e0748=_0x11d04c?_0x2e733e:_0x2f7979,_0x148749=_0x3e0748[_0x5efef1(0x32b)](_0x8fabbd)||0x6c0+0x8e3+0x1*-0xfa3;if(!(-0x288+-0x1b8f+0x1e17)===_0x148749||0xcae+-0x84a+-0x461===_0x148749&&0xebc+-0x6cf+-0x7e9!==_0x8c2a39||0x132d*0x2+0x1*0x6ab+-0x1*0x2d01===_0x148749&&0x2144+-0xe17*-0x1+-0x2f58!==_0x8c2a39)throw new Error('Attempted\x20to\x20decorate\x20a\x20public\x20method/accessor\x20that\x20has\x20the\x20same\x20name\x20as\x20a\x20previously\x20decorated\x20public\x20method/accessor.\x20This\x20is\x20not\x20currently\x20supported\x20by\x20the\x20decorators\x20plugin.\x20Property\x20name\x20was:\x20'+_0x8fabbd);!_0x148749&&_0x8c2a39>-0x2ea*-0xc+0x1*0x24cb+0x1*-0x47c1?_0x3e0748[_0x5efef1(0x1e4)](_0x8fabbd,_0x8c2a39):_0x3e0748[_0x5efef1(0x1e4)](_0x8fabbd,!(0xb9e+-0xc*-0x9d+-0x12fa));}_0x3345ab(_0x1816e5,_0x124b63,_0x58c6d3,_0x8fabbd,_0x8c2a39,_0x11d04c,_0x249da3,_0x4324aa);}}_0xbbd38b(_0x1816e5,_0x5857a9),_0xbbd38b(_0x1816e5,_0x48222e);}(_0x253a06,_0x2327f5,_0x28ca06),function(_0x528ea3,_0x4f695c,_0x1527df){var _0x436c1d=w_0x25f3;if(_0x1527df[_0x436c1d(0x259)]>0xe5+0x269c+-0x2781){for(var _0x4e0e1b=[],_0x3eca99=_0x4f695c,_0x5c69d2=_0x4f695c[_0x436c1d(0x341)],_0x2abebb=_0x1527df[_0x436c1d(0x259)]-(0x1*-0x1cbe+0xa*-0x2ba+0x3803);_0x2abebb>=0x8c0+-0x44d*0x4+0x874;_0x2abebb--){var _0x30122e={'v':!(0x26bb+0x34c*0x7+0x1*-0x3dce)};try{var _0x46e0b1=_0x1527df[_0x2abebb](_0x3eca99,{'kind':_0x436c1d(0x19c),'name':_0x5c69d2,'addInitializer':_0x556aac(_0x4e0e1b,_0x30122e)});}finally{_0x30122e['v']=!(0x114e+0x1f1*-0x1+0x1*-0xf5d);}void(-0x1*-0x1c41+0x38d*0x1+-0x1fce)!==_0x46e0b1&&(_0x13d7df(0x180+0x6*-0x2f+-0x5c,_0x46e0b1),_0x3eca99=_0x46e0b1);}_0x528ea3['push'](_0x3eca99,function(){var _0x1ef0f8=_0x436c1d;for(var _0x337d50=0x1*-0x217d+-0x1a35+-0x2*-0x1dd9;_0x337d50<_0x4e0e1b[_0x1ef0f8(0x259)];_0x337d50++)_0x4e0e1b[_0x337d50]['call'](_0x3eca99);});}}(_0x253a06,_0x2327f5,_0x53ed99),_0x253a06;};}var _0x15b960,_0x500e9f;function _0x4ab133(_0x538caa,_0x1dca3a,_0x362c83){return(_0x15b960=_0x15b960||_0x281b3b())(_0x538caa,_0x1dca3a,_0x362c83);}function _0x4591cd(){function _0x135cea(_0x43f619,_0x5dfa54){return function(_0x33b776){var _0x55feea=w_0x25f3;!function(_0x4d2467,_0x1aaab5){var _0x188bf8=w_0x25f3;if(_0x4d2467['v'])throw new Error(_0x188bf8(0x257));}(_0x5dfa54),_0x23bc0c(_0x33b776,_0x55feea(0x3ba)),_0x43f619[_0x55feea(0x36e)](_0x33b776);};}function _0x39ceae(_0x5713eb,_0x3cd64b,_0x2a5e05,_0x428f9b,_0x1cd0f5,_0x386fff,_0x54211b,_0x40ee96){var _0x32bd6c=w_0x25f3,_0x43d2aa;switch(_0x1cd0f5){case-0x3*-0x9f7+0x13bc+-0x31a0:_0x43d2aa=_0x32bd6c(0x1b9);break;case 0x14f5+-0x1f56+-0x1*-0xa63:_0x43d2aa=_0x32bd6c(0x25a);break;case-0x1*-0x346+-0xe+0x335*-0x1:_0x43d2aa=_0x32bd6c(0x181);break;case-0x2*-0x367+0x16b3+-0x1*0x1d7d:_0x43d2aa=_0x32bd6c(0x35d);break;default:_0x43d2aa=_0x32bd6c(0x23c);}var _0x1f76cf,_0x2d3a63,_0x52dc9f={'kind':_0x43d2aa,'name':_0x54211b?'#'+_0x3cd64b:_0x3cd64b,'static':_0x386fff,'private':_0x54211b},_0x33b885={'v':!(0x1*0xea1+0x22b7+0x3157*-0x1)};-0x948+-0x2454+0x1c*0x1a1!==_0x1cd0f5&&(_0x52dc9f[_0x32bd6c(0x225)]=_0x135cea(_0x428f9b,_0x33b885)),0xb*0xb1+0x1417+-0x1bb2===_0x1cd0f5?_0x54211b?(_0x1f76cf=_0x2a5e05[_0x32bd6c(0x32b)],_0x2d3a63=_0x2a5e05['set']):(_0x1f76cf=function(){return this[_0x3cd64b];},_0x2d3a63=function(_0x209e9f){this[_0x3cd64b]=_0x209e9f;}):0x240f+0xc11*0x3+-0x22*0x220===_0x1cd0f5?_0x1f76cf=function(){var _0x446fc0=_0x32bd6c;return _0x2a5e05[_0x446fc0(0x2e4)];}:(-0x4*-0x1f6+0x1b48+-0x231f!==_0x1cd0f5&&0x61*0x1a+-0x113*-0x11+-0x1c1a!==_0x1cd0f5||(_0x1f76cf=function(){var _0x447b7b=_0x32bd6c;return _0x2a5e05['get'][_0x447b7b(0x334)](this);}),0x1e4e+0x25b6*-0x1+0x769!==_0x1cd0f5&&0x1397+-0xb7e+-0x815!==_0x1cd0f5||(_0x2d3a63=function(_0x4c071d){_0x2a5e05['set']['call'](this,_0x4c071d);})),_0x52dc9f[_0x32bd6c(0x1d0)]=_0x1f76cf&&_0x2d3a63?{'get':_0x1f76cf,'set':_0x2d3a63}:_0x1f76cf?{'get':_0x1f76cf}:{'set':_0x2d3a63};try{return _0x5713eb(_0x40ee96,_0x52dc9f);}finally{_0x33b885['v']=!(-0x8c9+0x2*-0x1189+0x2bdb);}}function _0x23bc0c(_0x4aa0f2,_0x12640a){var _0x1b925e=w_0x25f3;if(_0x1b925e(0x1ee)!=typeof _0x4aa0f2)throw new TypeError(_0x12640a+_0x1b925e(0x2f8));}function _0x2de5e4(_0x3de3bb,_0xa398ec){var _0xf94308=w_0x25f3,_0x2c8661=typeof _0xa398ec;if(-0x33*0x4c+0x5*0x787+-0x1*0x167e===_0x3de3bb){if(_0xf94308(0x17d)!==_0x2c8661||null===_0xa398ec)throw new TypeError(_0xf94308(0x1fd));void(-0x1e03+-0x1eb+0xff7*0x2)!==_0xa398ec[_0xf94308(0x32b)]&&_0x23bc0c(_0xa398ec[_0xf94308(0x32b)],'accessor.get'),void(0x3ba+-0x24db+-0xb0b*-0x3)!==_0xa398ec[_0xf94308(0x1e4)]&&_0x23bc0c(_0xa398ec[_0xf94308(0x1e4)],_0xf94308(0x231)),void(0x7f8+-0x1b10+-0x1318*-0x1)!==_0xa398ec[_0xf94308(0x3b9)]&&_0x23bc0c(_0xa398ec['init'],_0xf94308(0x22f));}else{if(_0xf94308(0x1ee)!==_0x2c8661)throw new TypeError((-0xf65+0xbd0+0x395===_0x3de3bb?_0xf94308(0x23c):0x48e+-0x1*0x1ebe+0x1a3a===_0x3de3bb?_0xf94308(0x19c):_0xf94308(0x25a))+_0xf94308(0x2cd));}}function _0x343a1e(_0xc4b124,_0x1026d8,_0x47c875,_0x150a83,_0x4cdf11,_0x3e4a0b,_0x1dd313,_0x2b027d){var _0x1c87ab=w_0x25f3,_0x2f46a5,_0x5a2be0,_0x547138,_0x289168,_0x1e1872,_0xd2fd89,_0x17ee52=_0x47c875[-0x2099+-0x133*0x1+0x21cc];if(_0x1dd313?_0x2f46a5=0xb0a+0x2125+-0x2c2f===_0x4cdf11||0x12ea+0x5ba+0x385*-0x7===_0x4cdf11?{'get':_0x47c875[0x1cf3+0x7*-0x57d+0x97b],'set':_0x47c875[0x6*0x210+-0x2b3*0x3+0x1*-0x443]}:0x1f7b+-0x2*0x1253+0x52e*0x1===_0x4cdf11?{'get':_0x47c875[0x1476+0x23*-0xb5+0x44c]}:0x2290+-0x20e6+-0x1a6===_0x4cdf11?{'set':_0x47c875[0x1465*-0x1+-0x1*0x7d9+-0x1*-0x1c41]}:{'value':_0x47c875[-0x1550+0xb0f+0xa44]}:0x8bb+0x37b+0x1*-0xc36!==_0x4cdf11&&(_0x2f46a5=Object[_0x1c87ab(0x2a6)](_0x1026d8,_0x150a83)),-0xf*-0x49+-0x8c3*-0x1+0xd09*-0x1===_0x4cdf11?_0x547138={'get':_0x2f46a5[_0x1c87ab(0x32b)],'set':_0x2f46a5['set']}:-0x7c4+0x1da9+-0x15e3===_0x4cdf11?_0x547138=_0x2f46a5[_0x1c87ab(0x2e4)]:0x7e1+-0x159a+0xdbc===_0x4cdf11?_0x547138=_0x2f46a5[_0x1c87ab(0x32b)]:0x105+0x177d*0x1+0xbe*-0x21===_0x4cdf11&&(_0x547138=_0x2f46a5['set']),_0x1c87ab(0x1ee)==typeof _0x17ee52)void(-0x12c9+0x3*-0x2f+0x1e*0xa5)!==(_0x289168=_0x39ceae(_0x17ee52,_0x150a83,_0x2f46a5,_0x2b027d,_0x4cdf11,_0x3e4a0b,_0x1dd313,_0x547138))&&(_0x2de5e4(_0x4cdf11,_0x289168),-0x1cc9+-0x2*0xb5d+0x1*0x3383===_0x4cdf11?_0x5a2be0=_0x289168:-0x1*0x1517+0x25*0x44+0xb44===_0x4cdf11?(_0x5a2be0=_0x289168[_0x1c87ab(0x3b9)],_0x1e1872=_0x289168[_0x1c87ab(0x32b)]||_0x547138['get'],_0xd2fd89=_0x289168[_0x1c87ab(0x1e4)]||_0x547138[_0x1c87ab(0x1e4)],_0x547138={'get':_0x1e1872,'set':_0xd2fd89}):_0x547138=_0x289168);else for(var _0x5a46d9=_0x17ee52[_0x1c87ab(0x259)]-(-0x49*0x19+0x4a2+0x280);_0x5a46d9>=-0x225b+0x1562+0xcf9;_0x5a46d9--){var _0x60a0d7;void(-0xffd+0x7*-0x293+0x2202)!==(_0x289168=_0x39ceae(_0x17ee52[_0x5a46d9],_0x150a83,_0x2f46a5,_0x2b027d,_0x4cdf11,_0x3e4a0b,_0x1dd313,_0x547138))&&(_0x2de5e4(_0x4cdf11,_0x289168),0x52*-0x59+0x9*-0x104+0x2*0x12d3===_0x4cdf11?_0x60a0d7=_0x289168:-0x1689+0x1e96+-0x80c===_0x4cdf11?(_0x60a0d7=_0x289168['init'],_0x1e1872=_0x289168[_0x1c87ab(0x32b)]||_0x547138['get'],_0xd2fd89=_0x289168[_0x1c87ab(0x1e4)]||_0x547138[_0x1c87ab(0x1e4)],_0x547138={'get':_0x1e1872,'set':_0xd2fd89}):_0x547138=_0x289168,void(-0x1a77+-0x136f+-0xeb*-0x32)!==_0x60a0d7&&(void(-0x519+-0x1*-0x1245+-0x2*0x696)===_0x5a2be0?_0x5a2be0=_0x60a0d7:_0x1c87ab(0x1ee)==typeof _0x5a2be0?_0x5a2be0=[_0x5a2be0,_0x60a0d7]:_0x5a2be0[_0x1c87ab(0x36e)](_0x60a0d7)));}if(0x78c+0x1d71*-0x1+0x15e5*0x1===_0x4cdf11||-0x49*0x6+0x1e*0xd2+-0x1*0x16e5===_0x4cdf11){if(void(0x21db+0x22d*0xd+-0x3e24)===_0x5a2be0)_0x5a2be0=function(_0x5345b0,_0x1a659b){return _0x1a659b;};else{if('function'!=typeof _0x5a2be0){var _0x336daa=_0x5a2be0;_0x5a2be0=function(_0x30e05d,_0xf3b4df){var _0x4d9e7f=_0x1c87ab;for(var _0x362c52=_0xf3b4df,_0x22ed8a=0x1*0x1f3c+-0x610+-0x192c;_0x22ed8a<_0x336daa[_0x4d9e7f(0x259)];_0x22ed8a++)_0x362c52=_0x336daa[_0x22ed8a]['call'](_0x30e05d,_0x362c52);return _0x362c52;};}else{var _0x5aed3b=_0x5a2be0;_0x5a2be0=function(_0x5dc978,_0x5db160){return _0x5aed3b['call'](_0x5dc978,_0x5db160);};}}_0xc4b124[_0x1c87ab(0x36e)](_0x5a2be0);}0x177a+-0xe7*-0x1c+0x22*-0x16f!==_0x4cdf11&&(0x3af+0x9*-0x217+0x50b*0x3===_0x4cdf11?(_0x2f46a5[_0x1c87ab(0x32b)]=_0x547138[_0x1c87ab(0x32b)],_0x2f46a5[_0x1c87ab(0x1e4)]=_0x547138[_0x1c87ab(0x1e4)]):0x18a3*-0x1+0x263c+-0xd97===_0x4cdf11?_0x2f46a5[_0x1c87ab(0x2e4)]=_0x547138:-0x4*-0x31b+-0x837*0x4+-0x5*-0x417===_0x4cdf11?_0x2f46a5[_0x1c87ab(0x32b)]=_0x547138:-0x1a2e+0x2*0x131d+-0xc08===_0x4cdf11&&(_0x2f46a5[_0x1c87ab(0x1e4)]=_0x547138),_0x1dd313?-0x1*-0x1757+0x1ada+-0x3230===_0x4cdf11?(_0xc4b124[_0x1c87ab(0x36e)](function(_0xb886c1,_0x3a6bf5){var _0x288d67=_0x1c87ab;return _0x547138[_0x288d67(0x32b)][_0x288d67(0x334)](_0xb886c1,_0x3a6bf5);}),_0xc4b124[_0x1c87ab(0x36e)](function(_0x337452,_0x50608d){var _0x44b896=_0x1c87ab;return _0x547138[_0x44b896(0x1e4)][_0x44b896(0x334)](_0x337452,_0x50608d);})):-0x2*0x93d+0x297+-0x139*-0xd===_0x4cdf11?_0xc4b124['push'](_0x547138):_0xc4b124[_0x1c87ab(0x36e)](function(_0x1e24c7,_0x120c64){var _0x3e3294=_0x1c87ab;return _0x547138[_0x3e3294(0x334)](_0x1e24c7,_0x120c64);}):Object[_0x1c87ab(0x175)](_0x1026d8,_0x150a83,_0x2f46a5));}function _0x4c3a0d(_0x48207f,_0x257e35){var _0x1249e7=w_0x25f3;for(var _0x47fbbd,_0x402774,_0x333e3a=[],_0xe5fb1e=new Map(),_0x15f194=new Map(),_0x41a1c1=-0x1*0x575+-0x13c0+-0x1b*-0xef;_0x41a1c1<_0x257e35[_0x1249e7(0x259)];_0x41a1c1++){var _0x63ddf3=_0x257e35[_0x41a1c1];if(Array[_0x1249e7(0x2af)](_0x63ddf3)){var _0x446366,_0x186385,_0x19186f=_0x63ddf3[-0x5bc*0x4+0xf6a*-0x1+-0x3*-0xcc9],_0x4911c0=_0x63ddf3[0x1*0x46+-0xb73+0xb2f],_0x39f6a1=_0x63ddf3['length']>-0x4*-0x101+-0xe6c+0xa6b,_0x27b8e1=_0x19186f>=0xb76+0x73*-0x34+0x71*0x1b;if(_0x27b8e1?(_0x446366=_0x48207f,0x26c+-0x157d+-0x65b*-0x3!=(_0x19186f-=0x2324+0x27*0xbe+-0x4011)&&(_0x186385=_0x402774=_0x402774||[])):(_0x446366=_0x48207f[_0x1249e7(0x344)],-0x7*0x297+0x1b26+-0x905!==_0x19186f&&(_0x186385=_0x47fbbd=_0x47fbbd||[])),0x13d6*-0x1+0x1*0xbb7+0x81f!==_0x19186f&&!_0x39f6a1){var _0x3f295f=_0x27b8e1?_0x15f194:_0xe5fb1e,_0x566c7b=_0x3f295f[_0x1249e7(0x32b)](_0x4911c0)||-0x188*-0xb+0x3e*0x49+-0x2286;if(!(0x903*-0x1+-0x1f*-0x8d+0x1*-0x810)===_0x566c7b||-0x43*0x5+-0x1c6c+0x2f*0xa2===_0x566c7b&&-0x737+-0x1ac2+0x21fd!==_0x19186f||0xdd2+-0x202b+0x125d===_0x566c7b&&-0x1*0x118d+-0x2b8*-0x3+0x8*0x12d!==_0x19186f)throw new Error('Attempted\x20to\x20decorate\x20a\x20public\x20method/accessor\x20that\x20has\x20the\x20same\x20name\x20as\x20a\x20previously\x20decorated\x20public\x20method/accessor.\x20This\x20is\x20not\x20currently\x20supported\x20by\x20the\x20decorators\x20plugin.\x20Property\x20name\x20was:\x20'+_0x4911c0);!_0x566c7b&&_0x19186f>0xf*-0x26f+0x1*0x79d+0x1ce6*0x1?_0x3f295f[_0x1249e7(0x1e4)](_0x4911c0,_0x19186f):_0x3f295f['set'](_0x4911c0,!(0x4a*0x71+-0xad*0x2+0x6*-0x538));}_0x343a1e(_0x333e3a,_0x446366,_0x63ddf3,_0x4911c0,_0x19186f,_0x27b8e1,_0x39f6a1,_0x186385);}}return _0x45d367(_0x333e3a,_0x47fbbd),_0x45d367(_0x333e3a,_0x402774),_0x333e3a;}function _0x45d367(_0x59ffc8,_0x5ae59b){var _0x5b965e=w_0x25f3;_0x5ae59b&&_0x59ffc8[_0x5b965e(0x36e)](function(_0x3ed28e){var _0x5985fb=_0x5b965e;for(var _0x5bcc94=0x156a+0x18e0+-0x2e4a;_0x5bcc94<_0x5ae59b['length'];_0x5bcc94++)_0x5ae59b[_0x5bcc94][_0x5985fb(0x334)](_0x3ed28e);return _0x3ed28e;});}return function(_0x33b4ae,_0x339d11,_0x5ebc36){return{'e':_0x4c3a0d(_0x33b4ae,_0x339d11),get 'c'(){return function(_0x92474e,_0x343c16){var _0x3cc4d2=w_0x25f3;if(_0x343c16['length']>-0x271+-0x2690+0x2901){for(var _0x18f66c=[],_0x1dd6eb=_0x92474e,_0x1d8833=_0x92474e[_0x3cc4d2(0x341)],_0x163025=_0x343c16[_0x3cc4d2(0x259)]-(0x1*-0x20ed+0x1f94+0x2*0xad);_0x163025>=0xfba+0x22*0xb6+0x27e6*-0x1;_0x163025--){var _0x49ea6f={'v':!(-0x524+0x1a34+-0x150f)};try{var _0x181a9d=_0x343c16[_0x163025](_0x1dd6eb,{'kind':_0x3cc4d2(0x19c),'name':_0x1d8833,'addInitializer':_0x135cea(_0x18f66c,_0x49ea6f)});}finally{_0x49ea6f['v']=!(-0x23e+0x1*-0x7fb+0xa39);}void(-0x1*-0x253d+-0x23d+-0x80*0x46)!==_0x181a9d&&(_0x2de5e4(0x4c5+0xcaf+-0x2e7*0x6,_0x181a9d),_0x1dd6eb=_0x181a9d);}return[_0x1dd6eb,function(){var _0x543396=_0x3cc4d2;for(var _0x2a7175=-0xe71*-0x1+0x24c0+-0xa3d*0x5;_0x2a7175<_0x18f66c['length'];_0x2a7175++)_0x18f66c[_0x2a7175][_0x543396(0x334)](_0x1dd6eb);}];}}(_0x33b4ae,_0x5ebc36);}};};}function _0x49af1f(_0x3a86ea,_0x270f72,_0x219a67){return(_0x49af1f=_0x4591cd())(_0x3a86ea,_0x270f72,_0x219a67);}function _0x3e4ff5(_0x5b206b,_0xdfb3e7){return function(_0x19bdbd){var _0x1caf0f=w_0x25f3;_0x15eb90(_0xdfb3e7,_0x1caf0f(0x225)),_0x45d8b1(_0x19bdbd,_0x1caf0f(0x3ba)),_0x5b206b[_0x1caf0f(0x36e)](_0x19bdbd);};}function _0x19207d(_0x45ced6,_0xcee03f){var _0x41489e=w_0x25f3;if(!_0x45ced6(_0xcee03f))throw new TypeError(_0x41489e(0x255));}function _0x151762(_0x535a19,_0x4e0a69,_0x270f96,_0x52f7e9,_0x23d388,_0x49094d,_0x49f620,_0x251987,_0x40c067){var _0x3b49d7=w_0x25f3,_0x5d54d8;switch(_0x23d388){case-0x14b9+0x1dd5+-0x91b:_0x5d54d8=_0x3b49d7(0x1b9);break;case-0xe4a+0x12be+-0x472:_0x5d54d8=_0x3b49d7(0x25a);break;case-0x10f1*-0x2+0x179*0x19+-0x46b0:_0x5d54d8=_0x3b49d7(0x181);break;case 0x3*-0x5cf+0x778*0x1+0x353*0x3:_0x5d54d8=_0x3b49d7(0x35d);break;default:_0x5d54d8=_0x3b49d7(0x23c);}var _0x246ff5,_0x4e02d1,_0x45ccfd={'kind':_0x5d54d8,'name':_0x49f620?'#'+_0x4e0a69:_0x4e0a69,'static':_0x49094d,'private':_0x49f620},_0x4733a4={'v':!(-0xad5+0x1f*-0x59+-0x1f7*-0xb)};if(0xcf6+0x1e15+-0x3*0xe59!==_0x23d388&&(_0x45ccfd[_0x3b49d7(0x225)]=_0x3e4ff5(_0x52f7e9,_0x4733a4)),_0x49f620||-0x112e+-0x23ab+0x34d9!==_0x23d388&&0xa00+-0x234a+0x194c!==_0x23d388){if(-0x327*-0x2+-0xb00+0x4b4===_0x23d388)_0x246ff5=function(_0x36bc44){var _0x2d48fd=_0x3b49d7;return _0x19207d(_0x40c067,_0x36bc44),_0x270f96[_0x2d48fd(0x2e4)];};else{var _0x339c00=-0x1746+-0x1bb+0xad*0x25===_0x23d388||-0x859+0x4*-0xe2+-0x27*-0x4e===_0x23d388;(_0x339c00||0x5*-0x3bf+-0x1d90+0x304e===_0x23d388)&&(_0x246ff5=_0x49f620?function(_0x26da64){var _0x21bb3a=_0x3b49d7;return _0x19207d(_0x40c067,_0x26da64),_0x270f96['get'][_0x21bb3a(0x334)](_0x26da64);}:function(_0x2ac873){var _0xa2fd4f=_0x3b49d7;return _0x270f96[_0xa2fd4f(0x32b)][_0xa2fd4f(0x334)](_0x2ac873);}),(_0x339c00||-0x14c4*0x1+-0x231e+-0xb2e*-0x5===_0x23d388)&&(_0x4e02d1=_0x49f620?function(_0x1e61ca,_0x1c77b0){var _0x5f2230=_0x3b49d7;_0x19207d(_0x40c067,_0x1e61ca),_0x270f96[_0x5f2230(0x1e4)][_0x5f2230(0x334)](_0x1e61ca,_0x1c77b0);}:function(_0x4d5a08,_0x3ac6c6){var _0x1baec7=_0x3b49d7;_0x270f96[_0x1baec7(0x1e4)][_0x1baec7(0x334)](_0x4d5a08,_0x3ac6c6);});}}else _0x246ff5=function(_0x4ff2b5){return _0x4ff2b5[_0x4e0a69];},-0x20ec+0x12ee*0x2+-0x4f0===_0x23d388&&(_0x4e02d1=function(_0x2f8383,_0x4a22e5){_0x2f8383[_0x4e0a69]=_0x4a22e5;});var _0x557c63=_0x49f620?_0x40c067[_0x3b49d7(0x301)]():function(_0x2f4445){return _0x4e0a69 in _0x2f4445;};_0x45ccfd[_0x3b49d7(0x1d0)]=_0x246ff5&&_0x4e02d1?{'get':_0x246ff5,'set':_0x4e02d1,'has':_0x557c63}:_0x246ff5?{'get':_0x246ff5,'has':_0x557c63}:{'set':_0x4e02d1,'has':_0x557c63};try{return _0x535a19(_0x251987,_0x45ccfd);}finally{_0x4733a4['v']=!(0xea8+-0xe9f*0x1+0x1*-0x9);}}function _0x15eb90(_0x202051,_0x1c2373){var _0x51521b=w_0x25f3;if(_0x202051['v'])throw new Error('attempted\x20to\x20call\x20'+_0x1c2373+_0x51521b(0x168));}function _0x45d8b1(_0x2ad532,_0x423791){var _0x14562d=w_0x25f3;if(_0x14562d(0x1ee)!=typeof _0x2ad532)throw new TypeError(_0x423791+_0x14562d(0x2f8));}function _0xed525b(_0x522a86,_0x4177e1){var _0x442324=w_0x25f3,_0x399204=typeof _0x4177e1;if(-0x361+0x3*0x2c4+0x4ea*-0x1===_0x522a86){if(_0x442324(0x17d)!==_0x399204||null===_0x4177e1)throw new TypeError(_0x442324(0x1fd));void(-0x4a0+0x2e8+0x1b8)!==_0x4177e1[_0x442324(0x32b)]&&_0x45d8b1(_0x4177e1[_0x442324(0x32b)],_0x442324(0x170)),void(0xc4*-0x1d+0x651+0xfe3)!==_0x4177e1['set']&&_0x45d8b1(_0x4177e1[_0x442324(0x1e4)],_0x442324(0x231)),void(-0x168c+0x16b5*0x1+0x29*-0x1)!==_0x4177e1['init']&&_0x45d8b1(_0x4177e1[_0x442324(0x3b9)],_0x442324(0x22f));}else{if(_0x442324(0x1ee)!==_0x399204)throw new TypeError((0x20d7+0x7*-0x362+0x7*-0x14f===_0x522a86?_0x442324(0x23c):0x1*-0x1257+-0x2d9+-0x26*-0x8f===_0x522a86?'class':'method')+'\x20decorators\x20must\x20return\x20a\x20function\x20or\x20void\x200');}}function _0x244c39(_0x17683){return function(){return _0x17683(this);};}function _0x48532c(_0x5503e0){return function(_0x23d6a1){_0x5503e0(this,_0x23d6a1);};}function _0x23e6b(_0x5cf198,_0x5646db,_0x26f9f9,_0x37f54b,_0x47565f,_0x2aa948,_0xccabc1,_0x5c3a48,_0x9ffbdc){var _0x3e2adf=w_0x25f3,_0x592aeb,_0x365a6e,_0x4e8d10,_0x589008,_0x17d95a,_0x11c9ab,_0x9a7bbd=_0x26f9f9[0xa46+0xb3c+-0x1582];if(_0xccabc1?_0x592aeb=0x24*0x47+0xf85+-0x1981===_0x47565f||0x14ca+-0x1099+0x218*-0x2===_0x47565f?{'get':_0x244c39(_0x26f9f9[0x7*-0x2f0+0x227b+-0xde8]),'set':_0x48532c(_0x26f9f9[-0xad*-0x19+-0xc3c+0x29*-0x1d])}:0x1b99*-0x1+0x367+-0x1*-0x1835===_0x47565f?{'get':_0x26f9f9[0xb1*0x22+0x3*0xcef+-0x24*0x1bb]}:0x1*-0x10f+-0x1*-0x2023+-0x1f10===_0x47565f?{'set':_0x26f9f9[0x36*-0xa6+-0x1cb2+0x3fb9]}:{'value':_0x26f9f9[-0x7ba*0x1+0x1850+-0x1093]}:-0x2*0x713+0x26fe+0x27c*-0xa!==_0x47565f&&(_0x592aeb=Object[_0x3e2adf(0x2a6)](_0x5646db,_0x37f54b)),-0x1982+0x1947+0x14*0x3===_0x47565f?_0x4e8d10={'get':_0x592aeb[_0x3e2adf(0x32b)],'set':_0x592aeb[_0x3e2adf(0x1e4)]}:0xe9b+-0x2a3+0x5fb*-0x2===_0x47565f?_0x4e8d10=_0x592aeb['value']:-0x13*-0x5b+0x17cb+-0x1*0x1e89===_0x47565f?_0x4e8d10=_0x592aeb[_0x3e2adf(0x32b)]:0x7d7*-0x1+0x5*0x81+0x556===_0x47565f&&(_0x4e8d10=_0x592aeb['set']),'function'==typeof _0x9a7bbd)void(0x1*0x1ee3+0xd+-0x1ef0)!==(_0x589008=_0x151762(_0x9a7bbd,_0x37f54b,_0x592aeb,_0x5c3a48,_0x47565f,_0x2aa948,_0xccabc1,_0x4e8d10,_0x9ffbdc))&&(_0xed525b(_0x47565f,_0x589008),0xc91*-0x1+-0x893*-0x1+0x3fe===_0x47565f?_0x365a6e=_0x589008:-0x1*0x21dd+-0x109*-0x24+-0x366===_0x47565f?(_0x365a6e=_0x589008[_0x3e2adf(0x3b9)],_0x17d95a=_0x589008[_0x3e2adf(0x32b)]||_0x4e8d10[_0x3e2adf(0x32b)],_0x11c9ab=_0x589008['set']||_0x4e8d10[_0x3e2adf(0x1e4)],_0x4e8d10={'get':_0x17d95a,'set':_0x11c9ab}):_0x4e8d10=_0x589008);else for(var _0x4f1458=_0x9a7bbd['length']-(0x204c+0x277*0x7+-0xe*0x38a);_0x4f1458>=-0x23c2+0x91+0x2331;_0x4f1458--){var _0x189ac8;void(-0x18f8*-0x1+-0x143a+-0x1*0x4be)!==(_0x589008=_0x151762(_0x9a7bbd[_0x4f1458],_0x37f54b,_0x592aeb,_0x5c3a48,_0x47565f,_0x2aa948,_0xccabc1,_0x4e8d10,_0x9ffbdc))&&(_0xed525b(_0x47565f,_0x589008),-0x22cb+-0x2043*-0x1+0x288===_0x47565f?_0x189ac8=_0x589008:0x1bd*-0x3+0x283*0x8+0x2*-0x770===_0x47565f?(_0x189ac8=_0x589008[_0x3e2adf(0x3b9)],_0x17d95a=_0x589008[_0x3e2adf(0x32b)]||_0x4e8d10[_0x3e2adf(0x32b)],_0x11c9ab=_0x589008[_0x3e2adf(0x1e4)]||_0x4e8d10['set'],_0x4e8d10={'get':_0x17d95a,'set':_0x11c9ab}):_0x4e8d10=_0x589008,void(-0xaeb+0x1*0x1bb3+0x4*-0x432)!==_0x189ac8&&(void(-0x2359+-0x3*-0x323+0x19f0)===_0x365a6e?_0x365a6e=_0x189ac8:_0x3e2adf(0x1ee)==typeof _0x365a6e?_0x365a6e=[_0x365a6e,_0x189ac8]:_0x365a6e[_0x3e2adf(0x36e)](_0x189ac8)));}if(0x21ee+-0x129b+0xf53*-0x1===_0x47565f||-0x44d*-0x7+-0xa27+-0x13f3*0x1===_0x47565f){if(void(0x10*-0x218+-0x707*0x1+0x2887)===_0x365a6e)_0x365a6e=function(_0x56b70b,_0x5e4d55){return _0x5e4d55;};else{if(_0x3e2adf(0x1ee)!=typeof _0x365a6e){var _0x3101a1=_0x365a6e;_0x365a6e=function(_0x449bbf,_0x5196d8){var _0x360050=_0x3e2adf;for(var _0x143a4f=_0x5196d8,_0x36e4b1=-0x1ef8+-0x3*-0xd01+-0x80b*0x1;_0x36e4b1<_0x3101a1[_0x360050(0x259)];_0x36e4b1++)_0x143a4f=_0x3101a1[_0x36e4b1][_0x360050(0x334)](_0x449bbf,_0x143a4f);return _0x143a4f;};}else{var _0x1e2902=_0x365a6e;_0x365a6e=function(_0xe6998d,_0xec7c91){var _0x5bb1b6=_0x3e2adf;return _0x1e2902[_0x5bb1b6(0x334)](_0xe6998d,_0xec7c91);};}}_0x5cf198['push'](_0x365a6e);}0x82e+0x14c5+-0x1cf3!==_0x47565f&&(-0x1*-0x1d72+0x15b0+0x3*-0x110b===_0x47565f?(_0x592aeb[_0x3e2adf(0x32b)]=_0x4e8d10[_0x3e2adf(0x32b)],_0x592aeb['set']=_0x4e8d10['set']):0x1de7+-0x16*-0x10a+-0x34c1===_0x47565f?_0x592aeb[_0x3e2adf(0x2e4)]=_0x4e8d10:-0x2508+-0x2665+0x4b70===_0x47565f?_0x592aeb[_0x3e2adf(0x32b)]=_0x4e8d10:0x11*-0x9f+-0x6a5+0x1138===_0x47565f&&(_0x592aeb[_0x3e2adf(0x1e4)]=_0x4e8d10),_0xccabc1?0x8f6*0x1+-0xae9+0xa*0x32===_0x47565f?(_0x5cf198['push'](function(_0x31015b,_0x979f27){var _0x1938a1=_0x3e2adf;return _0x4e8d10['get'][_0x1938a1(0x334)](_0x31015b,_0x979f27);}),_0x5cf198[_0x3e2adf(0x36e)](function(_0x506584,_0x44c428){var _0x29e7e5=_0x3e2adf;return _0x4e8d10['set'][_0x29e7e5(0x334)](_0x506584,_0x44c428);})):0x27*-0xd0+0x4*-0x12d+0x2466===_0x47565f?_0x5cf198[_0x3e2adf(0x36e)](_0x4e8d10):_0x5cf198[_0x3e2adf(0x36e)](function(_0x3345b4,_0x845662){var _0x1b7734=_0x3e2adf;return _0x4e8d10[_0x1b7734(0x334)](_0x3345b4,_0x845662);}):Object['defineProperty'](_0x5646db,_0x37f54b,_0x592aeb));}function _0x754898(_0x2bd2c5,_0x2c3dcd,_0x119780){var _0x42b3ee=w_0x25f3;for(var _0x328f7d,_0x12addf,_0x29a079,_0x3330d8=[],_0x484293=new Map(),_0x4a2ea8=new Map(),_0x478e5d=0x1*-0x3e1+-0x22b8+0x2699;_0x478e5d<_0x2c3dcd[_0x42b3ee(0x259)];_0x478e5d++){var _0x5c7927=_0x2c3dcd[_0x478e5d];if(Array[_0x42b3ee(0x2af)](_0x5c7927)){var _0x25ea34,_0x5b9b12,_0x35b641=_0x5c7927[-0x33b*0x1+0xebe+0x3*-0x3d6],_0x49a803=_0x5c7927[-0x78a+-0x29*0x60+0xc*0x1e9],_0x410da0=_0x5c7927[_0x42b3ee(0x259)]>0x1291+-0x1e03+0xb75,_0x3c9168=_0x35b641>=-0x13e1*0x1+0x793+-0x5*-0x277,_0x41ada5=_0x119780;if(_0x3c9168?(_0x25ea34=_0x2bd2c5,-0x7e3+-0x1087*0x2+0x28f1!=(_0x35b641-=0x867+0xa1b+0x127d*-0x1)&&(_0x5b9b12=_0x12addf=_0x12addf||[]),_0x410da0&&!_0x29a079&&(_0x29a079=function(_0x106ee5){return _0x37e1e2(_0x106ee5)===_0x2bd2c5;}),_0x41ada5=_0x29a079):(_0x25ea34=_0x2bd2c5['prototype'],0x13a3*0x1+-0x2589+0x11e6*0x1!==_0x35b641&&(_0x5b9b12=_0x328f7d=_0x328f7d||[])),0xfad+0x3*-0xceb+0x1714!==_0x35b641&&!_0x410da0){var _0x4430b1=_0x3c9168?_0x4a2ea8:_0x484293,_0x3a9a47=_0x4430b1[_0x42b3ee(0x32b)](_0x49a803)||0x139e+-0xa*0x13d+-0x73c;if(!(-0x1*0x5df+0x94b+-0x36c)===_0x3a9a47||0x3*0x38+-0x25b*0x7+-0x8*-0x1fb===_0x3a9a47&&0x54+0x152e+-0x2a*0x83!==_0x35b641||0x2*-0x1cb+0x1e71+-0x1ad7*0x1===_0x3a9a47&&0x6e9+0xa71+0xc1*-0x17!==_0x35b641)throw new Error('Attempted\x20to\x20decorate\x20a\x20public\x20method/accessor\x20that\x20has\x20the\x20same\x20name\x20as\x20a\x20previously\x20decorated\x20public\x20method/accessor.\x20This\x20is\x20not\x20currently\x20supported\x20by\x20the\x20decorators\x20plugin.\x20Property\x20name\x20was:\x20'+_0x49a803);!_0x3a9a47&&_0x35b641>-0x73*-0x45+0x798+-0x2695?_0x4430b1['set'](_0x49a803,_0x35b641):_0x4430b1['set'](_0x49a803,!(0x1*0xb7c+-0x15ce*0x1+0xa52));}_0x23e6b(_0x3330d8,_0x25ea34,_0x5c7927,_0x49a803,_0x35b641,_0x3c9168,_0x410da0,_0x5b9b12,_0x41ada5);}}return _0xdfc9aa(_0x3330d8,_0x328f7d),_0xdfc9aa(_0x3330d8,_0x12addf),_0x3330d8;}function _0xdfc9aa(_0x18680e,_0x994303){var _0x3df611=w_0x25f3;_0x994303&&_0x18680e[_0x3df611(0x36e)](function(_0x3720d6){var _0xc905ee=_0x3df611;for(var _0x400047=0x17dc+-0x3df*0x6+-0xa2;_0x400047<_0x994303['length'];_0x400047++)_0x994303[_0x400047][_0xc905ee(0x334)](_0x3720d6);return _0x3720d6;});}function _0x2258b9(_0x31d7ec,_0x22dcdb){var _0x41474b=w_0x25f3;if(_0x22dcdb[_0x41474b(0x259)]>0x26a0+-0x145f+-0x1241){for(var _0x3c54e9=[],_0x8cb428=_0x31d7ec,_0x2344cd=_0x31d7ec['name'],_0x16f8af=_0x22dcdb[_0x41474b(0x259)]-(0x1321+0x1388+0x1354*-0x2);_0x16f8af>=0x1a79+0x6f*0x2a+-0x2caf;_0x16f8af--){var _0x36735e={'v':!(0x4*-0x601+0x116d*0x1+-0x698*-0x1)};try{var _0x147e7c=_0x22dcdb[_0x16f8af](_0x8cb428,{'kind':_0x41474b(0x19c),'name':_0x2344cd,'addInitializer':_0x3e4ff5(_0x3c54e9,_0x36735e)});}finally{_0x36735e['v']=!(0x1e88+-0x1973+-0x515*0x1);}void(-0xedb+-0x5*0x2bf+0x1c96)!==_0x147e7c&&(_0xed525b(0x10b8+-0xb*0x2e1+-0x3*-0x4ff,_0x147e7c),_0x8cb428=_0x147e7c);}return[_0x8cb428,function(){var _0x28afa7=_0x41474b;for(var _0x4967c5=-0x1*0x23b5+-0x2*-0x4c7+0x203*0xd;_0x4967c5<_0x3c54e9[_0x28afa7(0x259)];_0x4967c5++)_0x3c54e9[_0x4967c5][_0x28afa7(0x334)](_0x8cb428);}];}}function _0x499d65(_0x2be690,_0x428d43,_0x381e26,_0x409a13){return{'e':_0x754898(_0x2be690,_0x428d43,_0x409a13),get 'c'(){return _0x2258b9(_0x2be690,_0x381e26);}};}function _0x63f01f(_0x5fd149){var _0x2e1243=w_0x25f3,_0x111c4c={},_0x2aa094=!(-0x3d*-0x53+-0x2086*-0x1+-0x344c);function _0x3ad3fe(_0x4a2256,_0x3bcff1){return _0x2aa094=!(0x160*0x3+-0x4b5*0x1+0x1*0x95),{'done':!(0x1cd3+0x1*-0xa11+-0x12c1*0x1),'value':new _0x59d886(_0x3bcff1=new Promise(function(_0x353966){_0x353966(_0x5fd149[_0x4a2256](_0x3bcff1));}),0x2b*0x44+-0x8b3*0x2+0x1*0x5fb)};}return _0x111c4c[_0x2e1243(0x384)!=typeof Symbol&&Symbol['iterator']||_0x2e1243(0x1dd)]=function(){return this;},_0x111c4c[_0x2e1243(0x389)]=function(_0x45912d){return _0x2aa094?(_0x2aa094=!(0x1569+0x7*0x314+-0x2af4),_0x45912d):_0x3ad3fe('next',_0x45912d);},_0x2e1243(0x1ee)==typeof _0x5fd149[_0x2e1243(0x250)]&&(_0x111c4c[_0x2e1243(0x250)]=function(_0xd71e81){var _0x37886e=_0x2e1243;if(_0x2aa094)throw _0x2aa094=!(-0x648+-0x1eca+-0x2513*-0x1),_0xd71e81;return _0x3ad3fe(_0x37886e(0x250),_0xd71e81);}),_0x2e1243(0x1ee)==typeof _0x5fd149[_0x2e1243(0x2fd)]&&(_0x111c4c[_0x2e1243(0x2fd)]=function(_0xd4b711){return _0x2aa094?(_0x2aa094=!(-0x8e1*-0x4+-0x2194+0x3*-0xa5),_0xd4b711):_0x3ad3fe('return',_0xd4b711);}),_0x111c4c;}function _0x278b9f(_0x12b945){var _0x55cd1f=w_0x25f3,_0x449dfe,_0x31b182,_0x218f32,_0x2ecb8a=-0x1d9*-0x5+-0x5d4+-0x367;for('undefined'!=typeof Symbol&&(_0x31b182=Symbol[_0x55cd1f(0x17c)],_0x218f32=Symbol[_0x55cd1f(0x3b3)]);_0x2ecb8a--;){if(_0x31b182&&null!=(_0x449dfe=_0x12b945[_0x31b182]))return _0x449dfe[_0x55cd1f(0x334)](_0x12b945);if(_0x218f32&&null!=(_0x449dfe=_0x12b945[_0x218f32]))return new _0x3d6fc9(_0x449dfe[_0x55cd1f(0x334)](_0x12b945));_0x31b182=_0x55cd1f(0x230),_0x218f32=_0x55cd1f(0x1dd);}throw new TypeError(_0x55cd1f(0x2a9));}function _0x3d6fc9(_0x4d37b6){var _0x36b1b0=w_0x25f3;function _0x6c368b(_0x231d6c){var _0x3a3e78=w_0x25f3;if(Object(_0x231d6c)!==_0x231d6c)return Promise['reject'](new TypeError(_0x231d6c+_0x3a3e78(0x3a6)));var _0x4cdd04=_0x231d6c[_0x3a3e78(0x1e3)];return Promise['resolve'](_0x231d6c[_0x3a3e78(0x2e4)])['then'](function(_0x468cc1){return{'value':_0x468cc1,'done':_0x4cdd04};});}return(_0x3d6fc9=function(_0x5a83bf){var _0x2ffee8=w_0x25f3;this['s']=_0x5a83bf,this['n']=_0x5a83bf[_0x2ffee8(0x389)];})[_0x36b1b0(0x344)]={'s':null,'n':null,'next':function(){var _0x1eac7c=_0x36b1b0;return _0x6c368b(this['n'][_0x1eac7c(0x207)](this['s'],arguments));},'return':function(_0x379438){var _0x45dc23=_0x36b1b0,_0x35ef86=this['s'][_0x45dc23(0x2fd)];return void(0x23cd+0x6f3*0x1+-0x48*0x98)===_0x35ef86?Promise[_0x45dc23(0x278)]({'value':_0x379438,'done':!(-0x220+-0x16b0+0x8*0x31a)}):_0x6c368b(_0x35ef86[_0x45dc23(0x207)](this['s'],arguments));},'throw':function(_0x2ca86a){var _0x5812c4=_0x36b1b0,_0x449ee5=this['s'][_0x5812c4(0x2fd)];return void(0xe3b+0x1*-0x128e+0x453)===_0x449ee5?Promise[_0x5812c4(0x39b)](_0x2ca86a):_0x6c368b(_0x449ee5['apply'](this['s'],arguments));}},new _0x3d6fc9(_0x4d37b6);}function _0x81c36b(_0x305287){return new _0x59d886(_0x305287,-0x5e+0xf84+-0xf26*0x1);}function _0x37e1e2(_0x3127ce){var _0x43b88e=w_0x25f3;if(Object(_0x3127ce)!==_0x3127ce)throw TypeError('right-hand\x20side\x20of\x20\x27in\x27\x20should\x20be\x20an\x20object,\x20got\x20'+(null!==_0x3127ce?typeof _0x3127ce:_0x43b88e(0x308)));return _0x3127ce;}function _0x564673(_0x18bd7c,_0x51fb0f,_0x2103c5,_0x5f4619){var _0x41c64=w_0x25f3,_0xab8c16={'configurable':!(-0x15c4+0x25f7*-0x1+0x3bbb),'enumerable':!(0x2581+-0x121c+-0x1365)};return _0xab8c16[_0x18bd7c]=_0x5f4619,Object[_0x41c64(0x175)](_0x51fb0f,_0x2103c5,_0xab8c16);}function _0x19d66a(_0x14c297,_0x128d38){var _0x48eab4=w_0x25f3,_0x25f943=null==_0x14c297?null:_0x48eab4(0x384)!=typeof Symbol&&_0x14c297[Symbol[_0x48eab4(0x3b3)]]||_0x14c297[_0x48eab4(0x1dd)];if(null!=_0x25f943){var _0x285958,_0x5bcc9e,_0x2d4c39,_0x193c70,_0x146015=[],_0x16efb7=!(-0x7e*0x36+0xcff*0x1+0xd95),_0x5c9556=!(-0x1d71*-0x1+0x434*-0x9+0x864);try{if(_0x2d4c39=(_0x25f943=_0x25f943[_0x48eab4(0x334)](_0x14c297))['next'],-0xe3*-0xb+-0x1f3*0xe+-0x43*-0x43===_0x128d38){if(Object(_0x25f943)!==_0x25f943)return;_0x16efb7=!(0x17cb+0x18a9+-0x4f*0x9d);}else{for(;!(_0x16efb7=(_0x285958=_0x2d4c39['call'](_0x25f943))[_0x48eab4(0x1e3)])&&(_0x146015[_0x48eab4(0x36e)](_0x285958[_0x48eab4(0x2e4)]),_0x146015[_0x48eab4(0x259)]!==_0x128d38);_0x16efb7=!(0x1*-0x22d3+0x1f2e+0x3*0x137));}}catch(_0x22e14d){_0x5c9556=!(-0x3*-0x69e+-0xd79+-0x47*0x17),_0x5bcc9e=_0x22e14d;}finally{try{if(!_0x16efb7&&null!=_0x25f943[_0x48eab4(0x2fd)]&&(_0x193c70=_0x25f943['return'](),Object(_0x193c70)!==_0x193c70))return;}finally{if(_0x5c9556)throw _0x5bcc9e;}}return _0x146015;}}function _0x376fd5(_0x233550,_0x1b337f){var _0x545ac4=w_0x25f3,_0x524438=_0x233550&&(_0x545ac4(0x384)!=typeof Symbol&&_0x233550[Symbol[_0x545ac4(0x3b3)]]||_0x233550['@@iterator']);if(null!=_0x524438){var _0x259ce6,_0x4523ee=[];for(_0x524438=_0x524438['call'](_0x233550);_0x233550[_0x545ac4(0x259)]<_0x1b337f&&!(_0x259ce6=_0x524438['next']())[_0x545ac4(0x1e3)];)_0x4523ee[_0x545ac4(0x36e)](_0x259ce6['value']);return _0x4523ee;}}function _0x2e4b86(_0x760974,_0x438efa,_0x37a20b,_0x52b279){var _0x2644f8=w_0x25f3;_0x500e9f||(_0x500e9f=_0x2644f8(0x1ee)==typeof Symbol&&Symbol[_0x2644f8(0x31e)]&&Symbol[_0x2644f8(0x31e)](_0x2644f8(0x279))||0x1353f+-0x1157+-0x3921);var _0x513b54=_0x760974&&_0x760974[_0x2644f8(0x371)],_0x5469e9=arguments[_0x2644f8(0x259)]-(-0xcb*-0xb+-0xa*0x137+-0x28*-0x16);if(_0x438efa||0x109*-0x1c+0xdc1+0xf3b===_0x5469e9||(_0x438efa={'children':void(-0x2*-0x1037+0x32f+-0x239d)}),-0xf69+0xedb+-0x8f*-0x1===_0x5469e9)_0x438efa[_0x2644f8(0x16f)]=_0x52b279;else{if(_0x5469e9>-0x18e2+-0x25af+0x3e92){for(var _0x5c6f82=new Array(_0x5469e9),_0x63cb90=-0x1*-0xebd+-0x1a37+0x2*0x5bd;_0x63cb90<_0x5469e9;_0x63cb90++)_0x5c6f82[_0x63cb90]=arguments[_0x63cb90+(0x15f5+-0x245f+-0x4cf*-0x3)];_0x438efa[_0x2644f8(0x16f)]=_0x5c6f82;}}if(_0x438efa&&_0x513b54){for(var _0xbcee2c in _0x513b54)void(-0x1091*-0x1+-0x3*0x34d+-0x6aa)===_0x438efa[_0xbcee2c]&&(_0x438efa[_0xbcee2c]=_0x513b54[_0xbcee2c]);}else _0x438efa||(_0x438efa=_0x513b54||{});return{'$$typeof':_0x500e9f,'type':_0x760974,'key':void(-0x7*0x1cd+-0x1*-0xb38+0x163)===_0x37a20b?null:''+_0x37a20b,'ref':null,'props':_0x438efa,'_owner':null};}function _0x3a3eb5(_0x4619be,_0x3fc822){var _0x44978d=w_0x25f3,_0x504f59=Object[_0x44978d(0x17f)](_0x4619be);if(Object[_0x44978d(0x388)]){var _0x4ea700=Object['getOwnPropertySymbols'](_0x4619be);_0x3fc822&&(_0x4ea700=_0x4ea700[_0x44978d(0x164)](function(_0x2a0c03){var _0x162328=_0x44978d;return Object[_0x162328(0x2a6)](_0x4619be,_0x2a0c03)[_0x162328(0x23a)];})),_0x504f59['push'][_0x44978d(0x207)](_0x504f59,_0x4ea700);}return _0x504f59;}function _0x4ee2dd(_0xf371f0){var _0x145ce6=w_0x25f3;for(var _0x5e36a1=-0x5*0x1ad+0x11e6*-0x1+0x1a48;_0x5e36a1=-0x3*-0x4ef+-0x161b*-0x1+-0x24e8;--_0x218123){var _0x5e4d00=this[_0x2739ee(0x337)][_0x218123],_0x459dde=_0x5e4d00[_0x2739ee(0x387)];if(_0x2739ee(0x1c3)===_0x5e4d00['tryLoc'])return _0x5bc3f7(_0x2739ee(0x1db));if(_0x5e4d00[_0x2739ee(0x29e)]<=this[_0x2739ee(0x32c)]){var _0x55fd7f=_0x43238c['call'](_0x5e4d00,'catchLoc'),_0x47cf6c=_0x43238c[_0x2739ee(0x334)](_0x5e4d00,_0x2739ee(0x220));if(_0x55fd7f&&_0x47cf6c){if(this[_0x2739ee(0x32c)]<_0x5e4d00[_0x2739ee(0x2c4)])return _0x5bc3f7(_0x5e4d00['catchLoc'],!(-0x25*0xe9+-0x2368+0x4515));if(this['prev']<_0x5e4d00[_0x2739ee(0x220)])return _0x5bc3f7(_0x5e4d00[_0x2739ee(0x220)]);}else{if(_0x55fd7f){if(this['prev']<_0x5e4d00[_0x2739ee(0x2c4)])return _0x5bc3f7(_0x5e4d00[_0x2739ee(0x2c4)],!(0xf11+-0x1032+-0x121*-0x1));}else{if(!_0x47cf6c)throw new Error(_0x2739ee(0x33e));if(this[_0x2739ee(0x32c)]<_0x5e4d00['finallyLoc'])return _0x5bc3f7(_0x5e4d00['finallyLoc']);}}}}},'abrupt':function(_0x452b06,_0x46591d){var _0x189350=_0x44a0fc;for(var _0x24d9ee=this[_0x189350(0x337)][_0x189350(0x259)]-(-0x1*-0x434+0xb2d*-0x1+0x6fa);_0x24d9ee>=0x144c+-0x1*0x80f+-0xc3d;--_0x24d9ee){var _0x3e29ca=this[_0x189350(0x337)][_0x24d9ee];if(_0x3e29ca[_0x189350(0x29e)]<=this[_0x189350(0x32c)]&&_0x43238c[_0x189350(0x334)](_0x3e29ca,_0x189350(0x220))&&this[_0x189350(0x32c)]<_0x3e29ca[_0x189350(0x220)]){var _0x35a2dc=_0x3e29ca;break;}}_0x35a2dc&&(_0x189350(0x31f)===_0x452b06||_0x189350(0x2d2)===_0x452b06)&&_0x35a2dc[_0x189350(0x29e)]<=_0x46591d&&_0x46591d<=_0x35a2dc[_0x189350(0x220)]&&(_0x35a2dc=null);var _0x1837c0=_0x35a2dc?_0x35a2dc[_0x189350(0x387)]:{};return _0x1837c0['type']=_0x452b06,_0x1837c0[_0x189350(0x327)]=_0x46591d,_0x35a2dc?(this[_0x189350(0x25a)]=_0x189350(0x389),this[_0x189350(0x389)]=_0x35a2dc[_0x189350(0x220)],_0x511b05):this[_0x189350(0x162)](_0x1837c0);},'complete':function(_0x4daef1,_0x152d13){var _0x2e0c88=_0x44a0fc;if(_0x2e0c88(0x250)===_0x4daef1[_0x2e0c88(0x1ab)])throw _0x4daef1[_0x2e0c88(0x327)];return _0x2e0c88(0x31f)===_0x4daef1[_0x2e0c88(0x1ab)]||_0x2e0c88(0x2d2)===_0x4daef1[_0x2e0c88(0x1ab)]?this[_0x2e0c88(0x389)]=_0x4daef1[_0x2e0c88(0x327)]:_0x2e0c88(0x2fd)===_0x4daef1['type']?(this[_0x2e0c88(0x29a)]=this[_0x2e0c88(0x327)]=_0x4daef1[_0x2e0c88(0x327)],this[_0x2e0c88(0x25a)]=_0x2e0c88(0x2fd),this[_0x2e0c88(0x389)]='end'):_0x2e0c88(0x2f1)===_0x4daef1[_0x2e0c88(0x1ab)]&&_0x152d13&&(this[_0x2e0c88(0x389)]=_0x152d13),_0x511b05;},'finish':function(_0x33d396){var _0x38fc51=_0x44a0fc;for(var _0x5abbd8=this[_0x38fc51(0x337)][_0x38fc51(0x259)]-(0x1b6c+0x10f1+-0x2c5c);_0x5abbd8>=-0x106c+-0x26aa+0x3716;--_0x5abbd8){var _0x5eb5c4=this[_0x38fc51(0x337)][_0x5abbd8];if(_0x5eb5c4[_0x38fc51(0x220)]===_0x33d396)return this['complete'](_0x5eb5c4[_0x38fc51(0x387)],_0x5eb5c4[_0x38fc51(0x2fb)]),_0x2f82b3(_0x5eb5c4),_0x511b05;}},'catch':function(_0x235ccf){var _0x4e3345=_0x44a0fc;for(var _0x1db840=this['tryEntries'][_0x4e3345(0x259)]-(-0x3a*-0x4f+-0x1*-0x12d6+-0x24bb*0x1);_0x1db840>=-0x168+0x305+-0x19d;--_0x1db840){var _0x545b66=this['tryEntries'][_0x1db840];if(_0x545b66[_0x4e3345(0x29e)]===_0x235ccf){var _0x2d5b63=_0x545b66[_0x4e3345(0x387)];if(_0x4e3345(0x250)===_0x2d5b63[_0x4e3345(0x1ab)]){var _0x13e035=_0x2d5b63[_0x4e3345(0x327)];_0x2f82b3(_0x545b66);}return _0x13e035;}}throw new Error(_0x4e3345(0x23f));},'delegateYield':function(_0x395df9,_0x50d973,_0x48faf1){var _0x39919e=_0x44a0fc;return this[_0x39919e(0x1bc)]={'iterator':_0x2edbf4(_0x395df9),'resultName':_0x50d973,'nextLoc':_0x48faf1},'next'===this['method']&&(this['arg']=void(0x1958+-0xaba+-0xe9e)),_0x511b05;}},_0x507b23;}function _0x1db123(_0x149c27){var _0x39d672=w_0x25f3;return(_0x1db123=_0x39d672(0x1ee)==typeof Symbol&&'symbol'==typeof Symbol['iterator']?function(_0x4a8f2c){return typeof _0x4a8f2c;}:function(_0x40eb64){var _0x4f8eee=_0x39d672;return _0x40eb64&&_0x4f8eee(0x1ee)==typeof Symbol&&_0x40eb64[_0x4f8eee(0x2ac)]===Symbol&&_0x40eb64!==Symbol['prototype']?_0x4f8eee(0x2ed):typeof _0x40eb64;})(_0x149c27);}function _0x34d29a(){var _0x4bbdba=w_0x25f3;_0x34d29a=function(_0x1859fe,_0x26adf2){return new _0x3b92d4(_0x1859fe,void(-0x1f*0x133+0x3*0x4f5+0x23b*0xa),_0x26adf2);};var _0x5aed6e=RegExp[_0x4bbdba(0x344)],_0x58ba91=new WeakMap();function _0x3b92d4(_0x2f61fd,_0x513c44,_0xaf22fc){var _0xd932c6=_0x4bbdba,_0xd60509=new RegExp(_0x2f61fd,_0x513c44);return _0x58ba91[_0xd932c6(0x1e4)](_0xd60509,_0xaf22fc||_0x58ba91[_0xd932c6(0x32b)](_0x2f61fd)),_0x2e2f47(_0xd60509,_0x3b92d4[_0xd932c6(0x344)]);}function _0x427d42(_0x489eeb,_0x1699d8){var _0x1004fb=_0x4bbdba,_0x2ae826=_0x58ba91[_0x1004fb(0x32b)](_0x1699d8);return Object[_0x1004fb(0x17f)](_0x2ae826)[_0x1004fb(0x376)](function(_0x100f10,_0x5bfdf5){var _0x3ad421=_0x1004fb,_0x541568=_0x2ae826[_0x5bfdf5];if(_0x3ad421(0x28b)==typeof _0x541568)_0x100f10[_0x5bfdf5]=_0x489eeb[_0x541568];else{for(var _0x3bc728=0x1*0x3ce+0x3*-0x435+0x8d1;void(0x2695+0x1ee8+-0x457d)===_0x489eeb[_0x541568[_0x3bc728]]&&_0x3bc728+(0x3fb*-0x2+0x2631+-0x2*0xf1d)<_0x541568['length'];)_0x3bc728++;_0x100f10[_0x5bfdf5]=_0x489eeb[_0x541568[_0x3bc728]];}return _0x100f10;},Object[_0x1004fb(0x3b7)](null));}return _0x5dda7d(_0x3b92d4,RegExp),_0x3b92d4['prototype']['exec']=function(_0x1bc796){var _0x2a6afd=_0x4bbdba,_0x457628=_0x5aed6e[_0x2a6afd(0x209)]['call'](this,_0x1bc796);if(_0x457628){_0x457628['groups']=_0x427d42(_0x457628,this);var _0x546542=_0x457628['indices'];_0x546542&&(_0x546542['groups']=_0x427d42(_0x546542,this));}return _0x457628;},_0x3b92d4[_0x4bbdba(0x344)][Symbol[_0x4bbdba(0x377)]]=function(_0x10e2ef,_0x453a26){var _0x226516=_0x4bbdba;if(_0x226516(0x33c)==typeof _0x453a26){var _0x42705a=_0x58ba91[_0x226516(0x32b)](this);return _0x5aed6e[Symbol[_0x226516(0x377)]]['call'](this,_0x10e2ef,_0x453a26[_0x226516(0x377)](/\$<([^>]+)>/g,function(_0x4536fa,_0x53b1c5){var _0x5db08d=_0x226516,_0x1b4f5c=_0x42705a[_0x53b1c5];return'$'+(Array[_0x5db08d(0x2af)](_0x1b4f5c)?_0x1b4f5c[_0x5db08d(0x24f)]('$'):_0x1b4f5c);}));}if(_0x226516(0x1ee)==typeof _0x453a26){var _0x51880e=this;return _0x5aed6e[Symbol[_0x226516(0x377)]][_0x226516(0x334)](this,_0x10e2ef,function(){var _0x3ce548=_0x226516,_0x352e21=arguments;return _0x3ce548(0x17d)!=typeof _0x352e21[_0x352e21[_0x3ce548(0x259)]-(-0xf9*-0x7+0xd4+-0x2*0x3d1)]&&(_0x352e21=[]['slice'][_0x3ce548(0x334)](_0x352e21))[_0x3ce548(0x36e)](_0x427d42(_0x352e21,_0x51880e)),_0x453a26[_0x3ce548(0x207)](this,_0x352e21);});}return _0x5aed6e[Symbol[_0x226516(0x377)]]['call'](this,_0x10e2ef,_0x453a26);},_0x34d29a[_0x4bbdba(0x207)](this,arguments);}function _0x2772ab(_0x17b93c){var _0x2e8af4=w_0x25f3;this[_0x2e8af4(0x180)]=_0x17b93c;}function _0x125e01(_0x221f1a){return function(){var _0x2db496=w_0x25f3;return new _0x137ba2(_0x221f1a[_0x2db496(0x207)](this,arguments));};}function _0x8a0370(_0x1ac7e2,_0x440b38,_0x4611ac,_0x1f79f3,_0x26eb95,_0x4af1d4,_0x2b24bc){var _0x3a480e=w_0x25f3;try{var _0x16d0ce=_0x1ac7e2[_0x4af1d4](_0x2b24bc),_0x2e8eaf=_0x16d0ce[_0x3a480e(0x2e4)];}catch(_0xc88fae){return void _0x4611ac(_0xc88fae);}_0x16d0ce[_0x3a480e(0x1e3)]?_0x440b38(_0x2e8eaf):Promise[_0x3a480e(0x278)](_0x2e8eaf)[_0x3a480e(0x1ed)](_0x1f79f3,_0x26eb95);}function _0x50daaa(_0x10de0d){return function(){var _0x310206=this,_0x17ad73=arguments;return new Promise(function(_0x37120b,_0x728e37){var _0x5a819e=_0x10de0d['apply'](_0x310206,_0x17ad73);function _0x12466a(_0x263e68){var _0x123e7f=w_0x25f3;_0x8a0370(_0x5a819e,_0x37120b,_0x728e37,_0x12466a,_0x191f8b,_0x123e7f(0x389),_0x263e68);}function _0x191f8b(_0x255ae1){var _0x5d15ce=w_0x25f3;_0x8a0370(_0x5a819e,_0x37120b,_0x728e37,_0x12466a,_0x191f8b,_0x5d15ce(0x250),_0x255ae1);}_0x12466a(void(0xbdf+-0x129c+-0x19*-0x45));});};}function _0x297d3d(_0xd86983,_0x45590a){var _0x51c357=w_0x25f3;if(!(_0xd86983 instanceof _0x45590a))throw new TypeError(_0x51c357(0x2f7));}function _0x96f358(_0x217e4a,_0x156410){var _0x251198=w_0x25f3;for(var _0x336960=0x1613+-0x2f*0x3b+0xb3e*-0x1;_0x336960<_0x156410[_0x251198(0x259)];_0x336960++){var _0x3d27f0=_0x156410[_0x336960];_0x3d27f0[_0x251198(0x23a)]=_0x3d27f0[_0x251198(0x23a)]||!(-0xbf7+-0x166e+0x2266),_0x3d27f0[_0x251198(0x2bc)]=!(-0xde+0x1c94+-0x1bb6),_0x251198(0x2e4)in _0x3d27f0&&(_0x3d27f0['writable']=!(-0x21*0x12f+-0x2*-0x17d+0x2415)),Object[_0x251198(0x175)](_0x217e4a,_0x32e885(_0x3d27f0[_0x251198(0x392)]),_0x3d27f0);}}function _0x55cd8a(_0x174a54,_0x5e25eb,_0x465e50){var _0x4d17e4=w_0x25f3;return _0x5e25eb&&_0x96f358(_0x174a54[_0x4d17e4(0x344)],_0x5e25eb),_0x465e50&&_0x96f358(_0x174a54,_0x465e50),Object[_0x4d17e4(0x175)](_0x174a54,_0x4d17e4(0x344),{'writable':!(-0xc19+0x1aa+-0x2*-0x538)}),_0x174a54;}function _0x58d8c2(_0x5634d7,_0x53515c){var _0x5fc7bf=w_0x25f3;for(var _0x5e7563 in _0x53515c){(_0x532356=_0x53515c[_0x5e7563])[_0x5fc7bf(0x2bc)]=_0x532356[_0x5fc7bf(0x23a)]=!(0xb5+0x6*0x80+-0x3b5),'value'in _0x532356&&(_0x532356[_0x5fc7bf(0x2e3)]=!(0x19d*0x11+0x126f+-0x2ddc)),Object[_0x5fc7bf(0x175)](_0x5634d7,_0x5e7563,_0x532356);}if(Object[_0x5fc7bf(0x388)])for(var _0x45bcc1=Object[_0x5fc7bf(0x388)](_0x53515c),_0x368698=-0x11*-0x1af+-0x1ec7+0x228;_0x368698<_0x45bcc1[_0x5fc7bf(0x259)];_0x368698++){var _0x532356,_0x6be33e=_0x45bcc1[_0x368698];(_0x532356=_0x53515c[_0x6be33e])['configurable']=_0x532356[_0x5fc7bf(0x23a)]=!(-0x833+0x9*-0x76+0x1d*0x6d),_0x5fc7bf(0x2e4)in _0x532356&&(_0x532356[_0x5fc7bf(0x2e3)]=!(0x18bf+-0x337*0x5+-0x14*0x6f)),Object[_0x5fc7bf(0x175)](_0x5634d7,_0x6be33e,_0x532356);}return _0x5634d7;}function _0x40ff51(_0x254aaa,_0x287254){var _0x77273f=w_0x25f3;for(var _0x587d0b=Object[_0x77273f(0x32a)](_0x287254),_0xa5f40d=-0x2174+0x25a*-0x2+0x42*0x94;_0xa5f40d<_0x587d0b[_0x77273f(0x259)];_0xa5f40d++){var _0x23b426=_0x587d0b[_0xa5f40d],_0x59ae3e=Object[_0x77273f(0x2a6)](_0x287254,_0x23b426);_0x59ae3e&&_0x59ae3e[_0x77273f(0x2bc)]&&void(-0x221b*0x1+0x2326*0x1+-0x10b)===_0x254aaa[_0x23b426]&&Object['defineProperty'](_0x254aaa,_0x23b426,_0x59ae3e);}return _0x254aaa;}function _0x4a7824(_0x26784f,_0x47795a,_0x26a673){var _0x1edba1=w_0x25f3;return(_0x47795a=_0x32e885(_0x47795a))in _0x26784f?Object[_0x1edba1(0x175)](_0x26784f,_0x47795a,{'value':_0x26a673,'enumerable':!(0x24eb+0x1b2+-0x1*0x269d),'configurable':!(-0x472+-0xb7*0x12+0x1150),'writable':!(-0x23f8+0x257d+-0x185*0x1)}):_0x26784f[_0x47795a]=_0x26a673,_0x26784f;}function _0x36f54f(){var _0x472dbc=w_0x25f3;return(_0x36f54f=Object['assign']?Object[_0x472dbc(0x2a0)]['bind']():function(_0x3e0b52){var _0x1eddf8=_0x472dbc;for(var _0x16d75e=-0x645+-0x2115*0x1+-0x19*-0x193;_0x16d75e=0x8a7+-0x21ed+0x1946||(_0x5ca1c5[_0x31fbf6]=_0x161463[_0x31fbf6]);return _0x5ca1c5;}function _0x2a28e9(_0x2cdf2e,_0x74ac11){var _0x942c51=w_0x25f3;if(null==_0x2cdf2e)return{};var _0x5ef46e,_0x5acf70,_0x282df4=_0x2bfa3e(_0x2cdf2e,_0x74ac11);if(Object['getOwnPropertySymbols']){var _0x33d708=Object[_0x942c51(0x388)](_0x2cdf2e);for(_0x5acf70=-0xd30+0x4a1*0x1+0x7*0x139;_0x5acf70<_0x33d708[_0x942c51(0x259)];_0x5acf70++)_0x5ef46e=_0x33d708[_0x5acf70],_0x74ac11[_0x942c51(0x2c5)](_0x5ef46e)>=-0x1f5d*0x1+0x40b+0x1b52||Object[_0x942c51(0x344)][_0x942c51(0x1da)][_0x942c51(0x334)](_0x2cdf2e,_0x5ef46e)&&(_0x282df4[_0x5ef46e]=_0x2cdf2e[_0x5ef46e]);}return _0x282df4;}function _0x58f550(_0x94e938){var _0x41d076=w_0x25f3;if(void(-0x6a*0x1+-0xf6+0x160)===_0x94e938)throw new ReferenceError(_0x41d076(0x1bb));return _0x94e938;}function _0xf3b17(_0x7b2cf6,_0x347c9f){var _0x41f350=w_0x25f3;if(_0x347c9f&&(_0x41f350(0x17d)==typeof _0x347c9f||_0x41f350(0x1ee)==typeof _0x347c9f))return _0x347c9f;if(void(0x160d+-0x24fd+-0x8*-0x1de)!==_0x347c9f)throw new TypeError('Derived\x20constructors\x20may\x20only\x20return\x20object\x20or\x20undefined');return _0x58f550(_0x7b2cf6);}function _0x17b234(_0x33316f){var _0x3601f0=_0x3390dc();return function(){var _0x4733d6=w_0x25f3,_0x2848c1,_0x2d0386=_0x22af63(_0x33316f);if(_0x3601f0){var _0x252bb3=_0x22af63(this)[_0x4733d6(0x2ac)];_0x2848c1=Reflect[_0x4733d6(0x320)](_0x2d0386,arguments,_0x252bb3);}else _0x2848c1=_0x2d0386[_0x4733d6(0x207)](this,arguments);return _0xf3b17(this,_0x2848c1);};}function _0x2b3a8b(_0x409fbb,_0x508825){for(;!Object['prototype']['hasOwnProperty']['call'](_0x409fbb,_0x508825)&&null!==(_0x409fbb=_0x22af63(_0x409fbb)););return _0x409fbb;}function _0x33bfa4(){var _0x1bbf02=w_0x25f3;return(_0x33bfa4=_0x1bbf02(0x384)!=typeof Reflect&&Reflect[_0x1bbf02(0x32b)]?Reflect[_0x1bbf02(0x32b)][_0x1bbf02(0x301)]():function(_0x45f38c,_0x4a526c,_0x482f60){var _0xb2ad90=_0x1bbf02,_0x53f5d7=_0x2b3a8b(_0x45f38c,_0x4a526c);if(_0x53f5d7){var _0x24e05f=Object[_0xb2ad90(0x2a6)](_0x53f5d7,_0x4a526c);return _0x24e05f[_0xb2ad90(0x32b)]?_0x24e05f[_0xb2ad90(0x32b)][_0xb2ad90(0x334)](arguments[_0xb2ad90(0x259)]<0x7f*0x25+0x23f3+-0x364b?_0x45f38c:_0x482f60):_0x24e05f[_0xb2ad90(0x2e4)];}})['apply'](this,arguments);}function _0x5b010c(_0x878ee0,_0x27a5c4,_0x35cb40,_0x14e9ba){var _0x1c5f60=w_0x25f3;return(_0x5b010c='undefined'!=typeof Reflect&&Reflect[_0x1c5f60(0x1e4)]?Reflect[_0x1c5f60(0x1e4)]:function(_0xc1d4d,_0x3a32f8,_0x25436d,_0x5ef6e3){var _0x43a591=_0x1c5f60,_0x23d91e,_0x5ac459=_0x2b3a8b(_0xc1d4d,_0x3a32f8);if(_0x5ac459){if((_0x23d91e=Object[_0x43a591(0x2a6)](_0x5ac459,_0x3a32f8))[_0x43a591(0x1e4)])return _0x23d91e['set'][_0x43a591(0x334)](_0x5ef6e3,_0x25436d),!(0x4d*-0x59+-0x1*-0x3d6+0x16ef);if(!_0x23d91e[_0x43a591(0x2e3)])return!(-0x2691+0x9e*0x25+0xfbc);}if(_0x23d91e=Object['getOwnPropertyDescriptor'](_0x5ef6e3,_0x3a32f8)){if(!_0x23d91e[_0x43a591(0x2e3)])return!(0x14d+-0x1*-0x29b+-0x3e7);_0x23d91e[_0x43a591(0x2e4)]=_0x25436d,Object[_0x43a591(0x175)](_0x5ef6e3,_0x3a32f8,_0x23d91e);}else _0x4a7824(_0x5ef6e3,_0x3a32f8,_0x25436d);return!(0x295+-0xa39*-0x2+-0x1707);})(_0x878ee0,_0x27a5c4,_0x35cb40,_0x14e9ba);}function _0x2732a4(_0x24a602,_0x194547,_0x16a7b5,_0x8913b1,_0x531d0d){var _0x16563f=w_0x25f3;if(!_0x5b010c(_0x24a602,_0x194547,_0x16a7b5,_0x8913b1||_0x24a602)&&_0x531d0d)throw new TypeError(_0x16563f(0x34e));return _0x16a7b5;}function _0x281bf3(_0x4564b4,_0x1f4153){var _0x10b792=w_0x25f3;return _0x1f4153||(_0x1f4153=_0x4564b4[_0x10b792(0x2a5)](0x1017+0x1cb3+0x2*-0x1665)),Object[_0x10b792(0x198)](Object[_0x10b792(0x36c)](_0x4564b4,{'raw':{'value':Object[_0x10b792(0x198)](_0x1f4153)}}));}function _0x2d5f0c(_0x18863e,_0x13e8d4){var _0x47a3d1=w_0x25f3;return _0x13e8d4||(_0x13e8d4=_0x18863e['slice'](0x8f1*0x1+0x1*0x1e1f+-0x7d*0x50)),_0x18863e[_0x47a3d1(0x2ab)]=_0x13e8d4,_0x18863e;}function _0x246229(_0x5e71a0){var _0x47c977=w_0x25f3;throw new TypeError('\x22'+_0x5e71a0+_0x47c977(0x338));}function _0x5db7bf(_0x51f915){throw new TypeError('\x22'+_0x51f915+'\x22\x20is\x20write-only');}function _0x2275f4(_0x42246e){var _0x2f7351=w_0x25f3;throw new ReferenceError(_0x2f7351(0x1c7)+_0x42246e+'\x22\x20cannot\x20be\x20referenced\x20in\x20computed\x20property\x20keys.');}function _0x516c2b(){}function _0xab4ac9(_0xcde017){throw new ReferenceError(_0xcde017+'\x20is\x20not\x20defined\x20-\x20temporal\x20dead\x20zone');}function _0x5b377b(_0x5f4fa5,_0x2f492b){return _0x5f4fa5===_0x516c2b?_0xab4ac9(_0x2f492b):_0x5f4fa5;}function _0x5096de(_0x3b53c6,_0x3225c3){return _0x1ae312(_0x3b53c6)||_0x19d66a(_0x3b53c6,_0x3225c3)||_0x525331(_0x3b53c6,_0x3225c3)||_0x25b697();}function _0x1dff6c(_0x3a7a63,_0x147620){return _0x1ae312(_0x3a7a63)||_0x376fd5(_0x3a7a63,_0x147620)||_0x525331(_0x3a7a63,_0x147620)||_0x25b697();}function _0x5c885(_0x3953c0){return _0x1ae312(_0x3953c0)||_0x1853c6(_0x3953c0)||_0x525331(_0x3953c0)||_0x25b697();}function _0x534083(_0x3396cf){return _0x1ccd19(_0x3396cf)||_0x1853c6(_0x3396cf)||_0x525331(_0x3396cf)||_0x542337();}function _0x1ccd19(_0x15e4f0){var _0x384cbb=w_0x25f3;if(Array[_0x384cbb(0x2af)](_0x15e4f0))return _0x537c83(_0x15e4f0);}function _0x1ae312(_0x4bfad0){var _0x3bc5ad=w_0x25f3;if(Array[_0x3bc5ad(0x2af)](_0x4bfad0))return _0x4bfad0;}function _0x4bbf4b(_0x33a831,_0x181a3c,_0x135270){var _0x5bf329=w_0x25f3;if(_0x181a3c&&!Array[_0x5bf329(0x2af)](_0x181a3c)&&_0x5bf329(0x28b)==typeof _0x181a3c[_0x5bf329(0x259)]){var _0x43388b=_0x181a3c[_0x5bf329(0x259)];return _0x537c83(_0x181a3c,void(0xb*-0x29+0x387+-0x2*0xe2)!==_0x135270&&_0x135270<_0x43388b?_0x135270:_0x43388b);}return _0x33a831(_0x181a3c,_0x135270);}function _0x1853c6(_0x3086ca){var _0x3e6030=w_0x25f3;if(_0x3e6030(0x384)!=typeof Symbol&&null!=_0x3086ca[Symbol[_0x3e6030(0x3b3)]]||null!=_0x3086ca[_0x3e6030(0x1dd)])return Array['from'](_0x3086ca);}function _0x525331(_0x1ceb3f,_0x38a668){var _0x2b5250=w_0x25f3;if(_0x1ceb3f){if(_0x2b5250(0x33c)==typeof _0x1ceb3f)return _0x537c83(_0x1ceb3f,_0x38a668);var _0x2821d9=Object[_0x2b5250(0x344)][_0x2b5250(0x3ae)][_0x2b5250(0x334)](_0x1ceb3f)['slice'](0x2ae*-0x7+0x1*0x2181+0xeb7*-0x1,-(-0x1d*-0xec+0x2*-0x32+-0x1a57));return _0x2b5250(0x2eb)===_0x2821d9&&_0x1ceb3f[_0x2b5250(0x2ac)]&&(_0x2821d9=_0x1ceb3f[_0x2b5250(0x2ac)]['name']),'Map'===_0x2821d9||_0x2b5250(0x160)===_0x2821d9?Array[_0x2b5250(0x29c)](_0x1ceb3f):_0x2b5250(0x27f)===_0x2821d9||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/[_0x2b5250(0x22b)](_0x2821d9)?_0x537c83(_0x1ceb3f,_0x38a668):void(-0x1*0xf34+0x12e6+0x1d9*-0x2);}}function _0x537c83(_0x1cc46a,_0xca9940){var _0x4c66e3=w_0x25f3;(null==_0xca9940||_0xca9940>_0x1cc46a[_0x4c66e3(0x259)])&&(_0xca9940=_0x1cc46a[_0x4c66e3(0x259)]);for(var _0x3859e4=0x1fe3*0x1+-0x22be+0x2db,_0x3a8ed3=new Array(_0xca9940);_0x3859e4<_0xca9940;_0x3859e4++)_0x3a8ed3[_0x3859e4]=_0x1cc46a[_0x3859e4];return _0x3a8ed3;}function _0x542337(){var _0x1d894c=w_0x25f3;throw new TypeError(_0x1d894c(0x3a8));}function _0x25b697(){throw new TypeError('Invalid\x20attempt\x20to\x20destructure\x20non-iterable\x20instance.\x0aIn\x20order\x20to\x20be\x20iterable,\x20non-array\x20objects\x20must\x20have\x20a\x20[Symbol.iterator]()\x20method.');}function _0x350075(_0x189dc6,_0x3f6be9){var _0x464771=w_0x25f3,_0x199147=_0x464771(0x384)!=typeof Symbol&&_0x189dc6[Symbol[_0x464771(0x3b3)]]||_0x189dc6[_0x464771(0x1dd)];if(!_0x199147){if(Array['isArray'](_0x189dc6)||(_0x199147=_0x525331(_0x189dc6))||_0x3f6be9&&_0x189dc6&&_0x464771(0x28b)==typeof _0x189dc6[_0x464771(0x259)]){_0x199147&&(_0x189dc6=_0x199147);var _0x544a3f=0x1fd2+0x1*-0x1ad5+0x4fd*-0x1,_0x5b20ae=function(){};return{'s':_0x5b20ae,'n':function(){var _0x4c93f0=_0x464771;return _0x544a3f>=_0x189dc6[_0x4c93f0(0x259)]?{'done':!(-0x1aaa*0x1+-0x126e*0x1+0x2d18)}:{'done':!(0x7*-0x205+0x1d44+-0xf20),'value':_0x189dc6[_0x544a3f++]};},'e':function(_0x4d8393){throw _0x4d8393;},'f':_0x5b20ae};}throw new TypeError(_0x464771(0x2ae));}var _0x23cdd3,_0x5bcfbb=!(0x1*-0x810+-0xb3*0x2f+0x1*0x28ed),_0x4db4fd=!(0x221+0x62e*0x5+0x3*-0xb02);return{'s':function(){_0x199147=_0x199147['call'](_0x189dc6);},'n':function(){var _0x30f507=_0x464771,_0x2382b6=_0x199147['next']();return _0x5bcfbb=_0x2382b6[_0x30f507(0x1e3)],_0x2382b6;},'e':function(_0x3b0f24){_0x4db4fd=!(0x18e0+0x2*0x214+-0x1d08),_0x23cdd3=_0x3b0f24;},'f':function(){var _0x22fdad=_0x464771;try{_0x5bcfbb||null==_0x199147[_0x22fdad(0x2fd)]||_0x199147[_0x22fdad(0x2fd)]();}finally{if(_0x4db4fd)throw _0x23cdd3;}}};}function _0x37af30(_0x37ea9e,_0x2388a0){var _0x3701de=w_0x25f3,_0x2ac940='undefined'!=typeof Symbol&&_0x37ea9e[Symbol[_0x3701de(0x3b3)]]||_0x37ea9e['@@iterator'];if(_0x2ac940)return(_0x2ac940=_0x2ac940['call'](_0x37ea9e))[_0x3701de(0x389)][_0x3701de(0x301)](_0x2ac940);if(Array[_0x3701de(0x2af)](_0x37ea9e)||(_0x2ac940=_0x525331(_0x37ea9e))||_0x2388a0&&_0x37ea9e&&'number'==typeof _0x37ea9e[_0x3701de(0x259)]){_0x2ac940&&(_0x37ea9e=_0x2ac940);var _0x1dcac8=0x1d*-0x11a+-0x1f*0xc7+-0x380b*-0x1;return function(){var _0x306ed0=_0x3701de;return _0x1dcac8>=_0x37ea9e[_0x306ed0(0x259)]?{'done':!(-0x29d+0x5fb*-0x1+0x898)}:{'done':!(-0x6e*0x4f+0x2311+-0x11e),'value':_0x37ea9e[_0x1dcac8++]};};}throw new TypeError(_0x3701de(0x2ae));}function _0x5362e1(_0x4cd55f){return function(){var _0x58617f=w_0x25f3,_0x183c06=_0x4cd55f['apply'](this,arguments);return _0x183c06[_0x58617f(0x389)](),_0x183c06;};}function _0x4ad3b0(_0x460146,_0x32cf07){var _0x271683=w_0x25f3;if(_0x271683(0x17d)!=typeof _0x460146||null===_0x460146)return _0x460146;var _0xa9fa9c=_0x460146[Symbol['toPrimitive']];if(void(0x21b4+0x5*0x4c7+0x3997*-0x1)!==_0xa9fa9c){var _0x560849=_0xa9fa9c['call'](_0x460146,_0x32cf07||_0x271683(0x1d3));if('object'!=typeof _0x560849)return _0x560849;throw new TypeError(_0x271683(0x358));}return('string'===_0x32cf07?String:Number)(_0x460146);}function _0x32e885(_0x225644){var _0x1eb479=w_0x25f3,_0x45c5d4=_0x4ad3b0(_0x225644,_0x1eb479(0x33c));return _0x1eb479(0x2ed)==typeof _0x45c5d4?_0x45c5d4:String(_0x45c5d4);}function _0xf47dc3(_0x1d62a3,_0xeb8470){var _0x31107e=w_0x25f3;throw new Error(_0x31107e(0x1ff));}function _0x9d8dd5(_0x238b59,_0x206a18,_0x4378c6,_0x532e73){var _0x340b07=w_0x25f3;_0x4378c6&&Object[_0x340b07(0x175)](_0x238b59,_0x206a18,{'enumerable':_0x4378c6['enumerable'],'configurable':_0x4378c6[_0x340b07(0x2bc)],'writable':_0x4378c6[_0x340b07(0x2e3)],'value':_0x4378c6[_0x340b07(0x2d0)]?_0x4378c6['initializer'][_0x340b07(0x334)](_0x532e73):void(-0x1ae6+-0x1*-0xf6b+0xb7b*0x1)});}function _0x271739(_0xb876c5,_0x285720,_0x361de9,_0x52c079,_0x38a76d){var _0x26b61e=w_0x25f3,_0x25c883={};return Object['keys'](_0x52c079)[_0x26b61e(0x254)](function(_0xd1a198){_0x25c883[_0xd1a198]=_0x52c079[_0xd1a198];}),_0x25c883[_0x26b61e(0x23a)]=!!_0x25c883[_0x26b61e(0x23a)],_0x25c883[_0x26b61e(0x2bc)]=!!_0x25c883[_0x26b61e(0x2bc)],(_0x26b61e(0x2e4)in _0x25c883||_0x25c883['initializer'])&&(_0x25c883[_0x26b61e(0x2e3)]=!(0x21e8+-0x7cf*-0x5+-0x48f3)),_0x25c883=_0x361de9[_0x26b61e(0x2a5)]()[_0x26b61e(0x2f3)]()[_0x26b61e(0x376)](function(_0x490c4a,_0x3636f6){return _0x3636f6(_0xb876c5,_0x285720,_0x490c4a)||_0x490c4a;},_0x25c883),_0x38a76d&&void(0x1050+0x1*0x121f+-0x226f)!==_0x25c883[_0x26b61e(0x2d0)]&&(_0x25c883[_0x26b61e(0x2e4)]=_0x25c883[_0x26b61e(0x2d0)]?_0x25c883[_0x26b61e(0x2d0)][_0x26b61e(0x334)](_0x38a76d):void(0x1392+-0xbe*0x23+-0x1*-0x668),_0x25c883[_0x26b61e(0x2d0)]=void(-0x1*-0x1b9a+0x7f7+-0x2391*0x1)),void(0x53*0xb+0xa2*-0xf+0x5ed)===_0x25c883[_0x26b61e(0x2d0)]&&(Object[_0x26b61e(0x175)](_0xb876c5,_0x285720,_0x25c883),_0x25c883=null),_0x25c883;}_0x137ba2['prototype'][_0x5612de(0x1ee)==typeof Symbol&&Symbol[_0x5612de(0x17c)]||_0x5612de(0x230)]=function(){return this;},_0x137ba2[_0x5612de(0x344)]['next']=function(_0x4262ae){var _0x1543b4=_0x5612de;return this[_0x1543b4(0x1b1)](_0x1543b4(0x389),_0x4262ae);},_0x137ba2[_0x5612de(0x344)]['throw']=function(_0x29bafe){var _0x4c6950=_0x5612de;return this[_0x4c6950(0x1b1)]('throw',_0x29bafe);},_0x137ba2[_0x5612de(0x344)][_0x5612de(0x2fd)]=function(_0x49a53d){var _0x58f790=_0x5612de;return this[_0x58f790(0x1b1)](_0x58f790(0x2fd),_0x49a53d);};var _0x371360=0x11*-0x20+0x88*-0x1f+0x1298,_0x4d6c78,_0x312e19,_0x2a32a1,_0x371ac2;function _0x2ca065(_0x4c2f2d){var _0x4415ed=_0x5612de;return _0x4415ed(0x365)+_0x371360++ +'_'+_0x4c2f2d;}function _0x33f649(_0x59611c,_0xb8c4c1){var _0x22f43f=_0x5612de;if(!Object['prototype'][_0x22f43f(0x3b8)][_0x22f43f(0x334)](_0x59611c,_0xb8c4c1))throw new TypeError(_0x22f43f(0x271));return _0x59611c;}function _0x4c53b8(_0x467264,_0x1c39b3){var _0x12fb5e=_0x5612de;return _0x1de0ff(_0x467264,_0x3feef3(_0x467264,_0x1c39b3,_0x12fb5e(0x32b)));}function _0xf01d58(_0x248ad,_0xd514dd,_0x4ffcbe){var _0x2a0763=_0x5612de;return _0x30760d(_0x248ad,_0x3feef3(_0x248ad,_0xd514dd,_0x2a0763(0x1e4)),_0x4ffcbe),_0x4ffcbe;}function _0x3ff428(_0x59a12f,_0x2e6e68){var _0x1a4f4c=_0x5612de;return _0x1af7ca(_0x59a12f,_0x3feef3(_0x59a12f,_0x2e6e68,_0x1a4f4c(0x1e4)));}function _0x3feef3(_0x76bca3,_0x49ed2b,_0xf40042){var _0x26b65c=_0x5612de;if(!_0x49ed2b[_0x26b65c(0x2fc)](_0x76bca3))throw new TypeError(_0x26b65c(0x2ad)+_0xf40042+_0x26b65c(0x2bd));return _0x49ed2b[_0x26b65c(0x32b)](_0x76bca3);}function _0x46f318(_0x2345ad,_0x29e732,_0x5d3874){var _0x1785a7=_0x5612de;return _0x3fbf28(_0x2345ad,_0x29e732),_0x234ff5(_0x5d3874,_0x1785a7(0x32b)),_0x1de0ff(_0x2345ad,_0x5d3874);}function _0x3d490a(_0x421876,_0x556ee1,_0x489ccf,_0x32c792){return _0x3fbf28(_0x421876,_0x556ee1),_0x234ff5(_0x489ccf,'set'),_0x30760d(_0x421876,_0x489ccf,_0x32c792),_0x32c792;}function _0x1f2159(_0x3c139e,_0xf1d93,_0xa6753b){return _0x3fbf28(_0x3c139e,_0xf1d93),_0xa6753b;}function _0x2fcc7b(){var _0x52da1e=_0x5612de;throw new TypeError(_0x52da1e(0x1bd));}function _0x1de0ff(_0x35f1cc,_0x2cd6a2){var _0x331169=_0x5612de;return _0x2cd6a2[_0x331169(0x32b)]?_0x2cd6a2[_0x331169(0x32b)][_0x331169(0x334)](_0x35f1cc):_0x2cd6a2[_0x331169(0x2e4)];}function _0x30760d(_0x26c16c,_0x3bf250,_0x4bb532){var _0x4b5758=_0x5612de;if(_0x3bf250['set'])_0x3bf250['set'][_0x4b5758(0x334)](_0x26c16c,_0x4bb532);else{if(!_0x3bf250['writable'])throw new TypeError('attempted\x20to\x20set\x20read\x20only\x20private\x20field');_0x3bf250['value']=_0x4bb532;}}function _0x1af7ca(_0x38569e,_0x4d9da9){var _0x8504a5=_0x5612de;if(_0x4d9da9[_0x8504a5(0x1e4)])return _0x8504a5(0x36f)in _0x4d9da9||(_0x4d9da9['__destrObj']={set 'value'(_0xa3d23){var _0x3d920b=_0x8504a5;_0x4d9da9[_0x3d920b(0x1e4)][_0x3d920b(0x334)](_0x38569e,_0xa3d23);}}),_0x4d9da9['__destrObj'];if(!_0x4d9da9[_0x8504a5(0x2e3)])throw new TypeError('attempted\x20to\x20set\x20read\x20only\x20private\x20field');return _0x4d9da9;}function _0x153ad5(_0x361a17,_0x4bf341,_0x38ef34){var _0x36fea3=_0x5612de;return _0x3fbf28(_0x361a17,_0x4bf341),_0x234ff5(_0x38ef34,_0x36fea3(0x1e4)),_0x1af7ca(_0x361a17,_0x38ef34);}function _0x3fbf28(_0x1609bf,_0x372dbd){if(_0x1609bf!==_0x372dbd)throw new TypeError('Private\x20static\x20access\x20of\x20wrong\x20provenance');}function _0x234ff5(_0x5dcf40,_0xa0e3be){var _0x4d5828=_0x5612de;if(void(0x9*0x3b9+-0x7*-0x2bb+-0x382*0xf)===_0x5dcf40)throw new TypeError(_0x4d5828(0x2ad)+_0xa0e3be+'\x20private\x20static\x20field\x20before\x20its\x20declaration');}function _0x3cd893(_0x4d4ac2,_0x117b1a,_0x13ebad,_0x227002){var _0x404d8d=_0x5612de,_0x21250e=_0xdaf8f6();if(_0x227002){for(var _0x1ad96b=-0x1*-0x109d+0x157+-0x11f4;_0x1ad96b<_0x227002[_0x404d8d(0x259)];_0x1ad96b++)_0x21250e=_0x227002[_0x1ad96b](_0x21250e);}var _0x176094=_0x117b1a(function(_0x32bc32){var _0x1f970f=_0x404d8d;_0x21250e[_0x1f970f(0x313)](_0x32bc32,_0x1fa626[_0x1f970f(0x265)]);},_0x13ebad),_0x1fa626=_0x21250e[_0x404d8d(0x2df)](_0x872852(_0x176094['d'][_0x404d8d(0x1cb)](_0x210308)),_0x4d4ac2);return _0x21250e[_0x404d8d(0x217)](_0x176094['F'],_0x1fa626[_0x404d8d(0x265)]),_0x21250e[_0x404d8d(0x165)](_0x176094['F'],_0x1fa626[_0x404d8d(0x39c)]);}function _0xdaf8f6(){var _0x3fa41d=_0x5612de;_0xdaf8f6=function(){return _0x513b0e;};var _0x513b0e={'elementsDefinitionOrder':[[_0x3fa41d(0x25a)],[_0x3fa41d(0x23c)]],'initializeInstanceElements':function(_0x179aa6,_0x25d5cd){var _0x557071=_0x3fa41d;[_0x557071(0x25a),_0x557071(0x23c)][_0x557071(0x254)](function(_0x2476e8){var _0x9b5c0b=_0x557071;_0x25d5cd[_0x9b5c0b(0x254)](function(_0x299a4a){var _0x53da95=_0x9b5c0b;_0x299a4a['kind']===_0x2476e8&&_0x53da95(0x235)===_0x299a4a[_0x53da95(0x215)]&&this[_0x53da95(0x2e1)](_0x179aa6,_0x299a4a);},this);},this);},'initializeClassElements':function(_0x300a4c,_0x38031f){var _0x450502=_0x3fa41d,_0x4e1581=_0x300a4c[_0x450502(0x344)];[_0x450502(0x25a),_0x450502(0x23c)]['forEach'](function(_0x2fde74){_0x38031f['forEach'](function(_0xe5e082){var _0x123341=w_0x25f3,_0x174ec4=_0xe5e082[_0x123341(0x215)];if(_0xe5e082[_0x123341(0x26f)]===_0x2fde74&&(_0x123341(0x396)===_0x174ec4||_0x123341(0x344)===_0x174ec4)){var _0x184ef0=_0x123341(0x396)===_0x174ec4?_0x300a4c:_0x4e1581;this[_0x123341(0x2e1)](_0x184ef0,_0xe5e082);}},this);},this);},'defineClassElement':function(_0x2e2a2c,_0x5db810){var _0x522104=_0x3fa41d,_0x2d7321=_0x5db810['descriptor'];if(_0x522104(0x23c)===_0x5db810[_0x522104(0x26f)]){var _0x554ae6=_0x5db810[_0x522104(0x2d0)];_0x2d7321={'enumerable':_0x2d7321[_0x522104(0x23a)],'writable':_0x2d7321[_0x522104(0x2e3)],'configurable':_0x2d7321[_0x522104(0x2bc)],'value':void(-0x1*0x3bd+0x1ef9+-0x1b3c)===_0x554ae6?void(0x1*0x1afe+0xcd0+0x7f6*-0x5):_0x554ae6[_0x522104(0x334)](_0x2e2a2c)};}Object[_0x522104(0x175)](_0x2e2a2c,_0x5db810['key'],_0x2d7321);},'decorateClass':function(_0x4d5101,_0x1a7961){var _0x129760=_0x3fa41d,_0x1a31cc=[],_0x3f7e89=[],_0xf8b039={'static':[],'prototype':[],'own':[]};if(_0x4d5101[_0x129760(0x254)](function(_0x5c0ba5){var _0x27d627=_0x129760;this[_0x27d627(0x33d)](_0x5c0ba5,_0xf8b039);},this),_0x4d5101[_0x129760(0x254)](function(_0x239102){var _0x151241=_0x129760;if(!_0x51f3b5(_0x239102))return _0x1a31cc[_0x151241(0x36e)](_0x239102);var _0x43ec40=this[_0x151241(0x1ac)](_0x239102,_0xf8b039);_0x1a31cc[_0x151241(0x36e)](_0x43ec40[_0x151241(0x206)]),_0x1a31cc[_0x151241(0x36e)]['apply'](_0x1a31cc,_0x43ec40[_0x151241(0x3bc)]),_0x3f7e89[_0x151241(0x36e)]['apply'](_0x3f7e89,_0x43ec40[_0x151241(0x39c)]);},this),!_0x1a7961)return{'elements':_0x1a31cc,'finishers':_0x3f7e89};var _0x857545=this[_0x129760(0x34c)](_0x1a31cc,_0x1a7961);return _0x3f7e89['push'][_0x129760(0x207)](_0x3f7e89,_0x857545[_0x129760(0x39c)]),_0x857545[_0x129760(0x39c)]=_0x3f7e89,_0x857545;},'addElementPlacement':function(_0x9cedb8,_0x4c2171,_0xa6e6c8){var _0x5d3aa1=_0x3fa41d,_0x33e5e0=_0x4c2171[_0x9cedb8[_0x5d3aa1(0x215)]];if(!_0xa6e6c8&&-(-0x1a83+-0x1*-0x16ee+0x99*0x6)!==_0x33e5e0['indexOf'](_0x9cedb8['key']))throw new TypeError('Duplicated\x20element\x20('+_0x9cedb8[_0x5d3aa1(0x392)]+')');_0x33e5e0[_0x5d3aa1(0x36e)](_0x9cedb8[_0x5d3aa1(0x392)]);},'decorateElement':function(_0x463258,_0x11d852){var _0x495609=_0x3fa41d;for(var _0x4c5aa5=[],_0x46b095=[],_0x106243=_0x463258['decorators'],_0x195d25=_0x106243[_0x495609(0x259)]-(-0x1d5c*-0x1+0x1aa9+0x956*-0x6);_0x195d25>=0x18ee+0x1*0x1c19+0xf*-0x389;_0x195d25--){var _0x425891=_0x11d852[_0x463258[_0x495609(0x215)]];_0x425891[_0x495609(0x2ec)](_0x425891[_0x495609(0x2c5)](_0x463258[_0x495609(0x392)]),0x1*0xca7+-0xaf*-0xb+-0x142b);var _0x30722d=this[_0x495609(0x372)](_0x463258),_0x15a14c=this[_0x495609(0x18a)]((0x61*0x2d+0x1545+-0x1*0x2652,_0x106243[_0x195d25])(_0x30722d)||_0x30722d);_0x463258=_0x15a14c['element'],this['addElementPlacement'](_0x463258,_0x11d852),_0x15a14c[_0x495609(0x2f4)]&&_0x46b095[_0x495609(0x36e)](_0x15a14c[_0x495609(0x2f4)]);var _0x98e9dc=_0x15a14c[_0x495609(0x3bc)];if(_0x98e9dc){for(var _0x56dd4d=0xb*0x12e+-0x1f17+-0x121d*-0x1;_0x56dd4d<_0x98e9dc[_0x495609(0x259)];_0x56dd4d++)this[_0x495609(0x33d)](_0x98e9dc[_0x56dd4d],_0x11d852);_0x4c5aa5[_0x495609(0x36e)]['apply'](_0x4c5aa5,_0x98e9dc);}}return{'element':_0x463258,'finishers':_0x46b095,'extras':_0x4c5aa5};},'decorateConstructor':function(_0x2a4d32,_0x2666ea){var _0x5e156c=_0x3fa41d;for(var _0xb2f010=[],_0x17b2e4=_0x2666ea[_0x5e156c(0x259)]-(0x21c0+-0xc8e*0x2+-0x8a3*0x1);_0x17b2e4>=-0x1c70+-0x1*-0x1256+-0x1af*-0x6;_0x17b2e4--){var _0x222759=this[_0x5e156c(0x193)](_0x2a4d32),_0xc41f78=this[_0x5e156c(0x324)]((0x1f46+0x81d*0x2+-0x5f*0x80,_0x2666ea[_0x17b2e4])(_0x222759)||_0x222759);if(void(0x1373+0x2b1*-0x2+-0x1*0xe11)!==_0xc41f78[_0x5e156c(0x2f4)]&&_0xb2f010[_0x5e156c(0x36e)](_0xc41f78[_0x5e156c(0x2f4)]),void(0x31*-0xb5+-0x11*0x67+0x84c*0x5)!==_0xc41f78['elements']){_0x2a4d32=_0xc41f78[_0x5e156c(0x265)];for(var _0x38c2da=-0x1*-0xc12+0x7*-0x28d+0x5c9;_0x38c2da<_0x2a4d32[_0x5e156c(0x259)]-(0x59f*0x6+-0x1*-0x1c6d+0x25*-0x1ae);_0x38c2da++)for(var _0x2950ed=_0x38c2da+(0x1b91*0x1+-0xbc0+-0x8*0x1fa);_0x2950ed<_0x2a4d32['length'];_0x2950ed++)if(_0x2a4d32[_0x38c2da][_0x5e156c(0x392)]===_0x2a4d32[_0x2950ed][_0x5e156c(0x392)]&&_0x2a4d32[_0x38c2da][_0x5e156c(0x215)]===_0x2a4d32[_0x2950ed][_0x5e156c(0x215)])throw new TypeError(_0x5e156c(0x367)+_0x2a4d32[_0x38c2da][_0x5e156c(0x392)]+')');}}return{'elements':_0x2a4d32,'finishers':_0xb2f010};},'fromElementDescriptor':function(_0x4a43c8){var _0x302152=_0x3fa41d,_0x406d6b={'kind':_0x4a43c8[_0x302152(0x26f)],'key':_0x4a43c8['key'],'placement':_0x4a43c8['placement'],'descriptor':_0x4a43c8[_0x302152(0x20f)]};return Object[_0x302152(0x175)](_0x406d6b,Symbol[_0x302152(0x1c0)],{'value':_0x302152(0x1e8),'configurable':!(-0x1b3a+0x21bb*0x1+-0x14d*0x5)}),_0x302152(0x23c)===_0x4a43c8['kind']&&(_0x406d6b[_0x302152(0x2d0)]=_0x4a43c8['initializer']),_0x406d6b;},'toElementDescriptors':function(_0x14be66){if(void(0x1f48+-0x19fe+0x2a5*-0x2)!==_0x14be66)return _0x5c885(_0x14be66)['map'](function(_0x170c20){var _0x5285ee=w_0x25f3,_0xf0c7ce=this[_0x5285ee(0x25e)](_0x170c20);return this['disallowProperty'](_0x170c20,_0x5285ee(0x2f4),_0x5285ee(0x21f)),this[_0x5285ee(0x21a)](_0x170c20,_0x5285ee(0x3bc),_0x5285ee(0x21f)),_0xf0c7ce;},this);},'toElementDescriptor':function(_0x4c8a60){var _0x1fc4d7=_0x3fa41d,_0x33cfb6=String(_0x4c8a60[_0x1fc4d7(0x26f)]);if(_0x1fc4d7(0x25a)!==_0x33cfb6&&_0x1fc4d7(0x23c)!==_0x33cfb6)throw new TypeError('An\x20element\x20descriptor\x27s\x20.kind\x20property\x20must\x20be\x20either\x20\x22method\x22\x20or\x20\x22field\x22,\x20but\x20a\x20decorator\x20created\x20an\x20element\x20descriptor\x20with\x20.kind\x20\x22'+_0x33cfb6+'\x22');var _0xebb768=_0x32e885(_0x4c8a60[_0x1fc4d7(0x392)]),_0x52ab63=String(_0x4c8a60[_0x1fc4d7(0x215)]);if(_0x1fc4d7(0x396)!==_0x52ab63&&_0x1fc4d7(0x344)!==_0x52ab63&&_0x1fc4d7(0x235)!==_0x52ab63)throw new TypeError(_0x1fc4d7(0x2b5)+_0x52ab63+'\x22');var _0x33ee46=_0x4c8a60[_0x1fc4d7(0x20f)];this[_0x1fc4d7(0x21a)](_0x4c8a60,'elements',_0x1fc4d7(0x21f));var _0x1ca161={'kind':_0x33cfb6,'key':_0xebb768,'placement':_0x52ab63,'descriptor':Object[_0x1fc4d7(0x2a0)]({},_0x33ee46)};return _0x1fc4d7(0x23c)!==_0x33cfb6?this[_0x1fc4d7(0x21a)](_0x4c8a60,_0x1fc4d7(0x2d0),_0x1fc4d7(0x28d)):(this['disallowProperty'](_0x33ee46,_0x1fc4d7(0x32b),_0x1fc4d7(0x277)),this['disallowProperty'](_0x33ee46,'set',_0x1fc4d7(0x277)),this['disallowProperty'](_0x33ee46,_0x1fc4d7(0x2e4),_0x1fc4d7(0x277)),_0x1ca161['initializer']=_0x4c8a60[_0x1fc4d7(0x2d0)]),_0x1ca161;},'toElementFinisherExtras':function(_0x53b840){var _0x1157c0=_0x3fa41d;return{'element':this[_0x1157c0(0x25e)](_0x53b840),'finisher':_0x22df7b(_0x53b840,_0x1157c0(0x2f4)),'extras':this[_0x1157c0(0x370)](_0x53b840['extras'])};},'fromClassDescriptor':function(_0x30c308){var _0x482b04=_0x3fa41d,_0xf231c1={'kind':_0x482b04(0x19c),'elements':_0x30c308['map'](this[_0x482b04(0x372)],this)};return Object[_0x482b04(0x175)](_0xf231c1,Symbol[_0x482b04(0x1c0)],{'value':_0x482b04(0x1e8),'configurable':!(0xf*-0xf1+0x1*-0xcfe+-0x1b1d*-0x1)}),_0xf231c1;},'toClassDescriptor':function(_0x178ca0){var _0x2520a1=_0x3fa41d,_0x79f336=String(_0x178ca0[_0x2520a1(0x26f)]);if('class'!==_0x79f336)throw new TypeError('A\x20class\x20descriptor\x27s\x20.kind\x20property\x20must\x20be\x20\x22class\x22,\x20but\x20a\x20decorator\x20created\x20a\x20class\x20descriptor\x20with\x20.kind\x20\x22'+_0x79f336+'\x22');this['disallowProperty'](_0x178ca0,_0x2520a1(0x392),'A\x20class\x20descriptor'),this[_0x2520a1(0x21a)](_0x178ca0,_0x2520a1(0x215),'A\x20class\x20descriptor'),this[_0x2520a1(0x21a)](_0x178ca0,_0x2520a1(0x20f),_0x2520a1(0x1a1)),this['disallowProperty'](_0x178ca0,'initializer',_0x2520a1(0x1a1)),this[_0x2520a1(0x21a)](_0x178ca0,_0x2520a1(0x3bc),'A\x20class\x20descriptor');var _0x154e32=_0x22df7b(_0x178ca0,_0x2520a1(0x2f4));return{'elements':this['toElementDescriptors'](_0x178ca0['elements']),'finisher':_0x154e32};},'runClassFinishers':function(_0x58401c,_0x3ff489){var _0x4a8de1=_0x3fa41d;for(var _0x5ec766=0x13b0+-0xe05+0x1*-0x5ab;_0x5ec766<_0x3ff489[_0x4a8de1(0x259)];_0x5ec766++){var _0x176fd2=(0x7d*-0x25+0x101*0x14+0x5*-0x67,_0x3ff489[_0x5ec766])(_0x58401c);if(void(-0x1*0x200+0x1c4a+-0x1a4a)!==_0x176fd2){if(_0x4a8de1(0x1ee)!=typeof _0x176fd2)throw new TypeError('Finishers\x20must\x20return\x20a\x20constructor.');_0x58401c=_0x176fd2;}}return _0x58401c;},'disallowProperty':function(_0x376492,_0x1998df,_0x46cda4){var _0x5b24b6=_0x3fa41d;if(void(0x2239+-0xa67+-0x17d2)!==_0x376492[_0x1998df])throw new TypeError(_0x46cda4+_0x5b24b6(0x232)+_0x1998df+_0x5b24b6(0x2a1));}};return _0x513b0e;}function _0x210308(_0x5bae0){var _0x509268=_0x5612de,_0x2061f6,_0x1daa96=_0x32e885(_0x5bae0['key']);_0x509268(0x25a)===_0x5bae0[_0x509268(0x26f)]?_0x2061f6={'value':_0x5bae0[_0x509268(0x2e4)],'writable':!(-0x286+-0x3*-0x83b+-0x162b),'configurable':!(-0x1*-0x13f9+-0x2*-0x1127+-0x3647),'enumerable':!(-0x210*-0x1+0xf43*-0x2+0x1c77)}:_0x509268(0x32b)===_0x5bae0[_0x509268(0x26f)]?_0x2061f6={'get':_0x5bae0['value'],'configurable':!(-0x4*-0x87+0x25b6+-0x13e9*0x2),'enumerable':!(0x4*-0x105+-0x8*0x3b+0x5ed*0x1)}:'set'===_0x5bae0[_0x509268(0x26f)]?_0x2061f6={'set':_0x5bae0[_0x509268(0x2e4)],'configurable':!(-0x1*-0xd7c+0x24e2+-0x325e),'enumerable':!(0x246b*-0x1+-0xbdc+0x67*0x78)}:_0x509268(0x23c)===_0x5bae0['kind']&&(_0x2061f6={'configurable':!(-0x7*0x7c+0x0+-0x1*-0x364),'writable':!(0x10cc+0x93a+-0x1a06),'enumerable':!(-0x339+-0xfd1*-0x1+-0xc98)});var _0x12b911={'kind':_0x509268(0x23c)===_0x5bae0[_0x509268(0x26f)]?_0x509268(0x23c):'method','key':_0x1daa96,'placement':_0x5bae0[_0x509268(0x396)]?_0x509268(0x396):'field'===_0x5bae0[_0x509268(0x26f)]?'own':_0x509268(0x344),'descriptor':_0x2061f6};return _0x5bae0['decorators']&&(_0x12b911[_0x509268(0x1b7)]=_0x5bae0[_0x509268(0x1b7)]),_0x509268(0x23c)===_0x5bae0['kind']&&(_0x12b911['initializer']=_0x5bae0[_0x509268(0x2e4)]),_0x12b911;}function _0x3b2c5d(_0x1d2cd7,_0x5043bf){var _0x6af88f=_0x5612de;void(-0x1c1e+-0x19*-0xd9+-0x1*-0x6ed)!==_0x1d2cd7[_0x6af88f(0x20f)][_0x6af88f(0x32b)]?_0x5043bf['descriptor'][_0x6af88f(0x32b)]=_0x1d2cd7['descriptor'][_0x6af88f(0x32b)]:_0x5043bf['descriptor']['set']=_0x1d2cd7[_0x6af88f(0x20f)][_0x6af88f(0x1e4)];}function _0x872852(_0x11fd7b){var _0x45b70e=_0x5612de;for(var _0x3d0947=[],_0x1e20eb=function(_0x3c1862){var _0x56210d=w_0x25f3;return _0x56210d(0x25a)===_0x3c1862[_0x56210d(0x26f)]&&_0x3c1862[_0x56210d(0x392)]===_0x44502e['key']&&_0x3c1862[_0x56210d(0x215)]===_0x44502e[_0x56210d(0x215)];},_0x2773ae=-0xdd7+0x5*0x3f6+0x5f7*-0x1;_0x2773ae<_0x11fd7b[_0x45b70e(0x259)];_0x2773ae++){var _0x152571,_0x44502e=_0x11fd7b[_0x2773ae];if(_0x45b70e(0x25a)===_0x44502e[_0x45b70e(0x26f)]&&(_0x152571=_0x3d0947['find'](_0x1e20eb))){if(_0x3f9244(_0x44502e[_0x45b70e(0x20f)])||_0x3f9244(_0x152571[_0x45b70e(0x20f)])){if(_0x51f3b5(_0x44502e)||_0x51f3b5(_0x152571))throw new ReferenceError('Duplicated\x20methods\x20('+_0x44502e[_0x45b70e(0x392)]+_0x45b70e(0x20b));_0x152571[_0x45b70e(0x20f)]=_0x44502e[_0x45b70e(0x20f)];}else{if(_0x51f3b5(_0x44502e)){if(_0x51f3b5(_0x152571))throw new ReferenceError('Decorators\x20can\x27t\x20be\x20placed\x20on\x20different\x20accessors\x20with\x20for\x20the\x20same\x20property\x20('+_0x44502e['key']+').');_0x152571[_0x45b70e(0x1b7)]=_0x44502e[_0x45b70e(0x1b7)];}_0x3b2c5d(_0x44502e,_0x152571);}}else _0x3d0947[_0x45b70e(0x36e)](_0x44502e);}return _0x3d0947;}function _0x51f3b5(_0x3b7753){var _0x46d225=_0x5612de;return _0x3b7753[_0x46d225(0x1b7)]&&_0x3b7753['decorators']['length'];}function _0x3f9244(_0x197382){var _0x1ec41e=_0x5612de;return void(0xa8+0x12a*-0x4+0x400)!==_0x197382&&!(void(-0x2*-0x955+-0x965+-0x945)===_0x197382[_0x1ec41e(0x2e4)]&&void(-0x403+0xd*0x1e2+-0x1477)===_0x197382[_0x1ec41e(0x2e3)]);}function _0x22df7b(_0x1a9e0b,_0x4c3aa6){var _0x2004a6=_0x5612de,_0x42d7e1=_0x1a9e0b[_0x4c3aa6];if(void(-0x1ab*-0x11+-0x2164+0x1*0x509)!==_0x42d7e1&&_0x2004a6(0x1ee)!=typeof _0x42d7e1)throw new TypeError('Expected\x20\x27'+_0x4c3aa6+_0x2004a6(0x200));return _0x42d7e1;}function _0x44a779(_0x290bd9,_0x3c4184,_0x356c04){var _0x5e1d0b=_0x5612de;if(!_0x3c4184[_0x5e1d0b(0x2fc)](_0x290bd9))throw new TypeError(_0x5e1d0b(0x187));return _0x356c04;}function _0x227cd9(_0x44aab6,_0x47792b){var _0x50e6e8=_0x5612de;if(_0x47792b[_0x50e6e8(0x2fc)](_0x44aab6))throw new TypeError('Cannot\x20initialize\x20the\x20same\x20private\x20elements\x20twice\x20on\x20an\x20object');}function _0x41d2eb(_0x2d54de,_0x30b42b,_0x3cc210){_0x227cd9(_0x2d54de,_0x30b42b),_0x30b42b['set'](_0x2d54de,_0x3cc210);}function _0x4fd04a(_0x1b36b3,_0x1a6559){var _0x1aaac6=_0x5612de;_0x227cd9(_0x1b36b3,_0x1a6559),_0x1a6559[_0x1aaac6(0x332)](_0x1b36b3);}function _0x4fb313(){throw new TypeError('attempted\x20to\x20reassign\x20private\x20method');}function _0x4e4f66(_0x9f7990){return _0x9f7990;}_0x5612de(0x1ee)!=typeof Object[_0x5612de(0x2a0)]&&Object['defineProperty'](Object,_0x5612de(0x2a0),{'value':function(_0x478fa2,_0x273756){var _0x2b8676=_0x5612de;if(null==_0x478fa2)throw new TypeError(_0x2b8676(0x2cf));for(var _0x451cfb=Object(_0x478fa2),_0x5c5079=-0xb5d+-0x15f0+0x214e;_0x5c5079>0xd03+-0x269a+0x1*0x199b&0xa64*0x3+-0xf7b+-0xfa2]+_0x100715[0x2bb+-0x3*0x92d+0x18db&_0x1cb307],_0x1cb307<-0x1*-0x82+0x34*-0x3b+-0x2*-0x5c5&&(_0x1cb307<0x10f1*0x1+0x86a*0x4+-0x328f?_0x37750d[-0x2029+-0x1ef1+-0x3f4a*-0x1+_0x1cb307]=_0x1cb307:_0x37750d[-0x2485+0x1d*0x6d+0x1883+_0x1cb307]=_0x1cb307);var _0x55de18=function(_0x33a277){var _0x5155ba=_0x5612de;for(var _0x9eb2a1=_0x33a277[_0x5155ba(0x259)],_0x1edfde='',_0x2a69c4=-0x1c39+0x569+0x16d0;_0x2a69c4<_0x9eb2a1;)_0x1edfde+=_0x1f510e[_0x33a277[_0x2a69c4++]];return _0x1edfde;},_0x655940=function(_0x177456){for(var _0x1ffb9c=_0x177456['length']>>0x1*0x104b+0x10b1+-0x20fb,_0x3563ef=_0x1ffb9c<<-0x1*-0x263+-0x77*-0x21+0xd*-0x15d,_0x22520c=new Uint8Array(_0x1ffb9c),_0x32553=0x345*0x9+-0x1e9a+-0x1*-0x12d,_0x11b847=0x1*0x623+-0x239f*-0x1+-0x29c2;_0x11b847<_0x3563ef;)_0x22520c[_0x32553++]=_0x37750d[_0x177456['charCodeAt'](_0x11b847++)]<<-0x1551+-0xb5a+0x20af*0x1|_0x37750d[_0x177456['charCodeAt'](_0x11b847++)];return _0x22520c;},_0x42e709={'encode':_0x55de18,'decode':_0x655940},_0x1e7721=_0x5612de(0x384)!=typeof globalThis?globalThis:_0x5612de(0x384)!=typeof window?window:_0x5612de(0x384)!=typeof global?global:'undefined'!=typeof self?self:{};function _0x3bc8d3(_0x14565d){var _0x442e14=_0x5612de;return _0x14565d&&_0x14565d[_0x442e14(0x3bd)]&&Object[_0x442e14(0x344)][_0x442e14(0x3b8)][_0x442e14(0x334)](_0x14565d,_0x442e14(0x1d3))?_0x14565d[_0x442e14(0x1d3)]:_0x14565d;}function _0x3abc0b(_0x1ca7bd){var _0x292845=_0x5612de;return _0x1ca7bd&&Object[_0x292845(0x344)]['hasOwnProperty'][_0x292845(0x334)](_0x1ca7bd,_0x292845(0x1d3))?_0x1ca7bd[_0x292845(0x1d3)]:_0x1ca7bd;}function _0x41ace1(_0x2b7455){var _0x39470d=_0x5612de;return _0x2b7455&&Object[_0x39470d(0x344)][_0x39470d(0x3b8)]['call'](_0x2b7455,_0x39470d(0x1d3))&&0x155+0x16f8+-0x184c===Object[_0x39470d(0x17f)](_0x2b7455)[_0x39470d(0x259)]?_0x2b7455[_0x39470d(0x1d3)]:_0x2b7455;}function _0x3e9554(_0x22797e){var _0x1fa97f=_0x5612de;if(_0x22797e[_0x1fa97f(0x3bd)])return _0x22797e;var _0x21d08f=Object[_0x1fa97f(0x175)]({},_0x1fa97f(0x3bd),{'value':!(0x1f82+-0x10d*0x1d+0x5*-0x35)});return Object[_0x1fa97f(0x17f)](_0x22797e)['forEach'](function(_0x19f7e0){var _0x1f5a81=_0x1fa97f,_0x3cf185=Object[_0x1f5a81(0x2a6)](_0x22797e,_0x19f7e0);Object['defineProperty'](_0x21d08f,_0x19f7e0,_0x3cf185[_0x1f5a81(0x32b)]?_0x3cf185:{'enumerable':!(-0x222*-0x8+0x1*-0x21ac+0x84e*0x2),'get':function(){return _0x22797e[_0x19f7e0];}});}),_0x21d08f;}function _0x56409e(_0x362d96){var _0x265452=_0x5612de,_0x5870ef={'exports':{}};return _0x362d96(_0x5870ef,_0x5870ef[_0x265452(0x1b8)]),_0x5870ef['exports'];}function _0x171dc9(_0x5983b9){var _0x32782f=_0x5612de;throw new Error(_0x32782f(0x36d)+_0x5983b9+_0x32782f(0x357));}var _0x90795=_0x56409e(function(_0xb9c7d1){!(function(){var _0x2db296=w_0x25f3,_0x1f977a='input\x20is\x20invalid\x20type',_0x5a87b4=_0x2db296(0x17d)==typeof window,_0x11d177=_0x5a87b4?window:{};_0x11d177[_0x2db296(0x37a)]&&(_0x5a87b4=!(-0x15c4+0x97d*-0x2+0xb7*0x39));var _0x387fd8=!_0x5a87b4&&_0x2db296(0x17d)==typeof self,_0x2e1226=!_0x11d177[_0x2db296(0x216)]&&_0x2db296(0x17d)==typeof process&&process[_0x2db296(0x194)]&&process[_0x2db296(0x194)][_0x2db296(0x287)];_0x2e1226?_0x11d177=_0x1e7721:_0x387fd8&&(_0x11d177=self);var _0x299a21=!_0x11d177['JS_MD5_NO_COMMON_JS']&&_0xb9c7d1[_0x2db296(0x1b8)],_0x5efc8f=!(-0x8c4+-0xc3a+-0x1*-0x14ff),_0x1edc95=!_0x11d177[_0x2db296(0x2e6)]&&'undefined'!=typeof ArrayBuffer,_0x142491=_0x2db296(0x2d7)[_0x2db296(0x342)](''),_0x146907=[0x148*0x5+-0x753*0x2+-0x175*-0x6,0x1*0x340b+0x460a+0x1f9*0x3,0x2766cb+-0x6327ac+0x353*0x387b,-(-0xc13e2aae+-0x3aff8440+0x17c3daeee)],_0x4d13bd=[-0x1721+-0x1*0x611+0x1d32,-0x390+-0x3a5*-0x3+-0x757,0x5fc+-0x64d*-0x2+-0x1286,0x278+0x3*0x105+-0x56f],_0x3f581b=[_0x2db296(0x352),_0x2db296(0x238),_0x2db296(0x19b),_0x2db296(0x275),'arrayBuffer',_0x2db296(0x25f)],_0xcb4d61=_0x2db296(0x315)[_0x2db296(0x342)](''),_0x5c5e45=[],_0x54b265;if(_0x1edc95){var _0x171107=new ArrayBuffer(0xc87+-0xcd8+0x95);_0x54b265=new Uint8Array(_0x171107),_0x5c5e45=new Uint32Array(_0x171107);}!_0x11d177[_0x2db296(0x216)]&&Array[_0x2db296(0x2af)]||(Array['isArray']=function(_0x263669){var _0x1dd8cd=_0x2db296;return _0x1dd8cd(0x331)===Object['prototype']['toString']['call'](_0x263669);}),!_0x1edc95||!_0x11d177[_0x2db296(0x305)]&&ArrayBuffer['isView']||(ArrayBuffer['isView']=function(_0x4d052f){var _0x23ceed=_0x2db296;return _0x23ceed(0x17d)==typeof _0x4d052f&&_0x4d052f[_0x23ceed(0x275)]&&_0x4d052f['buffer'][_0x23ceed(0x2ac)]===ArrayBuffer;});var _0x2e393f=function(_0x55b71b){return function(_0x6901ec){var _0x38b178=w_0x25f3;return new _0x22f902(!(0x191d+0x9e+-0x19bb))[_0x38b178(0x2d5)](_0x6901ec)[_0x55b71b]();};},_0x1edabb=function(){var _0x5664ca=_0x2db296,_0x4c275f=_0x2e393f(_0x5664ca(0x352));_0x2e1226&&(_0x4c275f=_0x4d7e2d(_0x4c275f)),_0x4c275f[_0x5664ca(0x3b7)]=function(){return new _0x22f902();},_0x4c275f['update']=function(_0x5c3464){return _0x4c275f['create']()['update'](_0x5c3464);};for(var _0x349d5f=-0x9d*-0x1f+0x1*0x230c+-0x15*0x293;_0x349d5f<_0x3f581b[_0x5664ca(0x259)];++_0x349d5f){var _0x1537c4=_0x3f581b[_0x349d5f];_0x4c275f[_0x1537c4]=_0x2e393f(_0x1537c4);}return _0x4c275f;},_0x4d7e2d=function(_0x1afe1b){var _0x3e0a6e=eval('var _0x13db80 = w_0x25f3;require(_0x13db80(628));'),_0x5bfe7e=eval('var _0x20dc8b = w_0x25f3;require(\'buffer\')[_0x20dc8b(424)];'),_0x3d7396=function(_0x5142ff){var _0x30639a=w_0x25f3;if(_0x30639a(0x33c)==typeof _0x5142ff)return _0x3e0a6e[_0x30639a(0x1ca)](_0x30639a(0x35c))[_0x30639a(0x2d5)](_0x5142ff,_0x30639a(0x2a8))[_0x30639a(0x19b)](_0x30639a(0x352));if(null==_0x5142ff)throw _0x1f977a;return _0x5142ff[_0x30639a(0x2ac)]===ArrayBuffer&&(_0x5142ff=new Uint8Array(_0x5142ff)),Array[_0x30639a(0x2af)](_0x5142ff)||ArrayBuffer[_0x30639a(0x2c7)](_0x5142ff)||_0x5142ff[_0x30639a(0x2ac)]===_0x5bfe7e?_0x3e0a6e[_0x30639a(0x1ca)](_0x30639a(0x35c))['update'](new _0x5bfe7e(_0x5142ff))[_0x30639a(0x19b)](_0x30639a(0x352)):_0x1afe1b(_0x5142ff);};return _0x3d7396;};function _0x22f902(_0xad0da7){var _0x68ef08=_0x2db296;if(_0xad0da7)_0x5c5e45[-0x1b4c*0x1+-0x126b*0x2+0x4022]=_0x5c5e45[0x143f+-0x2217+0xde8]=_0x5c5e45[0x16*0x2e+-0x1d7c+0x1989]=_0x5c5e45[0xa0*-0x26+-0x3*0x93f+0x337f*0x1]=_0x5c5e45[-0xed2+0xe1d+0x2e*0x4]=_0x5c5e45[-0x10ff+-0x1*-0x394+0xd6f]=_0x5c5e45[-0x36f*-0x2+0x1*0x1a5+-0x87e]=_0x5c5e45[-0x1f81*0x1+0x25ce*-0x1+-0x1*-0x4555]=_0x5c5e45[0x1ae7*0x1+0x1*-0xe3c+-0xca4*0x1]=_0x5c5e45[-0x583*-0x4+-0x5f7*-0x5+-0x33d7]=_0x5c5e45[0x25e+-0x1d4b*0x1+-0x3a*-0x77]=_0x5c5e45[0xef9+-0x30*-0x57+-0x1f3f]=_0x5c5e45[0x11e+-0x18f*0x7+0x9d6]=_0x5c5e45[-0x2521*-0x1+0x1*0x1bf4+0x4109*-0x1]=_0x5c5e45[0x19*0xa6+0x1f06+-0x2f2f]=_0x5c5e45[-0x22d0+0x1f1*0xf+0x5bf]=_0x5c5e45[0x7*0x549+-0xa39+0x1*-0x1ab7]=0x1*-0xfb+0x1995+0xc4d*-0x2,this['blocks']=_0x5c5e45,this[_0x68ef08(0x329)]=_0x54b265;else{if(_0x1edc95){var _0x44d65b=new ArrayBuffer(0x11*-0xb+0xbfd+-0xafe);this[_0x68ef08(0x329)]=new Uint8Array(_0x44d65b),this[_0x68ef08(0x319)]=new Uint32Array(_0x44d65b);}else this[_0x68ef08(0x319)]=[0x45*0x10+-0xd15+0x8c5,0x1d70+0x493+-0x2203*0x1,0x46*0x86+0x8*-0x14e+-0x1a34,0x9bb+0x25*0xdf+-0x29f6,-0x1c*-0xda+0x188+0xcb*-0x20,-0x442+-0x199a+0x1ddc,-0x2*-0x32d+-0x605*-0x2+-0x1264,0x1af*-0x8+-0xf3c+0x29c*0xb,-0x8cc+0x1c3+0x709*0x1,0xb53*0x1+0x18e6+-0x3*0xc13,-0x2*0x135e+0x444+0x2278,0xe8*0x2b+0x2106+-0xa*0x733,0x3*0x449+0x1103+-0x1dde,0x30+-0x3*-0x95+-0x1ef,0x3ca*-0x3+-0x9*-0x1cb+-0x3*0x197,-0x14ce+-0x1b9+0x1687,-0xb2f+-0x53f*0x3+0x1aec];}this['h0']=this['h1']=this['h2']=this['h3']=this[_0x68ef08(0x385)]=this[_0x68ef08(0x25b)]=this[_0x68ef08(0x1cd)]=-0x1*0x4f9+0xeb*-0xd+0x10e8,this[_0x68ef08(0x182)]=this[_0x68ef08(0x1e9)]=!(-0x983+-0x5b1*-0x1+0x3d3),this[_0x68ef08(0x19f)]=!(-0xcdf+0x2157+-0x1478);}_0x22f902[_0x2db296(0x344)][_0x2db296(0x2d5)]=function(_0x36f09d){var _0x449340=_0x2db296;if(!this['finalized']){var _0x462898,_0x1dd68e=typeof _0x36f09d;if(_0x449340(0x33c)!==_0x1dd68e){if('object'!==_0x1dd68e)throw _0x1f977a;if(null===_0x36f09d)throw _0x1f977a;if(_0x1edc95&&_0x36f09d['constructor']===ArrayBuffer)_0x36f09d=new Uint8Array(_0x36f09d);else{if(!(Array[_0x449340(0x2af)](_0x36f09d)||_0x1edc95&&ArrayBuffer['isView'](_0x36f09d)))throw _0x1f977a;}_0x462898=!(-0xfea+0x449*0x3+-0x1*-0x30f);}for(var _0x2c6513,_0x1fd15b,_0x432ef6=0xadd+-0x4d7*0x2+0x1*-0x12f,_0x383058=_0x36f09d[_0x449340(0x259)],_0x17a6ed=this[_0x449340(0x319)],_0x161a31=this[_0x449340(0x329)];_0x432ef6<_0x383058;){if(this[_0x449340(0x1e9)]&&(this[_0x449340(0x1e9)]=!(-0x657+0x4fb+-0x15d*-0x1),_0x17a6ed[0x22e1*-0x1+-0xeb2+0x3193]=_0x17a6ed[0x283*-0xd+-0x141e+0x34d5],_0x17a6ed[0x1af3*0x1+-0x14d1*0x1+-0x15*0x4a]=_0x17a6ed[-0xc3*-0x2f+-0x49*-0x31+0x3*-0x1097]=_0x17a6ed[0x1e34+-0x1*-0x1837+-0x3669]=_0x17a6ed[-0x12e3+-0x1733*-0x1+0x1*-0x44d]=_0x17a6ed[0x31*-0x8b+0x1643*0x1+0x45c]=_0x17a6ed[0x87*-0xd+-0x14d5+0x1bb5]=_0x17a6ed[0x1d70+0x255a+0x4*-0x10b1]=_0x17a6ed[0x1312+0x2e6+0x15f1*-0x1]=_0x17a6ed[0x1ed9+0x551*-0x1+-0x1980]=_0x17a6ed[0x269*-0x7+0x1d8+0xf10]=_0x17a6ed[0x1134+-0x1*-0x15cd+-0xcfd*0x3]=_0x17a6ed[0x1229*0x1+0x23*-0xe3+0xceb]=_0x17a6ed[0x15*0x13a+-0x23e3*0x1+0x1*0xa2d]=_0x17a6ed[-0x4*0x8ee+-0x1d7d+0x4142*0x1]=_0x17a6ed[0x9d9+0x1*-0x215b+0x34*0x74]=_0x17a6ed[0x1e72+-0x9*0x2b+0xa8*-0x2c]=0x857+0x1cef+-0x2546),_0x462898){if(_0x1edc95){for(_0x1fd15b=this[_0x449340(0x385)];_0x432ef6<_0x383058&&_0x1fd15b<-0x836+0x1150+-0xce*0xb;++_0x432ef6)_0x161a31[_0x1fd15b++]=_0x36f09d[_0x432ef6];}else{for(_0x1fd15b=this[_0x449340(0x385)];_0x432ef6<_0x383058&&_0x1fd15b<-0x4*-0x4e8+-0xedb*0x1+-0x485;++_0x432ef6)_0x17a6ed[_0x1fd15b>>0x11*0x5c+0x1378*-0x2+0x3*0xaf2]|=_0x36f09d[_0x432ef6]<<_0x4d13bd[0x1*-0x4f+-0x2036+-0x1044*-0x2&_0x1fd15b++];}}else{if(_0x1edc95){for(_0x1fd15b=this[_0x449340(0x385)];_0x432ef6<_0x383058&&_0x1fd15b<0xc4*-0x1+0x78f*-0x1+0x893;++_0x432ef6)(_0x2c6513=_0x36f09d[_0x449340(0x195)](_0x432ef6))<0xd*0x1a+0x8f+-0x161?_0x161a31[_0x1fd15b++]=_0x2c6513:_0x2c6513<0x1b9a+0x1*-0x4a9+0xef1*-0x1?(_0x161a31[_0x1fd15b++]=-0x1aaa*-0x1+0x2511*-0x1+-0x23b*-0x5|_0x2c6513>>0x1568+0x75a*0x1+-0x994*0x3,_0x161a31[_0x1fd15b++]=0x54a+-0x1*0x1a66+-0x1cd*-0xc|0x8ad*0x4+0x6a3*0x1+-0x2918&_0x2c6513):_0x2c6513<0x1807f+-0x1*-0x44d4+0xed53*-0x1||_0x2c6513>=-0x1*-0x123e4+0x1be31+-0x5*0x66d1?(_0x161a31[_0x1fd15b++]=0x1*0x11f+0xffd*-0x1+0xfbe|_0x2c6513>>0x1*0x65+0x533+-0x11c*0x5,_0x161a31[_0x1fd15b++]=0x4*0x72b+0x23c9*0x1+-0x3ff5|_0x2c6513>>0x9*0x9+0x5*0x517+-0x5*0x526&0x200b+0x1*0xbe7+-0x2bb3,_0x161a31[_0x1fd15b++]=0x1*0x128f+0x11*-0x6a+-0xb05|0x27*0x59+-0x2126+0x1*0x13d6&_0x2c6513):(_0x2c6513=-0xb295+0x185*0x139+-0x2908+((-0x1ec2*-0x1+-0x183e+-0xf*0x2b&_0x2c6513)<<0x2e*0x3+0x14b+0x1*-0x1cb|0x1225*0x1+0x2376+-0x319c&_0x36f09d[_0x449340(0x195)](++_0x432ef6)),_0x161a31[_0x1fd15b++]=-0x969+0xc97+-0x23e|_0x2c6513>>-0xc19+-0x14f9+0x2124,_0x161a31[_0x1fd15b++]=0x202+0x1*0x187+-0x309|_0x2c6513>>-0x1da3+-0xa*0xda+-0x379*-0xb&0x2*-0x117b+-0x7c9+0x2afe,_0x161a31[_0x1fd15b++]=0x1*-0x1043+0x1185+-0xc2|_0x2c6513>>-0x8c6+0x1d87+-0x1d*0xb7&0x2a*0xe7+-0x1*0x1869+-0xd3e,_0x161a31[_0x1fd15b++]=-0x58e+0x6*-0x216+0x949*0x2|0x2474*-0x1+0xa9*0xd+0x1c1e&_0x2c6513);}else{for(_0x1fd15b=this[_0x449340(0x385)];_0x432ef6<_0x383058&&_0x1fd15b<-0x48a*0x7+0x1993+-0xd*-0x7f;++_0x432ef6)(_0x2c6513=_0x36f09d[_0x449340(0x195)](_0x432ef6))<0x2*-0x86+0x101*0x1f+-0x1*0x1d93?_0x17a6ed[_0x1fd15b>>-0x1297+0x71a+0xb7f]|=_0x2c6513<<_0x4d13bd[-0x1f26+0xd17+0x1212&_0x1fd15b++]:_0x2c6513<0x11b7+-0x150d+0xb56?(_0x17a6ed[_0x1fd15b>>-0x231f+-0x8eb+0x2c0c]|=(0x1ae0+0x70b+-0x212b|_0x2c6513>>-0x87f+-0x23f*0x5+-0x13c*-0x10)<<_0x4d13bd[0x3fb+0x8e1*-0x1+0x4e9&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>-0x1d97+0x6ad+0x16ec]|=(0x1d0e+0xf0a+-0x2b98|0xf2c+0xbb*-0x20+0x873&_0x2c6513)<<_0x4d13bd[0x8d9+-0x17e0+0xf0a&_0x1fd15b++]):_0x2c6513<-0x8b7d+0x113bf+0x4fbe||_0x2c6513>=-0xc9c0+0x16d4a*0x1+0x3c76?(_0x17a6ed[_0x1fd15b>>0x2*0x74f+0x1533+-0x23cf]|=(-0x7*0x28+-0x2*-0x1bb+-0x17e|_0x2c6513>>0x1d5c+0x1e6c+-0x1*0x3bbc)<<_0x4d13bd[0x12*0x119+0x4*-0x1c6+-0xca7&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>-0x26*0x2e+-0x12aa*0x2+-0x2c2a*-0x1]|=(0x245a+-0x1aa1+-0x1*0x939|_0x2c6513>>0x1fa6+-0x735+-0x2f*0x85&-0x5d3+-0x1eb2*-0x1+-0x18a0)<<_0x4d13bd[0xa40+0x1f49+-0x84e*0x5&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>0x21*-0xa4+-0x139*-0x5+0x1*0xf09]|=(-0x1434+0x5*-0x35+0x15bd|-0xc2d+0x2655+0x25b*-0xb&_0x2c6513)<<_0x4d13bd[0x76*0x26+-0x1*0x8ad+0x1c4*-0x5&_0x1fd15b++]):(_0x2c6513=0x12e*-0xbf+0x1cc8a+0x8c*0x26+((-0x3f5*0x3+-0xfe8+0x1fc6&_0x2c6513)<<0x29a+0x13a9+-0x1639|0x1*-0x1dd7+0x6a5+0x1*0x1b31&_0x36f09d[_0x449340(0x195)](++_0x432ef6)),_0x17a6ed[_0x1fd15b>>0xd*0x61+0xcb*-0x1e+0x12df]|=(0x1*-0xee4+0x2*-0x767+-0x6*-0x51b|_0x2c6513>>0x1*0x1a4d+-0x3*0x149+-0x1660)<<_0x4d13bd[-0x1c70+-0x1*-0x57b+0x1*0x16f8&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>0xef1+-0xba0+-0x1*0x34f]|=(-0xa3*-0x3+0x26af+0x140c*-0x2|_0x2c6513>>-0x518+0x1*-0xc26+0x114a&-0x25*-0xd1+-0x1130*-0x1+-0x2f26)<<_0x4d13bd[-0x73*-0xb+-0x17d*0x1+0x1*-0x371&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>0x3*-0x107+0x1dbd+-0x1aa6]|=(-0x151c+0x16*-0x191+0x3812|_0x2c6513>>0xded*-0x1+0x45+0xdae&-0x2af*-0x7+-0x49+0x1241*-0x1)<<_0x4d13bd[-0x19a2+-0x7d2+0x2177&_0x1fd15b++],_0x17a6ed[_0x1fd15b>>-0x180e+0x13d2+-0x1*-0x43e]|=(-0x9*-0x1f5+-0xf1*0xd+-0x6*0xd0|0x7f*0x2f+0x1*0x188c+-0x986*0x5&_0x2c6513)<<_0x4d13bd[0x1cf9+0xb*-0xe7+-0x1309&_0x1fd15b++]);}}this['lastByteIndex']=_0x1fd15b,this[_0x449340(0x25b)]+=_0x1fd15b-this['start'],_0x1fd15b>=-0xa42+0x2*-0x11c3+0x2e08?(this[_0x449340(0x385)]=_0x1fd15b-(-0x2b*0xe2+0xa55+0xd*0x225),this[_0x449340(0x2ba)](),this[_0x449340(0x1e9)]=!(0xc54+-0x1*-0xfa3+-0x1*0x1bf7)):this[_0x449340(0x385)]=_0x1fd15b;}return this[_0x449340(0x25b)]>-0x14462193f+-0xd7facb0+-0x2*-0x128f0e2f7&&(this['hBytes']+=this['bytes']/(0x73e4b4e0*0x3+-0x3fc*-0x40b5a1+-0x486a*0x4d396)<<-0x1fbb*0x1+-0x1623+0x35de,this[_0x449340(0x25b)]=this[_0x449340(0x25b)]%(0x7d756c*-0x1e1+-0x3b6aede*-0x2+0x278*0xc42bda)),this;}},_0x22f902[_0x2db296(0x344)][_0x2db296(0x1d9)]=function(){var _0x8b694=_0x2db296;if(!this[_0x8b694(0x182)]){this[_0x8b694(0x182)]=!(0x1038+0x1*-0x247f+0x1d*0xb3);var _0x52f24a=this[_0x8b694(0x319)],_0x21cbd8=this[_0x8b694(0x32e)];_0x52f24a[_0x21cbd8>>0x4cd*0x4+-0x1*-0x1511+-0x2843]|=_0x146907[0xb2e+-0x10*-0xbf+-0x41*0x5b&_0x21cbd8],_0x21cbd8>=0x8fe*0x3+-0x231c+-0x42d*-0x2&&(this['hashed']||this['hash'](),_0x52f24a[0x666+0x1432+-0x1a98]=_0x52f24a[0x24cd+0x1e1*0x14+0x19*-0x2f9],_0x52f24a[-0x1f46+0x1*0x1015+0xf41]=_0x52f24a[0xbe8+0x1a96+0xa7*-0x3b]=_0x52f24a[-0xb*0x14d+-0x1*0x59+-0xeaa*-0x1]=_0x52f24a[-0x1f1*-0x3+-0x1d69+0x1799]=_0x52f24a[0xa7b*-0x2+0xbb1*-0x1+-0x20ab*-0x1]=_0x52f24a[-0x11dc+-0xaa+0x2f*0x65]=_0x52f24a[-0x1*0x1f99+-0x1e53*0x1+0x3df2]=_0x52f24a[-0x103*-0x5+-0x2451+0x1f49]=_0x52f24a[0x21e6+-0xf9*0x1+-0x1*0x20e5]=_0x52f24a[0x88a+-0x1bea+-0x1*-0x1369]=_0x52f24a[0xf4d+-0x177d*-0x1+-0x26c0]=_0x52f24a[0x1e22+0x655*0x3+-0x3116]=_0x52f24a[-0x196b+0x19b*-0x10+-0x2d*-0x123]=_0x52f24a[-0x1*-0x228d+0x1cfc+-0x3f7c]=_0x52f24a[-0x13b4+-0x5bc+0x197e]=_0x52f24a[0x1e68+0x1209+-0x3062]=0x26a1+-0x124*-0x20+-0x4b21),_0x52f24a[0xbb3+0x1210+-0x1db5]=this[_0x8b694(0x25b)]<<-0x15*-0x1d7+0x2*-0xc2a+-0xe4c,_0x52f24a[-0x19e2+0x28*0x55+0xca9]=this[_0x8b694(0x1cd)]<<-0x178*0x6+0xa67+0x194*-0x1|this[_0x8b694(0x25b)]>>>0x1a65+-0x18*0x44+-0x16c*0xe,this['hash']();}},_0x22f902[_0x2db296(0x344)][_0x2db296(0x2ba)]=function(){var _0x2f0149=_0x2db296,_0x227f23,_0x3952af,_0x2a0c94,_0x39eb64,_0x2fd641,_0x113678,_0x128d32=this[_0x2f0149(0x319)];this['first']?_0x3952af=((_0x3952af=((_0x227f23=((_0x227f23=_0x128d32[0xe*0xd4+0x18b*-0x13+0x11b9]-(0x1ffe472d+-0x1*0x229b4beb+0x8a3acdb*0x5))<<-0xe3+0x794*0x5+0x2*-0x127d|_0x227f23>>>-0x2125+0x21d4+-0x96)-(-0xa42713c+-0x131*-0xc4faf+0xbc9d634)<<-0x76+-0x13ee+0x1464)^(_0x2a0c94=((_0x2a0c94=(-(0x1d99*-0x236f+0xdf*-0x44be3+0x18092f8b)^(_0x39eb64=((_0x39eb64=(-(-0xfc*-0xaf0303+0x524d2f32+-0xcedcd*0xbb4)^0x1*0x7b470ebd+0x1*0x4d64e86c+-0x51347fb2&_0x227f23)+_0x128d32[-0x2684+0x1f4e*-0x1+0x8f*0x7d]-(0x563f0e*-0x16+0x59003ae+0x46faddd*0x2))<<-0x684+0xa*0x289+-0x1a*0xb9|_0x39eb64>>>-0x1116*-0x2+0xee9*-0x1+-0x132f*0x1)+_0x227f23<<-0x2687*-0x1+0xfe3+-0x1*0x366a)&(-(0x1a9157f+0x1e0df645+-0xf84b74d)^_0x227f23))+_0x128d32[0x144d+0x15b*-0x18+0xc3d]-(-0x1*0x5504eeba+0x76f3f96f*0x1+-0x2*-0x109ad3b9))<<0x1af4+-0x12cb+-0x818|_0x2a0c94>>>0x7aa*0x1+-0x1994*-0x1+-0x212f)+_0x39eb64<<-0x9fc+-0x14*0xfa+-0x1d84*-0x1)&(_0x39eb64^_0x227f23))+_0x128d32[-0x2a*0x97+0x18c8+0x1*0x1]-(-0x992*0x9361+0x23b1c4+0x1*0x53d34a17))<<-0x7*0x1b1+-0x234d+-0x136*-0x27|_0x3952af>>>-0x113f*0x1+0x5*-0x7f+0x1cc*0xb)+_0x2a0c94<<-0x4f0+-0x1*-0x1f3d+-0x1a4d:(_0x227f23=this['h0'],_0x3952af=this['h1'],_0x2a0c94=this['h2'],_0x3952af=((_0x3952af+=((_0x227f23=((_0x227f23+=((_0x39eb64=this['h3'])^_0x3952af&(_0x2a0c94^_0x39eb64))+_0x128d32[0x3a5+-0x1684+0x12df*0x1]-(0x2b26bdc2+-0x1*-0x4468952b+-0x5773*0xcfc7))<<-0x1805+-0x18cb+0x30d7|_0x227f23>>>0x21ec*-0x1+-0x53c*-0x1+0x1cc9)+_0x3952af<<0xfe0+0x16a4+-0x2684)^(_0x2a0c94=((_0x2a0c94+=(_0x3952af^(_0x39eb64=((_0x39eb64+=(_0x2a0c94^_0x227f23&(_0x3952af^_0x2a0c94))+_0x128d32[0x1*-0x480+0x1*0x112f+-0xcae]-(0x1e4d0bcf+0x1be*-0xbfe2b+0xdd00bc5))<<-0x26fe+-0x462+0x2b6c|_0x39eb64>>>-0x1*0x251+-0xd16+0xf7b)+_0x227f23<<0x15be*0x1+0x2e3*0x8+-0x2cd6)&(_0x227f23^_0x3952af))+_0x128d32[-0xa5*-0x1d+0x5*-0x105+0x2f*-0x4a]+(0x15*0x27c2519+-0x1cb83878+0x654cf23*0x2))<<-0x9d5+-0x221e+-0xeac*-0x3|_0x2a0c94>>>-0x718*0x2+-0x5*-0x556+-0x425*0x3)+_0x39eb64<<0x223*-0xb+-0x92*0x22+0x2ae5)&(_0x39eb64^_0x227f23))+_0x128d32[-0x141c+0x376+0x10a9]-(0x29f06346+0x14e90f87*-0x3+-0x3*-0x1baefecb))<<0x103a+-0x24f4+0x14d0|_0x3952af>>>-0x16a5+-0xe92+0xbb*0x33)+_0x2a0c94<<0x25a2*0x1+0x1a76+-0x4018*0x1),_0x3952af=((_0x3952af+=((_0x227f23=((_0x227f23+=(_0x39eb64^_0x3952af&(_0x2a0c94^_0x39eb64))+_0x128d32[-0x2552+0x577+0x1fdf]-(-0xec5a4e1+0x23fe4*-0x78c+-0x9e*-0x447abf))<<-0x46e+0x19b5+-0x8*0x2a8|_0x227f23>>>0x100e+0xd63+-0x1d58)+_0x3952af<<-0x237d+0x1955*-0x1+0x3cd2)^(_0x2a0c94=((_0x2a0c94+=(_0x3952af^(_0x39eb64=((_0x39eb64+=(_0x2a0c94^_0x227f23&(_0x3952af^_0x2a0c94))+_0x128d32[-0x203+0xd54+-0x2d3*0x4]+(0x1*-0x20e3c675+0x8ef8e325+0x4e53fd*-0x7e))<<-0x9e3*0x2+0x2*-0x8fd+0x25cc|_0x39eb64>>>-0x7bd+0x2*-0x844+0x1859*0x1)+_0x227f23<<-0x3dc+-0x122e+0x326*0x7)&(_0x227f23^_0x3952af))+_0x128d32[-0xbfd+-0x1*0xba3+0x17a6]-(-0x93f8bbe0+-0x43dcbb55+0x1*0x12fa53122))<<0x185*-0x10+-0x76c+0x1fcd|_0x2a0c94>>>0x19b+0x51*0x6d+0x171*-0x19)+_0x39eb64<<0x971+0x1958+-0x5*0x6f5)&(_0x39eb64^_0x227f23))+_0x128d32[-0x978*-0x3+0x315+-0x2*0xfbb]-(-0x99026a+0xa6ba6*0x47+0xc4927*0x9))<<0x212+0x1*0x4c3+0xb*-0x9d|_0x3952af>>>-0x152a+0x1*-0x17b4+-0x77c*-0x6)+_0x2a0c94<<-0xa7*0x1f+0x2379+-0xf40,_0x3952af=((_0x3952af+=((_0x227f23=((_0x227f23+=(_0x39eb64^_0x3952af&(_0x2a0c94^_0x39eb64))+_0x128d32[-0x251a+-0x14*-0x18d+0x61e]+(0xb19f3051+-0xb487bda1*0x1+0x6c692628))<<0x1cc+-0x171a+0x1555|_0x227f23>>>0x1*-0x223b+-0x282+0x24d6)+_0x3952af<<0x1453+0x1aee+-0x2f41)^(_0x2a0c94=((_0x2a0c94+=(_0x3952af^(_0x39eb64=((_0x39eb64+=(_0x2a0c94^_0x227f23&(_0x3952af^_0x2a0c94))+_0x128d32[-0x1c63+-0x1a88+0x36f4]-(0x334017ba+0x1*-0x90d7af8d+-0x86*-0x191cf86))<<-0x1db2+0x1*0x10b1+0xd0d|_0x39eb64>>>-0x4*-0x20+-0xd81*-0x1+-0xded)+_0x227f23<<-0x2b0+0x4*-0x5b0+-0x94*-0x2c)&(_0x227f23^_0x3952af))+_0x128d32[-0x1*-0xf25+0xe75*0x1+-0x1d90]-(-0x5*0x4eb+0x12ff3*-0x1+0x1*0x1ecd9))<<-0xa43+0x1eed+0x1*-0x1499|_0x2a0c94>>>-0x1878+-0x92b+0x21b2)+_0x39eb64<<-0x1*0x2515+-0x8c8+-0x3b*-0xc7)&(_0x39eb64^_0x227f23))+_0x128d32[0x80b*-0x4+0xff5+0x1042]-(-0x2d479161*0x5+-0x139*0x33685d+0x197e398dc))<<-0x178+-0x22bf+0x1*0x244d|_0x3952af>>>0x15cc+-0x26b6+-0x5*-0x364)+_0x2a0c94<<0x293*-0x2+0x18f7+-0x13d1,_0x3952af=((_0x3952af+=((_0x227f23=((_0x227f23+=(_0x39eb64^_0x3952af&(_0x2a0c94^_0x39eb64))+_0x128d32[-0x1225+-0xa*-0x348+0x13*-0xc5]+(-0x36f3e05c+0x4445538c+0x5e3e9df2))<<-0x1b3*-0x3+0x2702+-0x2c14|_0x227f23>>>-0x2*0x1006+0x25*0xc5+0x3ac)+_0x3952af<<0x2501+0x18fb+0xf7f*-0x4)^(_0x2a0c94=((_0x2a0c94+=(_0x3952af^(_0x39eb64=((_0x39eb64+=(_0x2a0c94^_0x227f23&(_0x3952af^_0x2a0c94))+_0x128d32[-0x13cb+-0xcd4+0x20ac]-(-0x24*0x1e9ae6+-0xa0ba1*0x72+-0x18cc1*-0x737))<<0x702+0x2b*-0x35+0x1f1|_0x39eb64>>>-0x15dd+-0x22eb*-0x1+0x1*-0xcfa)+_0x227f23<<0x224b*0x1+0x2f*0x33+0x4*-0xaea)&(_0x227f23^_0x3952af))+_0x128d32[0xec7*-0x1+-0x61*-0x25+0xd0]-(0x48e87164+0x613cb841+-0x509e6d33))<<-0x1c0d+0x5d0+0x164e|_0x2a0c94>>>0x156a*0x1+0x16e7+-0x2c42)+_0x39eb64<<-0x1*-0x19ec+0x8b4+-0x22a0)&(_0x39eb64^_0x227f23))+_0x128d32[0x1df0+-0x2365+-0x1*-0x584]+(0x34eb7*0x25f0+-0x3*-0x1a616a08+-0x82ea7487))<<-0x3*0xc8c+0x1fc2*-0x1+-0x457c*-0x1|_0x3952af>>>-0x2464+0x2705+-0xdd*0x3)+_0x2a0c94<<-0x504+0x2*-0x5f9+0x10f6,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^_0x2a0c94&((_0x227f23=((_0x227f23+=(_0x2a0c94^_0x39eb64&(_0x3952af^_0x2a0c94))+_0x128d32[0x1b4d+0x2d4*0x5+-0x2970]-(0x30139*0x8d+-0x738f7c2+-0x1*-0xf7325fb))<<0x1acc+-0x13e5+-0x6e2|_0x227f23>>>0x7*0x1+-0x10e9+0x10fd*0x1)+_0x3952af<<-0x18fd+0x10cc+0x831)^_0x3952af))+_0x128d32[0x2*0x131e+-0x2511+0x1*-0x125]-(-0x1f*-0x3953eea+-0x4*-0x12786c0f+-0x793501d2))<<0x210d+0x11*-0x241+0x54d*0x1|_0x39eb64>>>-0x4*-0x265+-0x292+0xfd*-0x7)+_0x227f23<<-0x200b+-0xa*0xff+-0x1*-0x2a01)^_0x227f23&((_0x2a0c94=((_0x2a0c94+=(_0x227f23^_0x3952af&(_0x39eb64^_0x227f23))+_0x128d32[-0x92*-0x13+0x6b0+-0x117b]+(-0x4be8e758+-0x1a3c*-0x1e38f+0x40b96625))<<-0x1*0x13d1+0x22da+-0xefb|_0x2a0c94>>>0x166d+-0x955+-0xd06)+_0x39eb64<<-0x13af*0x1+-0x1*-0x1307+0xa8)^_0x39eb64))+_0x128d32[-0x547+-0x1*0x1c0b+-0x2152*-0x1]-(0x7*0x14d828a+0x7d4a6f4+-0x3*-0x1c75534))<<0x1bb9*-0x1+-0x26ee+0x42bb*0x1|_0x3952af>>>0x8*0x296+0x1ce9*0x1+-0x318d)+_0x2a0c94<<0x897*0x4+0x2ca+0x3*-0xc62,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^_0x2a0c94&((_0x227f23=((_0x227f23+=(_0x2a0c94^_0x39eb64&(_0x3952af^_0x2a0c94))+_0x128d32[0x107f*-0x1+-0x1299+-0x59*-0x65]-(0x1079c263+-0x1889*-0x11026+-0xbe0716))<<-0x260c+-0x1c23+0x2*0x211a|_0x227f23>>>0x22b*0x1+-0x16f3+-0x14e3*-0x1)+_0x3952af<<0x183b+-0x5*-0x72a+-0x3c0d*0x1)^_0x3952af))+_0x128d32[0x4*0x86b+0x1064+-0x3206]+(-0x1177f56+-0x27de7f4+-0x540d*-0x11d1))<<0x167*0x5+-0x5*0x531+0x12fb*0x1|_0x39eb64>>>0x244b+-0x13*0x139+-0x9*0x171)+_0x227f23<<-0xf6d+-0x1f9e+0x2f0b*0x1)^_0x227f23&((_0x2a0c94=((_0x2a0c94+=(_0x227f23^_0x3952af&(_0x39eb64^_0x227f23))+_0x128d32[0x187*0x4+0x1e69+-0x2476]-(-0x10191ede+-0xd72a9b7+0x44e9e214))<<0x90*0xc+-0x2*0x12+-0x68e|_0x2a0c94>>>0x16*0x1c5+0x137b+0x1d*-0x203)+_0x39eb64<<-0x14fa+0x43*-0x74+-0x3356*-0x1)^_0x39eb64))+_0x128d32[-0xf4a+-0x1992+0x4*0xa38]-(-0xae6496c+0x21ece5a8+0x12567fc))<<0x1ac8+-0x1ed2+0x3e*0x11|_0x3952af>>>0x1268+0x100d+0x1*-0x2269)+_0x2a0c94<<0x1817*-0x1+0x239b+-0xb84*0x1,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^_0x2a0c94&((_0x227f23=((_0x227f23+=(_0x2a0c94^_0x39eb64&(_0x3952af^_0x2a0c94))+_0x128d32[0x5*0x19c+0xef3+-0x16f6]+(0x39d62e7+0xb397e9b+0x130aec64))<<0x11*0x21f+-0x1c66+-0x7a4|_0x227f23>>>-0xf86+0x2*-0x4e5+0x879*0x3)+_0x3952af<<0x258a+0x19ec+-0x3f76)^_0x3952af))+_0x128d32[0x914+0xd73+0x20b*-0xb]-(-0xab52d21+-0x845d9*0x1f7+0x57bf62aa))<<-0x1*0x2f+-0x1280+0x12b8|_0x39eb64>>>0x2e*-0x9+0x85*0x4b+-0x2542)+_0x227f23<<-0x151*0x9+0x20da+-0x1501)^_0x227f23&((_0x2a0c94=((_0x2a0c94+=(_0x227f23^_0x3952af&(_0x39eb64^_0x227f23))+_0x128d32[-0x84*0x39+0x57f+0x2fd*0x8]-(0x852a967+0xa4943f2+-0x770fae0))<<-0x5*-0x722+0x1*-0x155f+-0xe3d|_0x2a0c94>>>0x171b+-0x2709*0x1+0x8*0x200)+_0x39eb64<<-0x267+0x6fb*-0x4+-0x7*-0x455)^_0x39eb64))+_0x128d32[0xd73+-0xe3f+0xd4]+(-0x882b6c12+0x26ddcab5+0x9d1*0x10fa2a))<<-0x1*-0xb31+0xa*-0x175+0x375|_0x3952af>>>0x106*0x26+0x2e9+-0x29c1)+_0x2a0c94<<-0x198a+0xb33*-0x1+0x24bd,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^_0x2a0c94&((_0x227f23=((_0x227f23+=(_0x2a0c94^_0x39eb64&(_0x3952af^_0x2a0c94))+_0x128d32[0x1d4f+-0x10ae+-0xc94]-(0x76d313f*0xd+-0x5*-0x7a635fc+-0x2*0x18573b92))<<0x612+-0x1*0x40+-0x5cd|_0x227f23>>>-0x12e9*0x2+-0x1*-0x19e7+-0x1*-0xc06)+_0x3952af<<0x10db+0x10b5*0x1+-0x3*0xb30)^_0x3952af))+_0x128d32[-0x796+-0x13b9+0x1b51]-(-0x17e5b*0x29+0x48058e0+-0x132c045))<<0x26d*-0x2+0x4*-0x34e+-0x5*-0x39f|_0x39eb64>>>0x198d*-0x1+-0x25d4+-0xa94*-0x6)+_0x227f23<<0x206d+-0xd3f+-0x132e)^_0x227f23&((_0x2a0c94=((_0x2a0c94+=(_0x227f23^_0x3952af&(_0x39eb64^_0x227f23))+_0x128d32[-0x1f*0x95+0x6*-0x3dc+-0x3*-0xdbe]+(-0x2*0x5ec874bd+-0x4537c41d*-0x1+0xdfc82836))<<0x1336+-0x12bc+-0x6c|_0x2a0c94>>>0x119*-0x1d+-0x3*0xc41+0x44aa*0x1)+_0x39eb64<<0xe*-0x29c+0x4ea+0x1f9e)^_0x39eb64))+_0x128d32[0x985+-0x1177+0x1*0x7fe]-(-0x3d273af+-0x1*-0x72611590+0x4471195))<<0x112*0x15+-0xa30+-0x412*0x3|_0x3952af>>>0x1*0xe74+0x6*0x1f7+-0x1a32)+_0x2a0c94<<0xf1+-0xe43*-0x2+-0x1d77*0x1,_0x3952af=((_0x3952af+=((_0x113678=(_0x39eb64=((_0x39eb64+=((_0x2fd641=_0x3952af^_0x2a0c94)^(_0x227f23=((_0x227f23+=(_0x2fd641^_0x39eb64)+_0x128d32[-0x87*-0x1+-0x1*-0x1626+-0x16a8]-(-0x3ca72+-0xac995+0x145ac5))<<-0x1231+-0x1*-0xb05+0x730|_0x227f23>>>0x20dd*0x1+-0x16b8+-0xa09)+_0x3952af<<0x1*0xe63+0xa34*-0x2+0x605))+_0x128d32[-0x9a*-0x23+0x25fd+-0x3b03]-(0xb06c444e+0x2d68c*0x1574+0x9*-0xcf8fe07))<<0x1f60*-0x1+-0x1fc2+-0x1*-0x3f2d|_0x39eb64>>>0x1c0e+0x7c*-0x3b+0x9b*0x1)+_0x227f23<<-0x1*0x7db+0x143d*0x1+-0x2*0x631)^_0x227f23)^(_0x2a0c94=((_0x2a0c94+=(_0x113678^_0x3952af)+_0x128d32[0x1315+0x22f5+-0x35ff]+(0x1*-0xc1ee2861+0x5f64a13c+0xd026e847))<<0x10b*0x15+0x9e2+-0x1fb9|_0x2a0c94>>>0x213e+-0x11*-0x93+-0x1*0x2af1)+_0x39eb64<<-0x4*0x7c8+0x1*0x33d+0x1be3))+_0x128d32[0x18c5*0x1+0x94d*-0x3+0x2*0x198]-(0xbbbcc3+0x1509*-0x2a32+0x4d6a0f3))<<0x12b5*0x1+-0xc*0xa2+-0xb06|_0x3952af>>>-0x596*0x5+0x2*-0x655+0x28a1)+_0x2a0c94<<0x5f*-0x36+0x2b1*0x4+0x946,_0x3952af=((_0x3952af+=((_0x113678=(_0x39eb64=((_0x39eb64+=((_0x2fd641=_0x3952af^_0x2a0c94)^(_0x227f23=((_0x227f23+=(_0x2fd641^_0x39eb64)+_0x128d32[-0xaa9+0x6*0x54e+0x7e*-0x2b]-(-0x1309d*-0x67b3+-0x8e97ecd6+0x6e74d9cb))<<-0x1*0x1169+-0x1c3f+0x2dac|_0x227f23>>>-0x12f*0x1d+0x178+0x20f7)+_0x3952af<<-0x1554+0x2*-0x136e+0x9*0x6b0))+_0x128d32[0x8bd*0x4+0x993+-0x2c83]+(-0x1ec1ce29+0x12d9d323+0x57c6caaf))<<0x2f9*0x1+0x1a55+-0x1d43|_0x39eb64>>>-0xdf6+0x94*0x2+0xce3)+_0x227f23<<-0xd5+-0x197e+0x1a53)^_0x227f23)^(_0x2a0c94=((_0x2a0c94+=(_0x113678^_0x3952af)+_0x128d32[0x697*-0x1+-0x617+0xcb5]-(0x2*-0x11de459+0xaabba88+0xd4c2ca))<<-0x1198*0x1+-0x42c*0x3+0x2*0xf16|_0x2a0c94>>>0x28e*0x2+0x1547+-0x125*0x17)+_0x39eb64<<0x45*0x21+-0x14*-0x7+0x971*-0x1))+_0x128d32[-0x1*-0x118b+0x1f4+-0x1375]-(0x2*-0x34ed62bd+-0x6d39a445+-0x11854ad4f*-0x1))<<-0x27f*-0x1+-0x4*0x31a+0xa00|_0x3952af>>>-0x319+-0x1*-0x247d+-0x215b*0x1)+_0x2a0c94<<-0x1bc7*0x1+0x1*-0x4f7+0x20be,_0x3952af=((_0x3952af+=((_0x113678=(_0x39eb64=((_0x39eb64+=((_0x2fd641=_0x3952af^_0x2a0c94)^(_0x227f23=((_0x227f23+=(_0x2fd641^_0x39eb64)+_0x128d32[-0x1*-0x1eb5+-0x1ba5+-0x3*0x101]+(0x338d6ddd+0x1*0x3db4c799+-0x48a6b6b0))<<0x126a+0x1*0x407+-0x166d*0x1|_0x227f23>>>-0x1f97+-0x2566*-0x1+0x5b3*-0x1)+_0x3952af<<0x2175+0x2187*0x1+-0x42fc))+_0x128d32[0x1701+0x1fb6+0x36b7*-0x1]-(0x1e38ad99+-0x3*-0x1727b1c+-0xd3146e7))<<0x15*-0x18d+-0x4*-0x75e+0x324|_0x39eb64>>>-0x7f8+0x1*0xc1+0x74c)+_0x227f23<<-0x86*0x27+-0x1*0x1a93+-0x17*-0x20b)^_0x227f23)^(_0x2a0c94=((_0x2a0c94+=(_0x113678^_0x3952af)+_0x128d32[0x1*-0x1c6f+-0x10f0+-0x4a*-0x9d]-(-0x353a5*0xce7+0x510a05c*0xe+0xf144056))<<0x2c*-0x8f+-0x20*-0x11b+0x2af*-0x4|_0x2a0c94>>>-0x167c*-0x1+0x1bef*-0x1+0x583)+_0x39eb64<<-0x3*0x3b+0x1555+0xa52*-0x2))+_0x128d32[0x35*-0x27+-0x1*0xdb8+-0x1*-0x15d1]+(0x2e39f97*0x1+-0x1*0x58f09b4+0x7338722))<<0x1d3*-0x6+0x1*0x171b+0x406*-0x3|_0x3952af>>>0x3c1+-0x1cbf+0x1907)+_0x2a0c94<<0x1b73+0x1d44+0x38b7*-0x1,_0x3952af=((_0x3952af+=((_0x113678=(_0x39eb64=((_0x39eb64+=((_0x2fd641=_0x3952af^_0x2a0c94)^(_0x227f23=((_0x227f23+=(_0x2fd641^_0x39eb64)+_0x128d32[0x30b*0x2+0xa52+-0x105f]-(0x131*0x25e655+-0xe61e*0x48b+0xe5c*-0x33bb))<<0x17a1+-0x26*0x3f+0x3*-0x4c1|_0x227f23>>>0x1079+-0x13*0xf5+0x1d2)+_0x3952af<<-0xf27+-0x1*-0x21d1+0x955*-0x2))+_0x128d32[0x20e*-0x3+-0x4a7*0x8+0x2b6e]-(0x1*0x79f0ea3+-0x184cea1d+-0x11d*-0x2590d9))<<0x12e3+0x12b*-0x2+-0x1082|_0x39eb64>>>-0x485*0x2+-0x61*0x1a+0x12f9)+_0x227f23<<0x1*-0x34c+-0x6*0x4dc+-0x1*-0x2074)^_0x227f23)^(_0x2a0c94=((_0x2a0c94+=(_0x113678^_0x3952af)+_0x128d32[-0x3e3+0x22d*-0x2+0x84c]+(-0xdb8446c+-0x1*-0x5cb3a83+0x278f86e1))<<-0x7*-0x3f1+0x17f*0x7+-0x2600|_0x2a0c94>>>-0x47e+0xce*0x2+0x2*0x179)+_0x39eb64<<0x1*0x9fe+0x1*0x17f5+-0x21f3))+_0x128d32[-0x18*-0x18e+-0x1dff+-0x74f*0x1]-(0x12fee507+-0x1*0x6a5c4769+-0x1*-0x92b10bfd))<<0x5*-0x52a+-0x1e75+0xde*0x41|_0x3952af>>>0x116b+-0x1*0x9ed+0x53*-0x17)+_0x2a0c94<<0x264e*-0x1+-0x11fd+0x384b,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^((_0x227f23=((_0x227f23+=(_0x2a0c94^(_0x3952af|~_0x39eb64))+_0x128d32[-0x1524+0x65*0x5b+0x1*-0xec3]-(0x63c0c3*0x1f+0x11a61fd*-0xc+-0xcff1dfb*-0x1))<<0x36c+-0x4eb*0x5+0x1531|_0x227f23>>>-0x941*-0x2+-0x1b*-0x9b+-0x22c1)+_0x3952af<<0x1862+-0x1359+-0x509)|~_0x2a0c94))+_0x128d32[-0x26d+-0x6*0xbd+0x6e2]+(-0x690c7*0xd6d+0x2c437c8e*0x1+-0x3786a162*-0x2))<<0x54a+-0x1bd3+0x1693|_0x39eb64>>>0x1076+0xa0d+-0x1a6d)+_0x227f23<<-0x1d37+0x1*0x14fb+0x83c)^((_0x2a0c94=((_0x2a0c94+=(_0x227f23^(_0x39eb64|~_0x3952af))+_0x128d32[0x190f*-0x1+-0x1503+0x2e20]-(0x12aa3*0x80ad+0x5*0x123e2fa3+-0x9ce661fd))<<-0x204a+-0x22cf+0x4328|_0x2a0c94>>>0x1*0xcac+-0x1bbf*-0x1+-0x142d*0x2)+_0x39eb64<<-0x11b4+0x10b1*0x1+-0x25*-0x7)|~_0x227f23))+_0x128d32[0x1*-0x10d+0x5f4+-0x19*0x32]-(0x2181582+-0x55ca10e+0x6b0eb53))<<-0xd3d*0x1+-0x1dd7+-0x1*-0x2b29|_0x3952af>>>0x676+0xa84*0x1+-0x10ef)+_0x2a0c94<<-0x2*0xc83+0x15*-0x10c+-0x1781*-0x2,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^((_0x227f23=((_0x227f23+=(_0x2a0c94^(_0x3952af|~_0x39eb64))+_0x128d32[0x2*-0x527+0x246c+-0x8e*0x2f]+(0x62359ef1+0xc3bd213a+-0xc0976668))<<-0x22fa*0x1+0x2af+0x2051|_0x227f23>>>-0x92b+-0x4b4*0x1+0xdf9)+_0x3952af<<0x494+-0x1902+-0x1*-0x146e)|~_0x2a0c94))+_0x128d32[0x1d8*0x1+0x1*-0x5ce+0x3f9]-(-0x324*-0x49722+0xde*0x31ad3b+-0x1bba29be*-0x2))<<0x941+-0x1651+-0x4e*-0x2b|_0x39eb64>>>0x2*-0xc2+-0x112a+0x12c4)+_0x227f23<<0x1369+-0x1261+-0x3*0x58)^((_0x2a0c94=((_0x2a0c94+=(_0x227f23^(_0x39eb64|~_0x3952af))+_0x128d32[0x10c5+0x78f+0x1*-0x184a]-(-0x3b62a*0x6+0x15245b+0x89612*0x2))<<0x252f+-0x5*-0x661+-0x4505|_0x2a0c94>>>0x251e+0x6*0x2b7+-0x3557*0x1)+_0x39eb64<<-0x1*-0xa79+-0x8d*-0x33+-0x2690)|~_0x227f23))+_0x128d32[0x395*0x8+-0x24a9+-0x52*-0x19]-(-0xc3191853+-0x57fdcd8*0x29+-0x141d00ee*-0x1b))<<0x132b*-0x1+-0x13*-0x15+0x11b1|_0x3952af>>>-0x175f+0x16*0xc2+-0x2*-0x35f)+_0x2a0c94<<-0xc*-0x2de+-0x1*-0x261a+0x1*-0x4882,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^((_0x227f23=((_0x227f23+=(_0x2a0c94^(_0x3952af|~_0x39eb64))+_0x128d32[0x4*-0x527+-0xb71+0x2015*0x1]+(0x2*-0x2405587c+0x963dc885*0x1+0x217566c2))<<0x14cb+-0x647*-0x1+-0x1b0c|_0x227f23>>>-0xbd*0x30+-0x13*0x14f+0x7*0x8a1)+_0x3952af<<-0xd*0x215+0xcc+0x1a45)|~_0x2a0c94))+_0x128d32[-0x638*0x6+-0x25fa+0x4b59]-(0xab1cd2+-0x184b1f6+0x2acae44))<<-0x177b+-0x500+0x1c85|_0x39eb64>>>-0x1e73+-0x3*0x1f1+-0x2*-0x122e)+_0x227f23<<0xb5*-0x3+0x158f+-0x4*0x4dc)^((_0x2a0c94=((_0x2a0c94+=(_0x227f23^(_0x39eb64|~_0x3952af))+_0x128d32[-0xe39+0x452+0x9ed]-(0x5a22d2a4+0x3a146128+0x154a34*-0x298))<<0x1*-0x1fd0+0x1837*0x1+0x7a8|_0x2a0c94>>>0x1212+0x2*-0xc47+0x68d)+_0x39eb64<<-0x20ba+0xbb2+-0x8*-0x2a1)|~_0x227f23))+_0x128d32[0x283*0x8+-0x5f*0x37+0x5e]+(-0x15db9faa+0x3841d*-0x1db3+0xcc505a92))<<-0x172f+-0x5f*-0x1+0x16e5|_0x3952af>>>0x6c+0x2*0x530+0xac1*-0x1)+_0x2a0c94<<0x3*0x312+0x19*-0x107+0x1079,_0x3952af=((_0x3952af+=((_0x39eb64=((_0x39eb64+=(_0x3952af^((_0x227f23=((_0x227f23+=(_0x2a0c94^(_0x3952af|~_0x39eb64))+_0x128d32[-0x5bc+-0x1*-0x887+-0x1*0x2c7]-(0xd0d1959+0x9a4d1b1+-0xe05698c))<<0x3*0x6a7+0x252e+-0x391d|_0x227f23>>>-0x2292+-0x1385+0x1*0x3631)+_0x3952af<<-0x2483*-0x1+0x93e+0xd*-0x385)|~_0x2a0c94))+_0x128d32[-0x1fd2+0x1bb0+-0x1*-0x42d]-(-0x9b7*0xd9fae+-0x9fdcf8b*-0x7+0xb3bb54*0xb8))<<0x1f*0xec+-0x260+-0x18a*0x11|_0x39eb64>>>0x140*-0x6+0x2231*-0x1+0x29c7)+_0x227f23<<0x26ef*-0x1+-0x112b+0x2b*0x14e)^((_0x2a0c94=((_0x2a0c94+=(_0x227f23^(_0x39eb64|~_0x3952af))+_0x128d32[-0x1*-0x100d+0xdc0+-0x1d*0x107]+(0x43dc7475+0x524cbd91+-0x82661*0xd2b))<<-0x1f*-0xf+-0x1d3a+-0x1*-0x1b78|_0x2a0c94>>>0x235b+0x162b+-0x3975)+_0x39eb64<<-0xf*-0x27b+-0xc58+-0x18dd)|~_0x227f23))+_0x128d32[-0x72*-0x1f+0xac4+-0x1889]-(0xa*-0x3c812a2+-0x2c*0x1bc4a2+-0x3*-0x15053b89))<<0x4*0x9c2+-0xd46*0x2+-0xc67|_0x3952af>>>-0x8b9+-0x1ed3*0x1+-0x1*-0x2797)+_0x2a0c94<<-0x1*-0xede+0x15d3+-0x1f*0x12f,this['first']?(this['h0']=_0x227f23+(-0x295123b+0xc62a6020*0x1+-0x22*0x2b71052)<<-0x2*-0x602+-0x1*0x12c2+0x6be,this['h1']=_0x3952af-(0x3a5b52c+0x1d978e61+0x7*-0x26f46ba)<<0x5ea*0x4+0xe5+-0x4e9*0x5,this['h2']=_0x2a0c94-(0xa6be44c7+0xc54d85a4+-0x104c6a769)<<0x6d*0x53+0x1809+-0x3b60,this['h3']=_0x39eb64+(-0x1fdbc0b+0x1*0x1a1047ff+0x85f6*-0xf0d)<<0x26e8+-0x5d*0x1f+0x151*-0x15,this[_0x2f0149(0x19f)]=!(0x25a2+-0x5*0x1d5+-0x38f*0x8)):(this['h0']=this['h0']+_0x227f23<<0x923+0xb1c+-0x143f,this['h1']=this['h1']+_0x3952af<<-0x23*0xc0+0x1aef+-0xaf,this['h2']=this['h2']+_0x2a0c94<<-0x1b53+-0x1acc+-0xa3*-0x55,this['h3']=this['h3']+_0x39eb64<<-0xba0*0x1+0x146+0xa*0x109);},_0x22f902[_0x2db296(0x344)]['hex']=function(){this['finalize']();var _0x58d18c=this['h0'],_0x4e2807=this['h1'],_0x41534b=this['h2'],_0x136921=this['h3'];return _0x142491[_0x58d18c>>0x3cb*-0x4+-0x6e*-0x3+0xde6&0x3b*0x47+0x1567+-0x25b5]+_0x142491[-0x1731+-0x16e+0x9*0x2be&_0x58d18c]+_0x142491[_0x58d18c>>-0xa78+0x18d4+0xe5*-0x10&0x81e+-0x6*0x529+0x16e7]+_0x142491[_0x58d18c>>0x1894+-0x3c+-0x1850&-0x2228*-0x1+0x3*0x4ff+-0x3116]+_0x142491[_0x58d18c>>-0x67*-0x1+-0x1842+0x17ef&0x580*-0x6+0x1b*0x171+-0x5dc]+_0x142491[_0x58d18c>>-0x1*-0x709+0x1594+-0x1*0x1c8d&0x1633+-0x70d+-0xf17]+_0x142491[_0x58d18c>>-0xaf6+0x1ddb*0x1+-0x12c9&-0x4b*0x7b+0x5*0x41b+0xf91]+_0x142491[_0x58d18c>>-0x1*0x1543+-0x1795*-0x1+-0x23a&-0x1e70+0xcc5+-0x11ba*-0x1]+_0x142491[_0x4e2807>>0x651+-0x10f7*-0x2+-0x283b&0x1199+-0x47*-0x59+-0x2a39]+_0x142491[0xe3*-0x22+0x2479+0x644*-0x1&_0x4e2807]+_0x142491[_0x4e2807>>0x121a+0x1a84+-0x2c92&-0x65*-0x4a+-0x5db+-0x1748]+_0x142491[_0x4e2807>>0x14a3+-0x11*0x55+-0x5*0x2fe&-0x24e3*-0x1+0xf64+-0x3438]+_0x142491[_0x4e2807>>0x1cf+-0x1c89+-0x2*-0xd67&-0xd47*-0x2+-0x1*0x221b+0x1e7*0x4]+_0x142491[_0x4e2807>>0x673+0x1d12+-0x1*0x2375&0x12ce+-0x8*-0xb5+-0x1867]+_0x142491[_0x4e2807>>0x1*0x1499+0x1a6b*0x1+-0x98*0x4f&0x1516+-0x147*-0x2+-0x1795*0x1]+_0x142491[_0x4e2807>>0x4*0x570+-0x2*0x622+-0x2*0x4b2&-0x6*-0x15d+-0x7eb+-0x34]+_0x142491[_0x41534b>>0x1738+-0x6*0x23b+-0x3*0x346&-0x97*-0x3a+0x3a6+-0x25cd]+_0x142491[-0x48*-0x11+0x1*-0x2502+0x13*0x1b3&_0x41534b]+_0x142491[_0x41534b>>-0x1*-0x1556+0x3*0x623+-0x27b3*0x1&-0x6b*0x1f+0x1*-0x49+-0x3*-0x46f]+_0x142491[_0x41534b>>-0x794+-0x1*0x2443+0x2bdf&0x26ca+-0xb02+0x2f*-0x97]+_0x142491[_0x41534b>>0x177f+-0x57b+-0x11f*0x10&0x1eed+0xbb2+-0x2a90]+_0x142491[_0x41534b>>0xab7*-0x2+0x3ba+0x11c4&-0x1621+0xa3f*0x2+-0xd9*-0x2]+_0x142491[_0x41534b>>0x607+-0x2065+0x1a7a&0x2*-0xb0f+0x629*-0x5+0x1a7d*0x2]+_0x142491[_0x41534b>>-0x2205+0x20ac+0x171&-0x1*-0x1337+0x6e*-0x1+-0x22*0x8d]+_0x142491[_0x136921>>-0x845+-0x1*0x267b+0x1762*0x2&0x340+-0x21*0xcb+0x16fa]+_0x142491[0x4*-0x8e2+-0xdea+0x3181&_0x136921]+_0x142491[_0x136921>>0x1784+0xa*0x31b+-0x1*0x3686&-0x26*0x4f+-0x1*-0xca8+0xdf*-0x1]+_0x142491[_0x136921>>0x1823*-0x1+0xe5c*-0x2+0x1*0x34e3&0x34e*0x6+0x47f+-0x1844*0x1]+_0x142491[_0x136921>>0x1*-0x10af+0x39a+0xd29&-0xea2+0x2*-0xad2+0x2455]+_0x142491[_0x136921>>0xadf+0x2db*-0x7+0x92e&-0x493*0x3+0x15e1+-0x819]+_0x142491[_0x136921>>-0x22d6+0x20*-0x17+0x25d2&-0x17*0x83+0x1214+-0x640]+_0x142491[_0x136921>>-0x96e+-0x1e99+0x281f&-0x18c8+0x33*0x89+-0x274];},_0x22f902['prototype'][_0x2db296(0x3ae)]=_0x22f902[_0x2db296(0x344)]['hex'],_0x22f902[_0x2db296(0x344)]['digest']=function(){var _0x5a0a9e=_0x2db296;this[_0x5a0a9e(0x1d9)]();var _0x2a8796=this['h0'],_0x2d7c31=this['h1'],_0x1378d3=this['h2'],_0x2e667f=this['h3'];return[0x1*0x4bd+0x1f15+0x5*-0x6f7&_0x2a8796,_0x2a8796>>0xbcd+-0x25f7+0x1a32&0x1507+-0x177d+0x1*0x375,_0x2a8796>>-0x141c+0x1ae2+-0x6b6&0x132a+-0x3*0x423+0xb*-0x86,_0x2a8796>>-0x1*-0x261f+0x115*-0xc+-0x190b&0x1233*-0x1+0xacf*-0x3+0x339f,-0x16a0+0x1002+0x79d*0x1&_0x2d7c31,_0x2d7c31>>-0x2*0xff4+-0x541*0x2+-0xe26*-0x3&0x54e+-0x1c*-0x148+-0x282f*0x1,_0x2d7c31>>-0x4*-0x83f+0x20ab*-0x1+-0x41&0x427*0x5+0xdd1+-0x2195*0x1,_0x2d7c31>>0xf*-0x201+0x1c26+-0x9*-0x39&0x7*-0x235+0x1be+0xeb4*0x1,0x34*0x9b+0xa36+-0x28b3&_0x1378d3,_0x1378d3>>0x463*0x1+-0x1f0d+0x1ab2&0x13*-0x17f+-0x536+0x22a2,_0x1378d3>>0x4a7+-0x1a4+-0x2f3&0x1066+0xb3d+-0x1aa4,_0x1378d3>>-0xc00+-0x2*0xfcb+0x2bae&0x11a7*-0x1+0x3a1*-0x1+0x1647,-0xa38+-0x1304+0x1e3b&_0x2e667f,_0x2e667f>>0x1102+0x1896+-0x2f8*0xe&-0x15b2*-0x1+-0xa6*0x31+-0xf*-0xbd,_0x2e667f>>0x25a2+-0x4*0x103+-0x2186&-0xa83*-0x3+-0x15f1+-0x899,_0x2e667f>>0xb95*0x3+-0x1a34+-0x873&0xfb6*-0x2+0x3b*0x3a+0x130d];},_0x22f902[_0x2db296(0x344)][_0x2db296(0x238)]=_0x22f902['prototype'][_0x2db296(0x19b)],_0x22f902['prototype'][_0x2db296(0x249)]=function(){var _0x45ab2e=_0x2db296;this[_0x45ab2e(0x1d9)]();var _0x31d586=new ArrayBuffer(0x13*-0x20e+-0x4*-0x62f+0xe5e),_0x2d9fbd=new Uint32Array(_0x31d586);return _0x2d9fbd[-0xbcc+-0xf5*-0x3+-0x5*-0x1c9]=this['h0'],_0x2d9fbd[0x5*0x103+0x26ee+-0x2bfc]=this['h1'],_0x2d9fbd[0x7d+-0x1b05*-0x1+-0x1b80]=this['h2'],_0x2d9fbd[0x1*0x1c1c+0x1*0x4eb+-0x2104]=this['h3'],_0x31d586;},_0x22f902['prototype']['buffer']=_0x22f902[_0x2db296(0x344)][_0x2db296(0x249)],_0x22f902[_0x2db296(0x344)][_0x2db296(0x25f)]=function(){var _0x16e12f=_0x2db296;for(var _0x1dad5c,_0x24f20d,_0x70d99b,_0xb48dfc='',_0x13e0cb=this[_0x16e12f(0x238)](),_0x12e65e=-0x719*-0x5+-0x747*-0x5+0x170*-0x32;_0x12e65e<-0x8c+0x164f+-0x15b4;)_0x1dad5c=_0x13e0cb[_0x12e65e++],_0x24f20d=_0x13e0cb[_0x12e65e++],_0x70d99b=_0x13e0cb[_0x12e65e++],_0xb48dfc+=_0xcb4d61[_0x1dad5c>>>0x13b+0x209*-0xb+0x152a]+_0xcb4d61[0x13d3+0x4c7+-0x185b&(_0x1dad5c<<0x1334+0x1dd7+0x7*-0x701|_0x24f20d>>>-0x3b3*0xa+-0x17b*-0x13+-0x1*-0x8e1)]+_0xcb4d61[-0x25dd+0x14bd+0x115f&(_0x24f20d<<-0x787+0x888+-0xff|_0x70d99b>>>0xe74+-0x104f+0xd*0x25)]+_0xcb4d61[0x68e+-0x1082+-0x175*-0x7&_0x70d99b];return _0x1dad5c=_0x13e0cb[_0x12e65e],_0xb48dfc+=_0xcb4d61[_0x1dad5c>>>0x2b*-0x77+0x29e*0x2+0xec3]+_0xcb4d61[_0x1dad5c<<-0x14c0+-0x2*-0x11a4+0x2*-0x742&0xe5f+-0x1dab+0xf8b*0x1]+'==';};var _0x336469=_0x1edabb();_0x299a21?_0xb9c7d1[_0x2db296(0x1b8)]=_0x336469:(_0x11d177[_0x2db296(0x35c)]=_0x336469,_0x5efc8f&&(void(0x1118+-0x25*0x98+0x1a0*0x3))(function(){return _0x336469;}));}());});function _0x5dd467(_0x2e8eb5){var _0x344c50=_0x5612de;return w_0x5c3140(_0x344c50(0x333),{get 0x0(){return _0x90795;},0x1:arguments,0x2:_0x2e8eb5},this);}function _0x176a57(){var _0x33742c=_0x5612de;return!!document[_0x33742c(0x373)];}function _0x1230e7(){return'undefined'!=typeof InstallTrigger;}function _0xf8ccf1(){var _0x25d57d=_0x5612de;return/constructor/i[_0x25d57d(0x22b)](window['HTMLElement'])||_0x25d57d(0x226)===(!window['safari']||_0x25d57d(0x384)!=typeof safari&&safari['pushNotification'])[_0x25d57d(0x3ae)]();}function _0x30c916(){var _0x56f7c2=_0x5612de;return new Date()[_0x56f7c2(0x16d)]();}function _0x5af46a(_0xeaff35){return null==_0xeaff35?'':'boolean'==typeof _0xeaff35?_0xeaff35?'1':'0':_0xeaff35;}function _0x325f58(_0x3fdcb7,_0x2838bc){var _0x17d12c=_0x5612de;_0x2838bc||(_0x2838bc=_0x17d12c(0x24c));for(var _0x3b8f2c='',_0x98f0de=_0x3fdcb7;_0x98f0de>-0x2*-0x90e+0x4*-0x3ae+-0x364;--_0x98f0de)_0x3b8f2c+=_0x2838bc[Math[_0x17d12c(0x1cf)](Math['random']()*_0x2838bc[_0x17d12c(0x259)])];return _0x3b8f2c;}var _0x39693d={'sec':0x9,'asgw':0x5,'init':0x0},_0x6caf={'bogusIndex':0x0,'msNewTokenList':[],'moveList':[],'clickList':[],'keyboardList':[],'activeState':[],'aidList':[]};function _0x53b77d(_0x357347){return w_0x5c3140('484e4f4a403f5243001714366d6da13c00000025f8c25369000000ee00110307070002161103021200031103070700021303062b2f11030207000335490700044211010044001400011101014a1200001100010700010d05000000003c000e00054303491101034a12000607000711000143024911010433000611010412000833000911010412000812000947002100110107070002161101021200031101070700021303062b2f110102070003354902110105430047004f11010433002511010412000a11010412000b190400962934001111010412000c11010412000d190400962947002100110107070002161101021200031101070700021303062b2f11010207000335490842000e0e7170737c7b7045677a657067616c027c7108717077607272706707707b63767a71700003727061047c7b737a02307607767a7b667a797007737c67707760720a7a60617067427c71617d0a7c7b7b7067427c71617d0b7a606170675d707c727d610b7c7b7b70675d707c727d61',{get 0x0(){return Image;},0x1:Object,get 0x2(){return _0x6caf;},get 0x3(){return console;},get 0x4(){return window;},get 0x5(){return _0x1230e7;},0x6:arguments,0x7:_0x357347},this);}function _0x3ed707(){var _0x2077c7=_0x5612de;return w_0x5c3140(_0x2077c7(0x31d),{get 0x0(){return navigator;},get 0x1(){var _0x573a52=_0x2077c7;return _0x573a52(0x384)!=typeof global?global:void(-0x11*-0x1cf+0x95*0x16+-0x1*0x2b8d);},0x2:Object,get 0x3(){var _0x4b9092=_0x2077c7;return _0x4b9092(0x384)!=typeof process?process:void(-0x65*0x2+0x2579*0x1+-0x1*0x24af);},get 0x4(){return _0x1db123;},0x5:arguments},this);}function _0x328bde(_0xacb410,_0x4ed7bd,_0x49d137){var _0x197e65=_0x5612de,_0x2d8b28=_0x197e65(0x2ce),_0x4dd4b8='=';_0x49d137&&(_0x4dd4b8=''),_0x4ed7bd&&(_0x2d8b28=_0x4ed7bd);for(var _0x1aa067,_0x3b8c55='',_0x2d1a36=0xe0f*0x2+-0x35b*-0x8+0x29e*-0x15;_0xacb410[_0x197e65(0x259)]>=_0x2d1a36+(0x73*-0x26+-0xb4e+0x1c63);)_0x1aa067=(-0x1cb9+0x2c6+-0x1*-0x1af2&_0xacb410[_0x197e65(0x195)](_0x2d1a36++))<<0x1de9+0x1c+-0x1*0x1df5|(-0x1*0xd91+-0x23a+0x10ca&_0xacb410[_0x197e65(0x195)](_0x2d1a36++))<<-0x16*0x135+0x9e2*0x3+-0x188*0x2|-0x752+-0x2*0xaf3+0x1e37&_0xacb410[_0x197e65(0x195)](_0x2d1a36++),_0x3b8c55+=_0x2d8b28[_0x197e65(0x25c)]((0x1c78125+-0x1df1122+-0xb*-0x190d17&_0x1aa067)>>0x59*0x4f+-0x1176+-0x1*0x9ef),_0x3b8c55+=_0x2d8b28['charAt']((-0x1*0x13b6b+-0x13*-0x65aa+-0x26033&_0x1aa067)>>0x17ea*-0x1+0xef7+0x8ff),_0x3b8c55+=_0x2d8b28['charAt']((-0x1*0x16bd+0x115+0x10a*0x24&_0x1aa067)>>0x29c*-0x1+-0x1*0x1475+0x1717),_0x3b8c55+=_0x2d8b28[_0x197e65(0x25c)](-0xc*0x196+0x1fae+-0xc67&_0x1aa067);return _0xacb410[_0x197e65(0x259)]-_0x2d1a36>-0x69f+-0x1*0xe5c+0x14fb&&(_0x1aa067=(0x740+-0x1ed1+0x1890&_0xacb410['charCodeAt'](_0x2d1a36++))<<-0x7*0xec+0x2167*0x1+-0x1ae3|(_0xacb410['length']>_0x2d1a36?(0x16aa+-0x762*0x1+-0xe49&_0xacb410[_0x197e65(0x195)](_0x2d1a36))<<0x1af7+-0x26be+0xbcf:0x1*-0x649+-0x1*0xe16+0x2e9*0x7),_0x3b8c55+=_0x2d8b28[_0x197e65(0x25c)]((-0x1*0x484e9+-0x13c88*-0x4a+-0x28bd*-0x40d&_0x1aa067)>>0x6c*-0x4a+-0x81c*-0x2+0xf12),_0x3b8c55+=_0x2d8b28['charAt']((-0x1ad98+0x1a5c8+0x3f7d0&_0x1aa067)>>0x1f95+-0x1c06+-0x383),_0x3b8c55+=_0xacb410['length']>_0x2d1a36?_0x2d8b28[_0x197e65(0x25c)]((-0x2447+-0x1*-0x1efe+0x1509&_0x1aa067)>>0x1d66+0x2*0x202+-0x2164):_0x4dd4b8,_0x3b8c55+=_0x4dd4b8),_0x3b8c55;}function _0x389396(_0x49fdfd,_0x32eedf){var _0x36dcb8=_0x5612de;return w_0x5c3140(_0x36dcb8(0x3ad),{0x0:arguments,0x1:_0x49fdfd,0x2:_0x32eedf},this);}function _0x3262d3(_0xb01975){var _0x55341a=_0x5612de;return _0x55341a(0x315)[_0x55341a(0x2c5)](_0xb01975);}function _0xb5350b(_0x1289f5){var _0x341e09=_0x5612de,_0x5b1849,_0x40ad1e,_0x341488,_0x5980c5,_0x31f759,_0x5bebd7='';for(_0x5b1849=0x13d5+-0xc4f*0x1+-0x786;_0x5b1849<_0x1289f5[_0x341e09(0x259)]-(0xe50*0x1+0x17f3+-0x2640);_0x5b1849+=-0xeed+0x1*0x10c0+-0x1cf*0x1)_0x40ad1e=_0x3262d3(_0x1289f5[_0x341e09(0x25c)](_0x5b1849)),_0x341488=_0x3262d3(_0x1289f5[_0x341e09(0x25c)](_0x5b1849+(0x1103+0xa86+0x1*-0x1b88))),_0x5980c5=_0x3262d3(_0x1289f5[_0x341e09(0x25c)](_0x5b1849+(-0x1c54+-0xf15+0x2b6b))),_0x31f759=_0x3262d3(_0x1289f5['charAt'](_0x5b1849+(0xd1a+0x1*-0x175d+-0x5*-0x20e))),_0x5bebd7+=String[_0x341e09(0x1e2)](_0x40ad1e<<0x33*-0x3f+0xe73*-0x2+0x2975|_0x341488>>>0x12d2+0x851+-0x1b1f),'='!==_0x1289f5[_0x341e09(0x25c)](_0x5b1849+(0x51*-0x25+-0x21d*0x12+0x31c1*0x1))&&(_0x5bebd7+=String['fromCharCode'](_0x341488<<-0xa0c+0xee3+-0x4d3&0x1*0x397+-0x113f+0xe98|_0x5980c5>>>0x2161+0x1b7*-0x5+0xc66*-0x2&0x22bb+-0xf08+0x6*-0x346)),'='!==_0x1289f5[_0x341e09(0x25c)](_0x5b1849+(0x125d*0x2+-0x3d*-0x31+0x146*-0x26))&&(_0x5bebd7+=String[_0x341e09(0x1e2)](_0x5980c5<<-0x2650+0x17*0xe9+-0x3*-0x5cd&0x754+-0x5df+-0xb5|_0x31f759));return _0x5bebd7;}_0x6caf[_0x5612de(0x380)]=-0x14e*-0x2+0x2519+-0x217*0x13,_0x6caf['msToken']='',_0x6caf[_0x5612de(0x263)]=_0x39693d[_0x5612de(0x3b9)],_0x6caf[_0x5612de(0x3b5)]='',_0x6caf['ttwid']='',_0x6caf[_0x5612de(0x316)]='',_0x6caf[_0x5612de(0x339)]='';var _0x28d239=0x778+0xa41+0x1*-0x11b9,_0x2d6b72,_0x1e9bba,_0xdc7355,_0xf08186;function _0x33f406(_0x4b5fb5){var _0x1d1da4=_0x5612de;return _0x4b5fb5&=-0x2632+0x3*-0x591+0x3724,String[_0x1d1da4(0x1e2)](_0x4b5fb5+(_0x4b5fb5<0x22f*0x1+-0x1cac+0x3*0x8dd?-0xd*0x123+0x19ac+-0x552*0x2:_0x4b5fb5<-0x1*-0x1646+0x1*-0x1f21+-0x305*-0x3?-0x2b4*0x1+-0x45*0xb+-0x2*-0x2f9:_0x4b5fb5<0x1*0x70c+0x123f+-0x190d*0x1?-(0x1f73+-0x1*-0x3d7+-0x46*0x81):-(-0x248f+0x20d3+0x3cd)));}function _0x4da136(_0x620f42){var _0xca40fa=_0x33f406;return _0xca40fa(_0x620f42>>0x420*-0x4+-0x53f*-0x4+-0x464)+_0xca40fa(_0x620f42>>-0x1492+-0x11c3+-0x3*-0xccd)+_0xca40fa(_0x620f42>>0x1479+0x571*0x6+-0x795*0x7)+_0xca40fa(_0x620f42>>-0x1d*0x4f+0x313*-0xb+0x2aca)+_0xca40fa(_0x620f42);}_0x2d6b72=_0x1e9bba=function(_0x5d2d39){return _0x2d6b72=_0xdc7355,_0x28d239=_0x5d2d39,_0x4da136(_0x5d2d39>>-0x418*0x8+0x9fe+0x7c*0x2f);},_0xdc7355=function(_0x135d9a){_0x2d6b72=_0xf08186;var _0x9cdabb=_0x28d239<<-0xa41+-0x164f+0x2b9*0xc|_0x135d9a>>>0xee4+0x1*-0x143+0xcd*-0x11;return _0x28d239=_0x135d9a,_0x4da136(_0x9cdabb);},_0xf08186=function(_0x36c054){return _0x2d6b72=_0x1e9bba,_0x4da136(_0x28d239<<-0x302+0x1*0x2447+-0x7*0x4bd|_0x36c054>>>-0x1f5e+0x1aa0+0x3d*0x14)+_0x33f406(_0x36c054);};var _0x539097=-0x2*-0x621a4af5+0xc681287f+-0xec7e44b0,_0x12ada0;function _0x75d5b3(_0x5a0afa,_0x1ccbbd){var _0x13780b=_0x5612de,_0xc312d4=_0x5a0afa['length'],_0x102845=_0xc312d4<<-0x3*-0x711+0x9a9*0x2+-0x2883;if(_0x1ccbbd){var _0x25438e=_0x5a0afa[_0xc312d4-(0x1b18+0x1*0xfb6+-0x1*0x2acd)];if(_0x25438e<(_0x102845-=0x1f*0x62+-0xac0+-0x8d*0x2)-(-0xfed+-0x8a*0x23+0x22ce)||_0x25438e>_0x102845)return null;_0x102845=_0x25438e;}for(var _0x41e0a1=-0x13b+-0x1963+-0xd4f*-0x2;_0x41e0a1<_0xc312d4;_0x41e0a1++)_0x5a0afa[_0x41e0a1]=String['fromCharCode'](-0x30*-0x12+-0x235b+-0x15*-0x192&_0x5a0afa[_0x41e0a1],_0x5a0afa[_0x41e0a1]>>>-0x1839*-0x1+-0x5*-0x196+0x1*-0x201f&-0x2*0x2b3+0x1*0x19c9+-0x1364,_0x5a0afa[_0x41e0a1]>>>-0x12b*0x1+0xa54*0x1+-0x11*0x89&0xc36*0x2+0x2*0x1259+0x1*-0x3c1f,_0x5a0afa[_0x41e0a1]>>>0x564+0x2325+-0x15*0x1ed&0x2673+-0x1411+-0x1163);var _0x5ef85e=_0x5a0afa[_0x13780b(0x24f)]('');return _0x1ccbbd?_0x5ef85e['substring'](0x3*-0x388+-0xe41+0x18d9,_0x102845):_0x5ef85e;}function _0x183951(_0x15f4d9,_0x59200b){var _0x4f373f=_0x5612de,_0x40761a,_0x6155ee=_0x15f4d9[_0x4f373f(0x259)],_0x21fe82=_0x6155ee>>0xb8d*-0x1+-0x1*0x5fe+-0x1*-0x118d;0x4*-0xbc+0x12f0+-0x1000!=(-0x79*0x2+-0xb1*0x16+0x102b&_0x6155ee)&&++_0x21fe82,_0x59200b?(_0x40761a=new Array(_0x21fe82+(0x13*-0x65+-0x14ef+0xfb*0x1d)))[_0x21fe82]=_0x6155ee:_0x40761a=new Array(_0x21fe82);for(var _0x56bca4=-0x9e0+0x51*0x1a+0x1a6;_0x56bca4<_0x6155ee;++_0x56bca4)_0x40761a[_0x56bca4>>-0xc1b*-0x2+-0xc2*-0x21+0x1*-0x3136]|=_0x15f4d9['charCodeAt'](_0x56bca4)<<((0x3*-0x4cd+-0xdfc+0x1c66&_0x56bca4)<<0x1*-0x133b+0x21a5+0xe67*-0x1);return _0x40761a;}function _0x25c901(_0x431b9d){return 0x1a1299587+0x91539*-0x1c1b+-0x202b07*-0x2ed&_0x431b9d;}function _0x38d33e(_0x1aed31,_0x212e8a,_0x427b15,_0x8fcd39,_0x4eaad6,_0x532b8e){return(_0x427b15>>>-0x1*-0x1ddb+-0x20f7+-0x3*-0x10b^_0x212e8a<<-0x1*-0x407+-0x132e+-0xf29*-0x1)+(_0x212e8a>>>-0x828+-0xf23+-0x9d*-0x26^_0x427b15<<-0xd51+0xc9f+0xb6)^(_0x1aed31^_0x212e8a)+(_0x532b8e[0x9ad+-0x763+-0xb*0x35&_0x8fcd39^_0x4eaad6]^_0x427b15);}function _0x231484(_0xefe811){var _0x13eaf5=_0x5612de;return _0xefe811[_0x13eaf5(0x259)]<-0xa13+0x1086+-0x66f&&(_0xefe811[_0x13eaf5(0x259)]=-0x1371+-0x1ec1*-0x1+0x5a6*-0x2),_0xefe811;}function _0x3d58e7(_0x5e28f2,_0x468b52){var _0x50c100=_0x5612de,_0x7c35df,_0x4c97b3,_0x5e0270,_0x3034db,_0x56f817,_0x3084b8,_0x66df40=_0x5e28f2[_0x50c100(0x259)],_0x5506f5=_0x66df40-(-0x1c76+0x2246+-0x5cf);for(_0x4c97b3=_0x5e28f2[_0x5506f5],_0x5e0270=0x22ee+0xa15*-0x1+-0x18d9*0x1,_0x3084b8=0x1*0x36f+-0x2707+0x2398|Math[_0x50c100(0x1cf)](0xc72*0x2+-0xe3*0x2b+0xd43+(0x1a+0x1c2a+-0x1c10)/_0x66df40);_0x3084b8>0x1*-0x1cd+-0x2476+0x2643;--_0x3084b8){for(_0x3034db=(_0x5e0270=_0x25c901(_0x5e0270+_0x539097))>>>0x3b*-0x2f+0x1290+0x3*-0x293&0x1533+-0x116d+-0x3c3,_0x56f817=0x8*0x23b+-0xb89+0x64f*-0x1;_0x56f817<_0x5506f5;++_0x56f817)_0x7c35df=_0x5e28f2[_0x56f817+(0x3b*0x5f+-0x1e1*0x1+-0x1403)],_0x4c97b3=_0x5e28f2[_0x56f817]=_0x25c901(_0x5e28f2[_0x56f817]+_0x38d33e(_0x5e0270,_0x7c35df,_0x4c97b3,_0x56f817,_0x3034db,_0x468b52));_0x7c35df=_0x5e28f2[-0x1570+-0x1*-0x385+-0x8b*-0x21],_0x4c97b3=_0x5e28f2[_0x5506f5]=_0x25c901(_0x5e28f2[_0x5506f5]+_0x38d33e(_0x5e0270,_0x7c35df,_0x4c97b3,_0x5506f5,_0x3034db,_0x468b52));}return _0x5e28f2;}function _0x4aaf04(_0x33a7ba,_0x386196){var _0x257c0b=_0x5612de,_0x379288,_0x247a8b,_0x334bb9,_0x2fa83f,_0x417a88,_0x2a3ae5=_0x33a7ba[_0x257c0b(0x259)],_0x429a0d=_0x2a3ae5-(0xc1*-0x11+0x1f99+-0xb*0x1b5);for(_0x379288=_0x33a7ba[-0x1*-0x1397+-0x13*-0x187+-0x309c],_0x334bb9=_0x25c901(Math[_0x257c0b(0x1cf)](0xe52*-0x1+-0x10*0xb+-0x34*-0x4a+(-0x3*-0xd9+0x9f*0x2f+0xfc4*-0x2)/_0x2a3ae5)*_0x539097);-0x2e*0x1f+0x2223+-0x47*0x67!==_0x334bb9;_0x334bb9=_0x25c901(_0x334bb9-_0x539097)){for(_0x2fa83f=_0x334bb9>>>0x437*0x5+-0xfd*0x22+-0xc89*-0x1&0x1*0x863+0x2267*0x1+-0x2f*0xe9,_0x417a88=_0x429a0d;_0x417a88>0xfc7+-0x788*-0x1+-0x15f*0x11;--_0x417a88)_0x247a8b=_0x33a7ba[_0x417a88-(0x3b*-0x4f+0x6d*-0xa+0xb3c*0x2)],_0x379288=_0x33a7ba[_0x417a88]=_0x25c901(_0x33a7ba[_0x417a88]-_0x38d33e(_0x334bb9,_0x379288,_0x247a8b,_0x417a88,_0x2fa83f,_0x386196));_0x247a8b=_0x33a7ba[_0x429a0d],_0x379288=_0x33a7ba[-0x1fb+0x1952*-0x1+-0x1d*-0xf1]=_0x25c901(_0x33a7ba[0x7*0x59+0x5*-0x2d2+0xbab]-_0x38d33e(_0x334bb9,_0x379288,_0x247a8b,-0xbac+-0x7*0xea+-0x606*-0x3,_0x2fa83f,_0x386196));}return _0x33a7ba;}function _0x532596(_0x2394ed){var _0x22f5e8=_0x5612de;if(/^[\x00-\x7f]*$/[_0x22f5e8(0x22b)](_0x2394ed))return _0x2394ed;for(var _0x47e840=[],_0x5f04b8=_0x2394ed[_0x22f5e8(0x259)],_0x150666=-0xe97+0xd02+0x195,_0x19b157=0xf1c+-0x1f5*0x8+0x8c;_0x150666<_0x5f04b8;++_0x150666,++_0x19b157){var _0x44de53=_0x2394ed[_0x22f5e8(0x195)](_0x150666);if(_0x44de53<-0x1*-0xb7b+0x1566+-0x2061)_0x47e840[_0x19b157]=_0x2394ed[_0x22f5e8(0x25c)](_0x150666);else{if(_0x44de53<-0x3*0xcd2+0x1168+0x1*0x1d0e)_0x47e840[_0x19b157]=String['fromCharCode'](0x2*0x579+0x263c*0x1+0x306e*-0x1|_0x44de53>>0xca4*-0x1+0x6f*0x13+-0x46d*-0x1,0x7*-0x5+0x35*0x73+-0x172c|0xf88*-0x1+-0x108f*-0x1+-0xc8&_0x44de53);else{if(!(_0x44de53<-0xaebb*0x1+-0x3*-0x3a46+0x6f7*0x1f||_0x44de53>0x1679b*0x1+-0x121*-0x15+-0x9f51)){if(_0x150666+(0x15a6+0x1ec1+-0x26*0x161)<_0x5f04b8){var _0x254ff6=_0x2394ed[_0x22f5e8(0x195)](_0x150666+(-0x42*-0x7a+0x4*0x91d+-0x43e7));if(_0x44de53<-0x181e4+0xbcbb+0x1a129&&-0x17db*-0xf+0xac6d*-0x1+-0x3d8*-0x9<=_0x254ff6&&_0x254ff6<=0x159a+0x17967+0x9b9*-0x12){var _0x304310=-0x1862f+0x1121d+0x2*0xba09+((0x1*-0x140f+-0x18dd+0x30eb&_0x44de53)<<-0xb0*-0x8+0x875+-0x7*0x1fd|0x2301+0x171f+0x3621*-0x1&_0x254ff6);_0x47e840[_0x19b157]=String[_0x22f5e8(0x1e2)](0x1afc*-0x1+0x26f4+-0xb08|_0x304310>>0xb*0x14e+-0x119*-0x1d+-0x2e1d&0x1037*0x1+-0x1090+-0x2*-0x4c,0x1922+0x4*0x351+-0x25e6|_0x304310>>0x1e93+-0x517+-0x1970&0x2481+0x1591+-0x39d3,-0xace*-0x1+0x1ef3+-0x2941|_0x304310>>-0xb7f*-0x3+-0x1*0xe7d+-0x13fa&-0x10d*-0x6+0x252a*0x1+-0x2b39,-0x1c61*0x1+0x1*-0x5e7+-0x13e*-0x1c|0x13*-0x1f9+0x16*-0x164+-0x37*-0x13e&_0x304310),++_0x150666;continue;}}throw new Error(_0x22f5e8(0x297));}_0x47e840[_0x19b157]=String[_0x22f5e8(0x1e2)](-0x170b+0x109b+0x750|_0x44de53>>-0xbf0*-0x2+0x25bc+-0x3d90,0x4ec+0x1f0c+-0x8de*0x4|_0x44de53>>0x2*0x829+-0x1b*-0x10c+-0x2c90&-0x2322+0x19d5+0x34*0x2f,0x2a1+0x1*0x1ac0+0x1*-0x1ce1|-0xe95+0x1cf*0x11+0x1*-0xfeb&_0x44de53);}}}return _0x47e840[_0x22f5e8(0x24f)]('');}function _0x45fbef(_0x316827,_0x2a039f){var _0x1c80ac=_0x5612de;for(var _0x4921e2=new Array(_0x2a039f),_0x79a402=0x20bd+-0x10b1+-0x1*0x100c,_0x28b895=-0xd2c+-0x1730+0x1*0x245c,_0x2bc88b=_0x316827[_0x1c80ac(0x259)];_0x79a402<_0x2a039f&&_0x28b895<_0x2bc88b;_0x79a402++){var _0x43b518=_0x316827[_0x1c80ac(0x195)](_0x28b895++);switch(_0x43b518>>-0x1342+0x67*-0x53+0x34ab){case-0x410*-0x3+-0xd9+-0xb57:case-0x17cb+0x2362+-0xb96*0x1:case-0x24*0x7f+-0x1*-0xcfc+0x19*0x32:case 0x2025+0x1102+-0x3124:case 0x1b0d+0xcb4+0xd3f*-0x3:case-0x11d9+0x9*0x16d+-0x1*-0x509:case 0x79*0x49+0x1*0x1b8e+-0x3e09*0x1:case 0x26c1+-0x2137*0x1+-0x583:_0x4921e2[_0x79a402]=_0x43b518;break;case 0x10f*-0x13+0x426+0x1003:case 0x164d*0x1+0xb5a*0x3+0x1c27*-0x2:if(!(_0x28b895<_0x2bc88b))throw new Error('Unfinished\x20UTF-8\x20octet\x20sequence');_0x4921e2[_0x79a402]=(-0x1f7*0xb+-0x1fa0+0x2ab*0x14&_0x43b518)<<-0x233b+0x160e+0xd33|-0x1e40+0x22b9+-0x2*0x21d&_0x316827[_0x1c80ac(0x195)](_0x28b895++);break;case-0x2512+-0x1595+0x3ab5:if(!(_0x28b895+(0xba3*-0x1+-0x17dd*0x1+0x1*0x2381)<_0x2bc88b))throw new Error(_0x1c80ac(0x394));_0x4921e2[_0x79a402]=(0x247f*-0x1+0xec2*0x1+-0x136*-0x12&_0x43b518)<<0x1*0x1bcd+-0x3dd+-0x17e4|(0x1631+-0x1058+0x2*-0x2cd&_0x316827[_0x1c80ac(0x195)](_0x28b895++))<<0x889*0x4+0x11c8*0x2+-0x45ae|-0x698*-0x4+-0x2f*-0x4f+-0x28a2&_0x316827[_0x1c80ac(0x195)](_0x28b895++);break;case-0x85*-0x8+0x1148+-0x1561:if(!(_0x28b895+(0x1*-0x8f1+-0x67*0x3e+0x21e5)<_0x2bc88b))throw new Error(_0x1c80ac(0x394));var _0x1740ac=((0x1*-0x1d4f+0x1*0x24bf+-0x769&_0x43b518)<<0xb19+0x4a*-0x48+-0x3*-0x343|(-0x1ab4+-0x205*-0x8+-0x3*-0x399&_0x316827[_0x1c80ac(0x195)](_0x28b895++))<<0x1e52+-0x144b+-0x5*0x1ff|(0x4c5*-0x3+0x1*-0x70b+0x1599&_0x316827[_0x1c80ac(0x195)](_0x28b895++))<<-0xf6e+-0x37*-0x86+0x239*-0x6|0x2*-0xc31+0x26b5+-0xe14&_0x316827[_0x1c80ac(0x195)](_0x28b895++))-(0x1*-0x6b6b+-0x14502+-0x1*-0x2b06d);if(!(0x86*0xd+-0x2210+0x1b42<=_0x1740ac&&_0x1740ac<=0x141d1*0x1+-0x3044*-0x8b+0x1*-0xb76be))throw new Error(_0x1c80ac(0x24e)+_0x1740ac['toString'](-0x959*0x3+-0x50e*-0x5+-0x1*-0x2d5));_0x4921e2[_0x79a402++]=_0x1740ac>>-0x1a89*0x1+0x125*-0x1+0x1bb8&-0x2*0x12e4+0xc1e+0x1da9|-0x4e*-0x332+-0x6764*-0x2+-0xf004*0x1,_0x4921e2[_0x79a402]=0x23f0+0x2b*0x11+-0x4*0x8b3&_0x1740ac|0xb45d+-0xd352+0x3*0x53a7;break;default:throw new Error(_0x1c80ac(0x330)+_0x43b518[_0x1c80ac(0x3ae)](-0x1f71+0xdc3*-0x1+0x2d44));}}return _0x79a402<_0x2a039f&&(_0x4921e2[_0x1c80ac(0x259)]=_0x79a402),String[_0x1c80ac(0x1e2)]['apply'](String,_0x4921e2);}function _0x25bfe1(_0x5ad020,_0x1001f1){var _0xa8facb=_0x5612de;for(var _0xdb2671=[],_0x67f891=new Array(-0x29*0x169+0x1d*-0x3c7+-0x6*-0x313a),_0x3fce02=-0x47c*-0x1+-0xc83+0x2ad*0x3,_0x47254b=0x1010+-0xb*0x29+-0xe4d,_0x4445a9=_0x5ad020[_0xa8facb(0x259)];_0x3fce02<_0x1001f1&&_0x47254b<_0x4445a9;_0x3fce02++){var _0x488af6=_0x5ad020['charCodeAt'](_0x47254b++);switch(_0x488af6>>-0x1148+0xbd2+0x57a){case-0x1*-0xb6b+0x16f6+-0x2261:case-0x1678+-0x1c12+0x3*0x10d9:case 0xd01*-0x2+0x2c2+-0x1a*-0xe5:case 0x1985+0x49*-0x6d+0x593:case 0xbac+0xd1a*0x1+-0x18c2:case-0x11*-0x1+0x1*-0x18bd+0x18b1:case 0x1e6+-0x129a*-0x1+0x1*-0x147a:case-0x1*0xf49+0xed+0xe63:_0x67f891[_0x3fce02]=_0x488af6;break;case 0x30e*0xb+0x255b+-0x1*0x46e9:case 0x71*-0xf+-0x1371+0x1a1d:if(!(_0x47254b<_0x4445a9))throw new Error(_0xa8facb(0x394));_0x67f891[_0x3fce02]=(-0x407*-0x5+-0xcb5+0x74f*-0x1&_0x488af6)<<-0x585+-0xbf8+0x1183|-0x1*0x1e25+0x6d*-0x3b+0x1281*0x3&_0x5ad020['charCodeAt'](_0x47254b++);break;case-0x676*-0x4+-0x351+-0x1679:if(!(_0x47254b+(0x1b63+-0x5*0x463+-0x573)<_0x4445a9))throw new Error(_0xa8facb(0x394));_0x67f891[_0x3fce02]=(0x2dd*0x7+-0x15dd+0x1e1&_0x488af6)<<0x141c+0x18e3+-0x2cf3|(-0x1*-0x619+0x1*0x18bf+-0xa33*0x3&_0x5ad020[_0xa8facb(0x195)](_0x47254b++))<<0x1343*-0x1+-0x9*0x198+0x21a1|0x455+-0x1ef3+0x1add&_0x5ad020['charCodeAt'](_0x47254b++);break;case-0x21f+-0x13f9*0x1+-0x6b*-0x35:if(!(_0x47254b+(-0xde*-0xb+0x20cf+-0x2a57)<_0x4445a9))throw new Error(_0xa8facb(0x394));var _0x59357e=((0x1081+-0x1b6f+0xaf5&_0x488af6)<<-0x1*-0x511+-0x45*0x55+0x11ea|(0x125a*0x2+0x4bd*-0x2+-0x1afb&_0x5ad020[_0xa8facb(0x195)](_0x47254b++))<<0xbf4*-0x2+-0x1512+0x2d06|(0x1aae+0xd81+-0x27f0&_0x5ad020[_0xa8facb(0x195)](_0x47254b++))<<0x1759+-0x9a1*-0x1+0x4c*-0x6f|0x352*-0xb+-0xde7+0x32ac&_0x5ad020[_0xa8facb(0x195)](_0x47254b++))-(-0x57e9+0x19207+-0x3a1e);if(!(0x41b*-0x7+0x1658+0x1*0x665<=_0x59357e&&_0x59357e<=-0xf6*-0x7ed+-0x1bcf3b+-0x4*-0x90c5f))throw new Error('Character\x20outside\x20valid\x20Unicode\x20range:\x200x'+_0x59357e['toString'](-0x40f+-0x5*-0x61f+0x1c4*-0xf));_0x67f891[_0x3fce02++]=_0x59357e>>-0x15e2+0x1*-0xb51+-0x43*-0x7f&-0x11a2+0xb*-0x29+0x7cc*0x3|0x1*-0x3d14+0x159a9+-0x1*0x4495,_0x67f891[_0x3fce02]=0x1*0x101+-0x499+0x797&_0x59357e|0x1dec+0x87f7+0x361d;break;default:throw new Error('Bad\x20UTF-8\x20encoding\x200x'+_0x488af6[_0xa8facb(0x3ae)](-0x11*0x1dc+0x1a53+0x559));}if(_0x3fce02>=-0xb3*-0x16e+0x1616+0x1a6*-0x5b){var _0x2ceb4e=_0x3fce02+(0x4*-0x8fe+0x665*0x3+0x10ca);_0x67f891[_0xa8facb(0x259)]=_0x2ceb4e,_0xdb2671[_0xdb2671[_0xa8facb(0x259)]]=String[_0xa8facb(0x1e2)][_0xa8facb(0x207)](String,_0x67f891),_0x1001f1-=_0x2ceb4e,_0x3fce02=-(-0x18*-0x111+0x2*0x9e3+-0x2d5d);}}return _0x3fce02>0x1*0xb5d+-0x20a3+0x1546&&(_0x67f891['length']=_0x3fce02,_0xdb2671[_0xdb2671[_0xa8facb(0x259)]]=String['fromCharCode'][_0xa8facb(0x207)](String,_0x67f891)),_0xdb2671['join']('');}function _0x31dfb5(_0x360a73,_0xe765ba){var _0x293f34=_0x5612de;return(null==_0xe765ba||_0xe765ba<-0x1605+0xf7*0xe+0x883)&&(_0xe765ba=_0x360a73[_0x293f34(0x259)]),-0x6*0x30b+-0x1608+0x284a===_0xe765ba?'':/^[\x00-\x7f]*$/[_0x293f34(0x22b)](_0x360a73)||!/^[\x00-\xff]*$/[_0x293f34(0x22b)](_0x360a73)?_0xe765ba===_0x360a73[_0x293f34(0x259)]?_0x360a73:_0x360a73[_0x293f34(0x3a7)](0x169+0x887+0x6a*-0x18,_0xe765ba):_0xe765ba<0x1d0d6+0x73e5+-0x4*0x512f?_0x45fbef(_0x360a73,_0xe765ba):_0x25bfe1(_0x360a73,_0xe765ba);}function _0xdda738(_0x21d8ce,_0x4bf3f9){var _0x46d91f=_0x5612de;return null==_0x21d8ce||0x1*-0xab1+-0x2567*-0x1+-0x1ab6===_0x21d8ce[_0x46d91f(0x259)]?_0x21d8ce:(_0x21d8ce=_0x532596(_0x21d8ce),_0x4bf3f9=_0x532596(_0x4bf3f9),_0x75d5b3(_0x3d58e7(_0x183951(_0x21d8ce,!(0xad5+0xaa1+-0x2*0xabb)),_0x231484(_0x183951(_0x4bf3f9,!(-0x106c+-0x1173*-0x2+-0x1279)))),!(-0x1*0x2c8+-0xc8+0x391)));}function _0x4bb829(_0x1f763e,_0x47e9cd){var _0x18b798=_0x5612de;return null==_0x1f763e||-0x12*0xe1+0x3cb*-0x5+0x22c9===_0x1f763e[_0x18b798(0x259)]?_0x1f763e:(_0x47e9cd=_0x532596(_0x47e9cd),_0x31dfb5(_0x75d5b3(_0x4aaf04(_0x183951(_0x1f763e,!(-0x40*-0x16+-0x133*-0x1d+-0x2846)),_0x231484(_0x183951(_0x47e9cd,!(0x1e64+0xcee+-0x2b51)))),!(-0x1daa+-0xbb1+0x295b))));}function _0x39dfe4(){var _0x2fd4da=_0x5612de,_0x21e994='';try{window[_0x2fd4da(0x3be)]&&(_0x21e994=window[_0x2fd4da(0x3be)]['getItem']('_byted_param_sw')),_0x21e994&&!window[_0x2fd4da(0x1dc)]||(_0x21e994=window['localStorage'][_0x2fd4da(0x2d8)]('_byted_param_sw'));}catch(_0x3b8cd9){}if(_0x21e994)try{var _0x25acb5=_0x4bb829(_0xb5350b(_0x21e994[_0x2fd4da(0x2a5)](0x19*0xc9+-0x1bc8+-0x82f*-0x1)),_0x21e994[_0x2fd4da(0x2a5)](0x2*0xa88+-0x25*-0x94+-0x2a74,0x2c6*0x5+-0x14c6+0x1bc*0x4));if('on'===_0x25acb5)return!(0x527*-0x7+0x7b7+0x1c5a);if(_0x2fd4da(0x304)===_0x25acb5)return!(0x1b29*-0x1+-0xd8d+0x1*0x28b7);}catch(_0x1da1e4){}return!(-0x158c+0x10*-0x13d+0x1*0x295d);}function _0x1b4bf1(){var _0x4eb990=_0x5612de;return w_0x5c3140(_0x4eb990(0x211),{get 0x0(){var _0x448d97=_0x4eb990;return _0x448d97(0x384)!=typeof navigator?navigator:void(-0xd95*-0x2+-0x1*0x16ed+0x5*-0xd9);},get 0x1(){var _0x389269=_0x4eb990;return _0x389269(0x384)!=typeof window?window:void(0x63e+0xb*-0x32b+-0x1c9b*-0x1);},get 0x2(){return _0x1db123;},0x3:Object,get 0x4(){return'undefined'!=typeof document?document:void(0xe3a+0x2499+-0x32d3);},get 0x5(){var _0x813809=_0x4eb990;return _0x813809(0x384)!=typeof location?location:void(-0x1*-0x2145+0x54e*-0x7+-0x1*-0x3dd);},get 0x6(){return _0x176a57;},get 0x7(){return'undefined'!=typeof history?history:void(-0x17d+-0x1ef4+0x2071);},0x8:arguments},this);}function _0x532cd9(){var _0x3b3ba1=_0x5612de;return w_0x5c3140(_0x3b3ba1(0x190),{get 0x0(){return _0x176a57;},get 0x1(){return navigator;},get 0x2(){return PluginArray;},get 0x3(){return window;},0x4:arguments},this);}function _0x3391fc(){var _0x2aea9b=_0x5612de;return w_0x5c3140(_0x2aea9b(0x266),{get 0x0(){return _0x12ada0;},get 0x1(){return navigator;},0x2:Object,get 0x3(){return window;},0x4:arguments,0x5:RegExp},this);}function _0x21fa28(){var _0x1106df=_0x5612de;return w_0x5c3140(_0x1106df(0x17b),{set 0x0(_0x2b3a0b){_0x12ada0=_0x2b3a0b;},0x1:Object,get 0x2(){return window;},0x3:arguments},this);}function _0x49b1d7(_0x4f4807){var _0x4f1753=_0x5612de;return w_0x5c3140(_0x4f1753(0x171),{get 0x0(){return _0x1230e7;},get 0x1(){return indexedDB;},get 0x2(){return _0xf8ccf1;},get 0x3(){return window;},get 0x4(){return DOMException;},get 0x5(){return _0x176a57;},get 0x6(){return _0x6caf;},0x7:arguments,0x8:_0x4f4807},this);}function _0x462256(){var _0x1ed1aa=_0x5612de;return w_0x5c3140(_0x1ed1aa(0x163),{get 0x0(){return _0x176a57;},get 0x1(){return document;},get 0x2(){return navigator;},0x3:arguments,0x4:RegExp},this);}function _0x3cee0e(){var _0x5f1e22=_0x5612de;return w_0x5c3140(_0x5f1e22(0x2dd),{get 0x0(){return navigator;},get 0x1(){return window;},0x2:arguments,0x3:RegExp},this);}function _0x1f9824(){var _0x1d4f72=_0x5612de,_0x5821af='';if(_0x6caf['PLUGIN'])_0x5821af=_0x6caf[_0x1d4f72(0x354)];else{for(var _0x580f96=[],_0x3ca683=navigator[_0x1d4f72(0x1ba)]||[],_0x15f771=0x389*0x5+-0x2a*-0xe2+-0x36c1;_0x15f771<-0xe3b*0x1+0x1a76+-0x6*0x209;_0x15f771++)try{for(var _0x5a4f6c=_0x3ca683[_0x15f771],_0x3a1f6=[],_0x1a6da9=-0x869+-0x111*0x14+-0x1dbd*-0x1;_0x1a6da9<_0x5a4f6c[_0x1d4f72(0x259)];_0x1a6da9++)_0x5a4f6c[_0x1d4f72(0x37c)](_0x1a6da9)&&_0x3a1f6[_0x1d4f72(0x36e)](_0x5a4f6c['item'](_0x1a6da9)[_0x1d4f72(0x1ab)]);var _0x1ae7c8=_0x5a4f6c[_0x1d4f72(0x341)]+'';_0x5a4f6c['version']&&(_0x1ae7c8+=_0x5a4f6c[_0x1d4f72(0x2c1)]+''),_0x1ae7c8+=_0x5a4f6c['filename']+'',_0x1ae7c8+=_0x3a1f6[_0x1d4f72(0x24f)](''),_0x580f96['push'](_0x1ae7c8);}catch(_0x4e31c6){}_0x5821af=_0x580f96[_0x1d4f72(0x24f)]('##'),_0x6caf[_0x1d4f72(0x354)]=_0x5821af;}return _0x5821af[_0x1d4f72(0x2a5)](-0x1*0x26c+-0x1e2f+0x209b,-0x26ff+0x1b6e*0x1+0xf91);}function _0x30412e(){var _0x5a747f=_0x5612de,_0x5640f8=[];try{var _0x3405cb=navigator[_0x5a747f(0x1ba)];if(_0x3405cb){for(var _0x53b06e=-0x8aa+0x341+0x115*0x5;_0x53b06e<_0x3405cb[_0x5a747f(0x259)];_0x53b06e++)for(var _0xa74ec6=-0x1829+0x1f9c+-0x773;_0xa74ec6<_0x3405cb[_0x53b06e][_0x5a747f(0x259)];_0xa74ec6++){var _0x2bcc6c=[_0x3405cb[_0x53b06e][_0x5a747f(0x19e)],_0x3405cb[_0x53b06e][_0xa74ec6][_0x5a747f(0x1ab)],_0x3405cb[_0x53b06e][_0xa74ec6][_0x5a747f(0x169)]][_0x5a747f(0x24f)]('|');_0x5640f8['push'](_0x2bcc6c);}}}catch(_0x11c0f4){}return _0x5640f8;}function _0x28e2ec(){var _0x3301ae=_0x5612de;return w_0x5c3140(_0x3301ae(0x1a6),{get 0x0(){return navigator;},get 0x1(){return _0x1f9824;},get 0x2(){return window;},0x3:arguments},this);}function _0x5863d1(){var _0x247350=_0x5612de;return w_0x5c3140(_0x247350(0x21c),{get 0x0(){return _0x462335;},get 0x1(){return _0x39dfe4;},get 0x2(){return _0x1b4bf1;},get 0x3(){return _0x53b77d;},get 0x4(){return _0x49b1d7;},get 0x5(){return _0x3ed707;},get 0x6(){return _0x532cd9;},get 0x7(){return _0x3391fc;},get 0x8(){return _0x462256;},get 0x9(){return _0x3cee0e;},get 0xa(){return _0x28e2ec;},get 0xb(){return _0x6caf;},0xc:arguments},this);}function _0x2a900b(_0x29489b){var _0x3163ac=_0x5612de;for(var _0x2edc36=Object[_0x3163ac(0x17f)](_0x29489b),_0x4da10f=-0x26c9+-0x11f1*-0x1+0x14d8,_0x26fc90=_0x2edc36[_0x3163ac(0x259)]-(0x1cea+-0x5*0x2f5+0x8*-0x1c4);_0x26fc90>=-0x2*-0x397+0x17ce+-0x1efc;_0x26fc90--){_0x4da10f=(_0x29489b[_0x2edc36[_0x26fc90]]?0x455*0x9+0x1715+-0x3e11:-0x1*-0x22f7+0x1f6c*0x1+-0x203*0x21)<<_0x2edc36[_0x3163ac(0x259)]-_0x26fc90-(-0x64c*-0x6+-0x163+-0x224*0x11)|_0x4da10f;}return _0x4da10f;}function _0x246aeb(_0x1b02ae,_0x32abc9){var _0x172517=_0x5612de;for(var _0x4d2e6c=0x1e44+0x2*0x443+0x2*-0x1365;_0x4d2e6c<_0x32abc9[_0x172517(0x259)];_0x4d2e6c++)_0x1b02ae=(-0xa15f+-0x1a803+0x349a1)*_0x1b02ae+_0x32abc9[_0x172517(0x195)](_0x4d2e6c)>>>-0x2c6+0x2*-0x126c+0x279e;return _0x1b02ae;}function _0x184783(_0x9867bc,_0x21d0e6){var _0x25b989=_0x5612de;for(var _0x21703e=-0xa6*-0x1+0xbf*-0x5+-0x315*-0x1;_0x21703e<_0x21d0e6[_0x25b989(0x259)];_0x21703e++)_0x9867bc=(-0x6275+0xd966+-0x5f*-0x172)*(_0x9867bc^_0x21d0e6['charCodeAt'](_0x21703e))>>>-0xf*0x1a+0x7*-0x543+0x443*0x9;return _0x9867bc;}function _0xf119da(_0x57529f,_0x19340f){var _0x1de06d=_0x5612de;for(var _0x3e7b02=0x45a*0x1+-0xe5d*0x2+0x1860;_0x3e7b02<_0x19340f[_0x1de06d(0x259)];_0x3e7b02++){var _0x25af5d=_0x19340f[_0x1de06d(0x195)](_0x3e7b02);if(_0x25af5d>=-0x12321+0x417*-0xa+0x22407&&_0x25af5d<=0x92d4+0xb6fd+-0x6dd2&&_0x3e7b02<_0x19340f[_0x1de06d(0x259)]){var _0xfb40a0=_0x19340f['charCodeAt'](_0x3e7b02+(0x688*0x4+0x145*0x17+-0x3752));-0xa62a+-0x10ba7+-0x5d67*-0x7==(0xfbaf+0x1ce63+-0x1ce12&_0xfb40a0)&&(_0x25af5d=((-0x9fe*0x1+0x2*-0xf33+0x2c63&_0x25af5d)<<-0x86d*0x4+-0x211*0x9+-0x1*-0x3457)+(-0x1b17+0xb6c+0x13aa&_0xfb40a0)+(0x2*0xab9c+-0x18f24+-0x4*-0x4dfb),_0x3e7b02+=-0x1e31+-0xa*0x38b+-0x150*-0x32);}_0x57529f=(-0x3a*0x7e4+-0x3827*-0x2+0x151*0x1c9)*_0x57529f+_0x25af5d>>>0x3e7+-0x1*-0x5e7+-0x9ce;}return _0x57529f;}function _0x53f850(_0x450920){var _0x30ac3f=_0x5612de,_0x4eeea4=_0x450920||'';return _0x4eeea4=(_0x4eeea4=-(-0x13*-0x5a+0xb2f+-0x11dc)!==(_0x4eeea4=_0x4eeea4[_0x30ac3f(0x377)](/(http:\/\/|https:\/\/|\/\/)?[^\/]*/,''))['indexOf']('?')?_0x4eeea4[_0x30ac3f(0x3a7)](-0x819*0x3+0x1d1c+-0x9*0x89,_0x4eeea4[_0x30ac3f(0x2c5)]('?')):_0x4eeea4)||'/';}function _0x446110(_0xc80785){var _0x5bf664=_0x5612de,_0x471548=_0xc80785||'',_0x2cf363=_0x471548[_0x5bf664(0x1c2)](/[?](\w+=.*&?)*/),_0x137237=(_0x471548=_0x2cf363?_0x2cf363[-0x2292+-0x4*0x520+0x173*0x26][_0x5bf664(0x3a7)](0x112*0x11+0x61+-0x2*0x949):'')?_0x471548[_0x5bf664(0x342)]('&'):null,_0x290269={};if(_0x137237){for(var _0x15cd4c=0xd54+-0x49f+0x2e7*-0x3;_0x15cd4c<_0x137237[_0x5bf664(0x259)];_0x15cd4c++)_0x290269[_0x137237[_0x15cd4c]['split']('=')[0x65c+0x3f8*-0x2+0x194]]=_0x137237[_0x15cd4c][_0x5bf664(0x342)]('=')[0x15bb*-0x1+-0x50b*0x4+0x29e8];}return _0x290269;}function _0x3a1cf3(_0x8af04,_0xe4b509){var _0x28f81c=_0x5612de;if(!_0x8af04||'{}'===JSON[_0x28f81c(0x1e6)](_0x8af04))return{};for(var _0x257ee8=Object[_0x28f81c(0x17f)](_0x8af04)['sort'](),_0x24c84a={},_0x4bfbcd=-0x1*0x21fb+-0x25*-0x16+0x1ecd;_0x4bfbcd<_0x257ee8[_0x28f81c(0x259)];_0x4bfbcd++)_0x24c84a[_0x257ee8[_0x4bfbcd]]=_0xe4b509?_0x8af04[_0x257ee8[_0x4bfbcd]]+'':_0x8af04[_0x257ee8[_0x4bfbcd]];return _0x24c84a;}function _0x20b77a(_0x3745f1){var _0x211da5=_0x5612de;return Array[_0x211da5(0x2af)](_0x3745f1)?_0x3745f1['map'](_0x20b77a):_0x3745f1 instanceof Object?Object[_0x211da5(0x17f)](_0x3745f1)[_0x211da5(0x38e)]()[_0x211da5(0x376)](function(_0x4b45b3,_0x5188a8){return _0x4b45b3[_0x5188a8]=_0x20b77a(_0x3745f1[_0x5188a8]),_0x4b45b3;},{}):_0x3745f1;}function _0x483e03(_0x44c650){var _0x498394=_0x5612de;if(!_0x44c650||'{}'===JSON[_0x498394(0x1e6)](_0x44c650))return'';for(var _0x52341a=Object['keys'](_0x44c650)['sort'](),_0x3c151a='',_0x4f5c3f=-0xefc+0x25ba+0x29*-0x8e;_0x4f5c3f<_0x52341a[_0x498394(0x259)];_0x4f5c3f++)_0x3c151a+=[_0x52341a[_0x4f5c3f]]+'='+_0x44c650[_0x52341a[_0x4f5c3f]]+'&';return _0x3c151a;}function _0x4bae98(){var _0x33d77a=_0x5612de;try{return!!window[_0x33d77a(0x3be)];}catch(_0x2002b2){return!(0xcab+-0xcff+0x2*0x2a);}}function _0x275e5a(){try{return!!window['localStorage'];}catch(_0x2df8fe){return!(-0x1ff*-0xd+0x1cf1*-0x1+-0x17f*-0x2);}}function _0x4fdb47(){var _0x34b6b1=_0x5612de;try{return!!window[_0x34b6b1(0x218)];}catch(_0x3b6071){return!(0xedb*-0x1+0x41b*-0x5+-0x2*-0x11b1);}}function _0x1afbc2(){return _0x5af46a(_0x4fdb47())+_0x5af46a(_0x275e5a())+_0x5af46a(_0x4bae98());}function _0xc6f828(_0x23a724){var _0x198ea0=_0x5612de,_0x112670,_0x256bde=document[_0x198ea0(0x260)](_0x198ea0(0x24d));_0x256bde[_0x198ea0(0x302)]=0x1813*-0x1+0x1f94+0x751*-0x1,_0x256bde[_0x198ea0(0x245)]=-0x205f+0x4f6+-0x1b79*-0x1;var _0xa760ad=_0x256bde['getContext']('2d');_0xa760ad[_0x198ea0(0x18f)]='14px\x20serif',_0xa760ad[_0x198ea0(0x196)]('龘ฑภ경',0x2162*0x1+0xbbb+-0x2d1b,-0x1*-0x138a+-0x164e+0x90*0x5),_0xa760ad[_0x198ea0(0x3a0)]=-0xa67+0x7c*0x39+0x103*-0x11,_0xa760ad[_0x198ea0(0x268)]=0x22cf*0x1+-0x4d3+0x5*-0x5ff,_0xa760ad[_0x198ea0(0x340)]=_0x198ea0(0x21b),_0xa760ad['arc'](-0x1*-0x5dd+-0x6de+0x109,-0x336*0x4+-0x167a+0x712*0x5,-0x7c8+0xb80+-0x3b*0x10,0x411+0x38*-0xaa+0x3d*0x8b,0x7f*-0x20+0x11bb+-0x1d9),_0xa760ad['stroke'](),_0x112670=_0x256bde[_0x198ea0(0x239)]();for(var _0x367ff8=0xfa7*-0x1+-0x53*0x6d+0x32fe;_0x367ff8<0x17b+-0x1b0e*0x1+0x2b*0x99;_0x367ff8++)_0x23a724=(0x1675*-0xc+-0xe91a+0x2f6d5)*_0x23a724+_0x112670['charCodeAt'](_0x23a724%_0x112670[_0x198ea0(0x259)])>>>-0x44+0xfa0*-0x2+-0x1f84*-0x1;return _0x23a724;}var _0x37a93a=-0x1b3b+0x2136+-0x1*0x5fb;function _0x18b4be(){var _0x4d1378=_0x5612de;try{return _0x37a93a||(_0x462335[_0x4d1378(0x219)]?-(-0x1*0x19d5+-0x1e5d+0x3833):_0x37a93a=_0xc6f828(0x15*0x144d79ba+0x8831fd63+-0x153df3ab6));}catch(_0x3c9d0d){return-(0x1e58+-0x26*-0xcb+0x3c79*-0x1);}}function _0x1a39c4(){if(_0x37a93a)return _0x37a93a;_0x37a93a=_0xc6f828(-0x361cdc2a*-0x5+0x17c5*0xb369b+-0x2f7*0x6a0ca6);}var _0x45ece5={'fpProfileUrl':_0x5612de(0x172)};function _0x130155(){var _0x5e9262=_0x5612de,_0x380346=window[_0x5e9262(0x328)];return _0x380346[_0x5e9262(0x302)]+'_'+_0x380346[_0x5e9262(0x245)]+'_'+_0x380346[_0x5e9262(0x17e)];}function _0x2a76f8(){var _0x1d2b79=_0x5612de,_0xa8cb6a=window['screen'];return _0xa8cb6a[_0x1d2b79(0x38a)]+'_'+_0xa8cb6a[_0x1d2b79(0x276)];}function _0x3da279(){return new Promise(function(_0x2a10ed){var _0x7d3f27=w_0x25f3;if(_0x7d3f27(0x1b2)in navigator)try{navigator['getBattery']()[_0x7d3f27(0x1ed)](function(_0x13ff1d){var _0x479b99=_0x7d3f27;_0x2a10ed(_0x13ff1d[_0x479b99(0x3ac)]+'_'+_0x13ff1d[_0x479b99(0x2b0)]+'_'+_0x13ff1d[_0x479b99(0x353)]+'_'+_0x13ff1d[_0x479b99(0x213)]);});}catch(_0x2c28b8){_0x2a10ed('');}else _0x2a10ed('');});}var _0x47ec2a={};function _0x299f3a(){var _0x2531b6=_0x5612de,_0x1a8a34,_0x2e0843=_0x2531b6(0x33b),_0x29ea20=-0x12a4+0x30b+0xf99;void(-0x2b*0xa+-0x1d8d+0x1f3b)!==navigator[_0x2e0843]&&(_0x29ea20=navigator[_0x2e0843]);try{document[_0x2531b6(0x188)](_0x2531b6(0x240)),_0x1a8a34=!(-0x1f61+0x19*0x22+0x1c0f);}catch(_0x3992e0){_0x1a8a34=!(0x17*-0x5d+-0x1*0x209+0x377*0x3);}var _0x11190a=_0x2531b6(0x1ea)in window;return Object[_0x2531b6(0x2a0)](_0x47ec2a,{'maxTouchPoints':_0x29ea20,'touchEvent':_0x1a8a34,'touchStart':_0x11190a}),_0x29ea20+'_'+_0x1a8a34+'_'+_0x11190a;}function _0xbe842b(){return _0x47ec2a;}function _0x4649a1(){var _0x3b6f20=_0x5612de,_0x4256a7=new Date();_0x4256a7[_0x3b6f20(0x22d)](-0x1477+0xde4*-0x1+0xb74*0x3),_0x4256a7[_0x3b6f20(0x294)](-0x360+-0x263d+0x29a2);var _0x213a03=-_0x4256a7[_0x3b6f20(0x237)]();_0x4256a7[_0x3b6f20(0x294)](-0x9b5+0x5*0x77e+-0x2*0xddb);var _0x5a2621=-_0x4256a7[_0x3b6f20(0x237)]();return Math['min'](_0x213a03,_0x5a2621);}function _0x487576(){var _0x5557a1=_0x5612de;if(_0x6caf[_0x5557a1(0x2db)])return _0x6caf[_0x5557a1(0x2db)];try{var _0x4f28ec=document[_0x5557a1(0x260)](_0x5557a1(0x24d))[_0x5557a1(0x2f9)]('webgl'),_0x36473d=_0x4f28ec['getExtension'](_0x5557a1(0x375)),_0x3ebb8d=_0x4f28ec[_0x5557a1(0x383)](_0x36473d['UNMASKED_VENDOR_WEBGL'])+'/'+_0x4f28ec['getParameter'](_0x36473d[_0x5557a1(0x39e)]);return _0x6caf[_0x5557a1(0x2db)]=_0x3ebb8d,_0x3ebb8d;}catch(_0x2b9fa5){return'';}}function _0x4f323e(){var _0x2968a5=_0x5612de,_0xf55c22=[_0x2968a5(0x27b),'sans-serif',_0x2968a5(0x1fb)],_0x3ed5f8={},_0x2fef11={};if(!document[_0x2968a5(0x189)])return'0';for(var _0x6f70bc=-0x2271+0x2f*0x72+0xd83,_0x3e66d7=_0xf55c22;_0x6f70bc<_0x3e66d7['length'];_0x6f70bc++){var _0x189f91=_0x3e66d7[_0x6f70bc],_0x2bf145=document[_0x2968a5(0x260)](_0x2968a5(0x1f7));_0x2bf145['innerHTML']=_0x2968a5(0x2a3),_0x2bf145['style'][_0x2968a5(0x362)]='72px',_0x2bf145['style']['fontFamily']=_0x189f91,document[_0x2968a5(0x189)][_0x2968a5(0x26c)](_0x2bf145),_0x3ed5f8[_0x189f91]=_0x2bf145[_0x2968a5(0x2ee)],_0x2fef11[_0x189f91]=_0x2bf145['offsetHeight'],document[_0x2968a5(0x189)][_0x2968a5(0x30d)](_0x2bf145);}var _0x4f59cf,_0x4abd0f=['Trebuchet\x20MS',_0x2968a5(0x177),_0x2968a5(0x25d),'Segoe\x20UI',_0x2968a5(0x321),_0x2968a5(0x192),'MT\x20Extra',_0x2968a5(0x3a9),_0x2968a5(0x28e),_0x2968a5(0x346),'Meiryo',_0x2968a5(0x247),_0x2968a5(0x1ef),_0x2968a5(0x1d8),'IrisUPC','Palatino','Colonna\x20MT','Playbill',_0x2968a5(0x1f4),_0x2968a5(0x288),_0x2968a5(0x1f2),_0x2968a5(0x38d),'OPTIMA',_0x2968a5(0x2d9),_0x2968a5(0x348),_0x2968a5(0x2b3),'Savoye\x20LET',_0x2968a5(0x3c5),_0x2968a5(0x21e)];_0x4f59cf=-0x255b*-0x1+-0x1*-0x139d+-0x38f8;for(var _0x16c300=-0x1*0xc3e+-0x20ff+0x2d3d;_0x16c300<_0x4abd0f[_0x2968a5(0x259)];_0x16c300++){var _0x2c3be4,_0x37b937=_0x350075(_0xf55c22);try{for(_0x37b937['s']();!(_0x2c3be4=_0x37b937['n']())[_0x2968a5(0x1e3)];){var _0x400340=_0x2c3be4['value'],_0x200821=document[_0x2968a5(0x260)]('span');_0x200821[_0x2968a5(0x36a)]=_0x2968a5(0x2a3),_0x200821['style'][_0x2968a5(0x362)]=_0x2968a5(0x283),_0x200821[_0x2968a5(0x280)]['fontFamily']=_0x4abd0f[_0x16c300]+','+_0x400340,document['body'][_0x2968a5(0x26c)](_0x200821);var _0x533f71=_0x200821[_0x2968a5(0x2ee)]!==_0x3ed5f8[_0x400340]||_0x200821[_0x2968a5(0x2a4)]!==_0x2fef11[_0x400340];if(document[_0x2968a5(0x189)][_0x2968a5(0x30d)](_0x200821),_0x533f71){_0x16c300<-0x45*-0x8a+-0x7a9+-0x1d6b&&(_0x4f59cf|=-0x239+0x3*-0x458+0xf42<<_0x16c300);break;}}}catch(_0x5c1c6e){_0x37b937['e'](_0x5c1c6e);}finally{_0x37b937['f']();}}return _0x4f59cf[_0x2968a5(0x3ae)](-0xe97+-0x183c+-0x1*-0x26e3);}function _0x5090f5(){var _0x458c87=_0x5612de;try{new WebSocket(_0x458c87(0x199));}catch(_0x34e866){return _0x34e866[_0x458c87(0x312)];}}function _0x468d57(){var _0x2ef0c5=_0x5612de;return eval[_0x2ef0c5(0x3ae)]()[_0x2ef0c5(0x259)];}function _0x5bbaf0(){var _0x5ec561=_0x5612de,_0x5971d6=window['RTCPeerConnection']||window[_0x5ec561(0x2e0)]||window[_0x5ec561(0x19a)],_0x5aac87=[];return new Promise(function(_0x4e6ec1){var _0x5af634=_0x5ec561;(_0x176a57()||navigator[_0x5af634(0x229)][_0x5af634(0x202)]()[_0x5af634(0x2c5)](_0x5af634(0x20e))>-0x707+-0x106*0x10+-0x1*-0x1767)&&_0x4e6ec1('');try{if(_0x5971d6&&'function'==typeof _0x5971d6){var _0xadabb6=new _0x5971d6({'iceServers':[{'urls':_0x5af634(0x27c)}]}),_0x4405e6=function(){},_0x4de1d1=/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/;_0xadabb6[_0x5af634(0x1eb)]=function(){var _0x374eb0=_0x5af634;_0x374eb0(0x162)===_0xadabb6['iceGatheringState']&&(_0xadabb6[_0x374eb0(0x27a)](),_0xadabb6=null);},_0xadabb6['onicecandidate']=function(_0x4e2717){var _0x18e62e=_0x5af634;if(_0x4e2717&&_0x4e2717[_0x18e62e(0x227)]&&_0x4e2717[_0x18e62e(0x227)][_0x18e62e(0x227)]){if(''===_0x4e2717['candidate'][_0x18e62e(0x227)])return;var _0x1d1cd1=_0x4de1d1['exec'](_0x4e2717[_0x18e62e(0x227)][_0x18e62e(0x227)]);if(null!==_0x1d1cd1&&_0x1d1cd1[_0x18e62e(0x259)]>-0x1675*0x1+-0x19*0xdd+0x19*0x1c3){var _0xbaa956=_0x1d1cd1[-0x1bdd*0x1+0x13*-0x1df+-0x11*-0x3bb];-(0x1*-0x12c1+-0xa*0x38c+-0x4ee*-0xb)===_0x5aac87[_0x18e62e(0x2c5)](_0xbaa956)&&_0x5aac87[_0x18e62e(0x36e)](_0xbaa956);}}else _0x4e6ec1(_0x5aac87[_0x18e62e(0x24f)]());},_0xadabb6[_0x5af634(0x1e5)](''),setTimeout(function(){var _0x36b72e=_0x5af634;_0x4e6ec1(_0x5aac87[_0x36b72e(0x24f)]());},0x2205*0x1+0x1d4*-0x10+-0x67*0x7);var _0x38a358=_0xadabb6[_0x5af634(0x2f5)]();_0x38a358 instanceof Promise?_0x38a358[_0x5af634(0x1ed)](function(_0x33e26c){return _0xadabb6['setLocalDescription'](_0x33e26c);})[_0x5af634(0x1ed)](_0x4405e6):_0xadabb6['createOffer'](function(_0x1c6e51){_0xadabb6['setLocalDescription'](_0x1c6e51,_0x4405e6,_0x4405e6);},_0x4405e6);}else _0x4e6ec1('');}catch(_0x31debb){_0x4e6ec1('');}});}function _0x2e02ca(){var _0x74cdcb=_0x5612de;return _0x74cdcb(0x3af)[_0x74cdcb(0x377)](/[xy]/g,function(_0x5790dd){var _0x52f0b5=_0x74cdcb,_0x2eeaf3=(0x506+-0x85c+-0x2*-0x1b3)*Math[_0x52f0b5(0x18e)]()|0xbb9*-0x1+0x1*0x9dd+0x1dc;return('x'==_0x5790dd?_0x2eeaf3:-0xd1f*0x1+0x1df6+-0x1*0x10d4&_0x2eeaf3|0x182d+-0x10*0x262+-0xdfb*-0x1)[_0x52f0b5(0x3ae)](0x1a65+0x3*0x17a+0x13b*-0x19);});}function _0x2e2fa0(_0x1a6938){var _0x5aaa98=_0x5612de;return-0x14*0x1df+0x649+0x1f45===_0x1a6938[_0x5aaa98(0x259)]&&_0x246aeb(-0x21ad+0x819*-0x1+0x29c6,_0x1a6938[_0x5aaa98(0x1b0)](-0x1a55*0x1+0x1279+0x7dc,0x31b+-0xcb4*-0x1+-0xfaf))[_0x5aaa98(0x3ae)]()['substring'](0x17cc+0x1*-0xd19+-0x3*0x391,-0x1ff*0x1+0x2579+-0x1c6*0x14)===_0x1a6938[_0x5aaa98(0x1b0)](0x1746+0x7b8+-0x1ede,-0x369*-0x2+-0x5e5*0x4+0xbc*0x17);}function _0x178d7c(){var _0x4d5814=_0x5612de,_0x2d4341=_0x24dc34(_0x4d5814(0x262));return _0x2d4341&&_0x2e2fa0(_0x2d4341)||_0x1f42cb(_0x4d5814(0x262),_0x2d4341=((_0x2d4341=_0x2e02ca())+_0x246aeb(0x17*0x12d+-0x6*0x3f7+-0x341,_0x2d4341))[_0x4d5814(0x1b0)](0x2d1*-0xd+0x3a*-0x7c+0x40b5,0x188e+0x13*-0x127+-0x287)),_0x2d4341;}function _0x3c0a68(_0x449fe8,_0x5ede0c){var _0x40bd5b=_0x5612de,_0x401085=null;try{_0x401085=document['getElementsByTagName'](_0x40bd5b(0x221))[-0x2581+0x844+-0x1d3d*-0x1];}catch(_0x599ca4){_0x401085=document['body'];}if(null!==_0x401085){var _0x3a3665=document[_0x40bd5b(0x260)](_0x40bd5b(0x361)),_0x215422='_'+parseInt((0x3df0+0x13d+-0x181d)*Math['random'](),0x1*-0x2141+-0x1*-0xf82+0x11c9)+'_'+new Date()[_0x40bd5b(0x16d)]();_0x449fe8+=_0x40bd5b(0x1be)+_0x215422,_0x3a3665[_0x40bd5b(0x16b)]=_0x449fe8,window[_0x215422]=function(_0x1efd79){var _0x4afb72=_0x40bd5b;try{_0x5ede0c(_0x1efd79),_0x401085[_0x4afb72(0x30d)](_0x3a3665),delete window[_0x215422];}catch(_0xf98f95){}},_0x401085[_0x40bd5b(0x26c)](_0x3a3665);}}function _0x4072ad(_0x18cd85){return w_0x5c3140('484e4f4a403f524300022c395eafc0a4000000004a04440d00000030110104324700040700004202110100030443011400011100010211010102110102110104110001430207000143021842000200404f4c4d4a4b484946474445424340415e5f5c5d5a5b58595657546f6c6d6a6b686966676465626360617e7f7c7d7a7b78797677743e3f3c3d3a3b383936372320',{get 0x0(){return _0x325f58;},get 0x1(){return _0x328bde;},get 0x2(){return _0xdda738;},0x3:arguments,0x4:_0x18cd85},this);}function _0x5c0cdd(_0x380d2b,_0x1c644a){var _0x349270=_0x5612de;if(_0x1c644a){for(var _0x25fd23=-0xbdc+-0xd78*-0x2+-0xf14,_0x2c655d=-0x1*-0xbdb+0xc2+-0xc9d;_0x2c655d<_0x380d2b[_0x349270(0x259)];_0x2c655d++)_0x380d2b[_0x2c655d]['p']&&(_0x380d2b[_0x2c655d]['r']=_0x1c644a[_0x25fd23++]);}var _0x20da32='';_0x380d2b[_0x349270(0x254)](function(_0x564ece){_0x20da32+=_0x5af46a(_0x564ece['r'])+'^^';}),_0x20da32+=_0x30c916();var _0x2803a3=_0x2e02ca(),_0x273650=Math[_0x349270(0x1cf)](_0x2803a3[_0x349270(0x195)](0x127a+0x22e0+-0x3557*0x1)/(0x1*0x199f+-0xab4+-0x1*0xee3))+_0x2803a3[_0x349270(0x195)](-0x15*0xca+-0x1ff6*-0x1+0x1*-0xf61)%(-0x1085+-0x553+0x15e0),_0x102025=_0x2803a3['substring'](0x1*-0x1ebf+0x13a1+0x1e*0x5f,0x5c7+-0x5a5+-0x5*0x6+_0x273650);_0x20da32=_0x328bde(_0xdda738(_0x20da32,_0x102025)+_0x2803a3);var _0x197e34=_0x45ece5['fpProfileUrl'];_0x3c0a68(_0x197e34+=_0x349270(0x173)+encodeURIComponent(_0x20da32)+'&',function(_0x5b4c95){var _0x3c0c48=_0x349270;0x1db6+-0x21d*0x1+-0x1b99==_0x5b4c95[_0x3c0c48(0x356)]&&_0x5b4c95['fp']&&(_0x462335[_0x3c0c48(0x35f)]=_0x5b4c95['fp'],_0x462335[_0x3c0c48(0x363)]=_0x4072ad(_0x5b4c95['fp']),_0x1f42cb('tt_scid',_0x5b4c95['fp']));});}function _0x1c3b6d(_0x20f8c9){var _0x596053=_0x5612de;return w_0x5c3140(_0x596053(0x242),{get 0x0(){return navigator;},get 0x1(){return window;},get 0x2(){return document;},get 0x3(){return _0x30c916;},get 0x4(){return _0x1afbc2;},get 0x5(){return _0x18b4be;},get 0x6(){return _0x130155;},get 0x7(){return _0x2a76f8;},get 0x8(){return _0x3da279;},get 0x9(){return _0x299f3a;},get 0xa(){return _0x4649a1;},get 0xb(){return _0x487576;},get 0xc(){return _0x4f323e;},get 0xd(){return _0x1f9824;},get 0xe(){return _0x24dc34;},get 0xf(){return _0x5090f5;},get 0x10(){return _0x468d57;},get 0x11(){return _0x5bbaf0;},get 0x12(){return _0x45b94b;},get 0x13(){return _0x178d7c;},get 0x14(){return _0x5af46a;},0x15:Promise,get 0x16(){return _0x5c0cdd;},0x17:arguments,0x18:_0x20f8c9},this);}function _0x20cbf3(_0x38a8fe,_0x406d4b,_0x2e7a9b){var _0x1b116c=_0x5612de;return w_0x5c3140(_0x1b116c(0x233),{0x0:String,0x1:Date,get 0x2(){return _0x45b94b;},get 0x3(){return _0x184783;},get 0x4(){return location;},0x5:parseInt,get 0x6(){return _0x5863d1;},0x7:JSON,get 0x8(){return _0xf119da;},get 0x9(){return _0x3a1cf3;},get 0xa(){return _0x20b77a;},get 0xb(){return _0x446110;},0xc:Object,get 0xd(){return _0x483e03;},get 0xe(){return _0x53f850;},get 0xf(){return _0x2a900b;},get 0x10(){return _0x18b4be;},get 0x11(){return _0x462335;},get 0x12(){return _0x4072ad;},get 0x13(){return _0x24dc34;},get 0x14(){return navigator;},0x15:arguments,0x16:_0x38a8fe,0x17:_0x406d4b,0x18:_0x2e7a9b},this);}function _0x5e5a64(_0x27ec41,_0x4e1246){var _0x15a192=_0x5612de;for(var _0x3f84da={},_0x389521=-0x4bd*-0x1+-0x28a+-0x233;_0x389521<_0x4e1246['length'];_0x389521++){var _0x58af98=_0x4e1246[_0x389521],_0x1b7f4e=_0x27ec41[_0x58af98];null==_0x1b7f4e&&(_0x1b7f4e=!(-0x1f76+-0x1c06+0x3b7d*0x1)),null===_0x1b7f4e||'function'!=typeof _0x1b7f4e&&_0x15a192(0x17d)!==_0x1db123(_0x1b7f4e)||(_0x1b7f4e=!(-0x2*-0xef9+-0xa1*0x3+-0xb*0x28d)),_0x3f84da[_0x58af98]=_0x1b7f4e;}return _0x3f84da;}function _0x2a6ac2(){var _0x42b9bc=_0x5612de;return _0x5e5a64(navigator,[_0x42b9bc(0x36b),'appName',_0x42b9bc(0x17a),_0x42b9bc(0x246),_0x42b9bc(0x3a4),_0x42b9bc(0x360),_0x42b9bc(0x1fa),_0x42b9bc(0x33b),_0x42b9bc(0x38c),_0x42b9bc(0x212),'vendorSub','doNotTrack',_0x42b9bc(0x24a),_0x42b9bc(0x2e2),_0x42b9bc(0x292),_0x42b9bc(0x390),_0x42b9bc(0x38f)]);}function _0x226933(){var _0xe67fb0=_0x5612de;return _0x5e5a64(window,[_0xe67fb0(0x29d),_0xe67fb0(0x222),_0xe67fb0(0x306),_0xe67fb0(0x26e),_0xe67fb0(0x366),'isSecureContext',_0xe67fb0(0x3c0),_0xe67fb0(0x399),'locationbar',_0xe67fb0(0x29f),_0xe67fb0(0x39f),_0xe67fb0(0x2e0),'postMessage','webkitRequestAnimationFrame',_0xe67fb0(0x2de),'netscape']);}function _0x1e5a7f(){var _0x577bf2=_0x5612de;return _0x5e5a64(document,[_0x577bf2(0x335),_0x577bf2(0x253),_0x577bf2(0x373),'layers',_0x577bf2(0x2c0)]);}function _0x11a1d6(){var _0x3927af=_0x5612de,_0x306255=document[_0x3927af(0x260)](_0x3927af(0x24d)),_0x3b8708=null;try{_0x3b8708=_0x306255[_0x3927af(0x2f9)](_0x3927af(0x26b))||_0x306255['getContext'](_0x3927af(0x3aa));}catch(_0x30a5e5){}return _0x3b8708||(_0x3b8708=null),_0x3b8708;}function _0x39c3d8(_0xc6dcf6){var _0x59367a=_0x5612de,_0x5a3a25=_0xc6dcf6[_0x59367a(0x1d7)](_0x59367a(0x1f1))||_0xc6dcf6[_0x59367a(0x1d7)](_0x59367a(0x2e5))||_0xc6dcf6[_0x59367a(0x1d7)](_0x59367a(0x31a));if(_0x5a3a25){var _0x5cca98=_0xc6dcf6[_0x59367a(0x383)](_0x5a3a25[_0x59367a(0x1bf)]);return 0xb*-0x2b8+0xa*0xe+0x1d5c===_0x5cca98&&(_0x5cca98=-0xe6+-0x1909+-0xe5*-0x1d),_0x5cca98;}return null;}function _0x425568(){var _0x556a1e=_0x5612de;if(_0x6caf[_0x556a1e(0x23d)])return _0x6caf[_0x556a1e(0x23d)];var _0x4f4b6e=_0x11a1d6();if(!_0x4f4b6e)return{};var _0x58c017={'supportedExtensions':_0x4f4b6e[_0x556a1e(0x30f)]()||[],'antialias':_0x4f4b6e[_0x556a1e(0x1c5)]()[_0x556a1e(0x1a5)],'blueBits':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x27e)]),'depthBits':_0x4f4b6e['getParameter'](_0x4f4b6e[_0x556a1e(0x252)]),'greenBits':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x3c3)]),'maxAnisotropy':_0x39c3d8(_0x4f4b6e),'maxCombinedTextureImageUnits':_0x4f4b6e['getParameter'](_0x4f4b6e[_0x556a1e(0x1d2)]),'maxCubeMapTextureSize':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x300)]),'maxFragmentUniformVectors':_0x4f4b6e['getParameter'](_0x4f4b6e['MAX_FRAGMENT_UNIFORM_VECTORS']),'maxRenderbufferSize':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x2da)]),'maxTextureImageUnits':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x269)]),'maxTextureSize':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x203)]),'maxVaryingVectors':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x264)]),'maxVertexAttribs':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e['MAX_VERTEX_ATTRIBS']),'maxVertexTextureImageUnits':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x205)]),'maxVertexUniformVectors':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e[_0x556a1e(0x19d)]),'shadingLanguageVersion':_0x4f4b6e['getParameter'](_0x4f4b6e['SHADING_LANGUAGE_VERSION']),'stencilBits':_0x4f4b6e['getParameter'](_0x4f4b6e['STENCIL_BITS']),'version':_0x4f4b6e[_0x556a1e(0x383)](_0x4f4b6e['VERSION'])};return _0x6caf[_0x556a1e(0x23d)]=_0x58c017,_0x58c017;}function _0x18707d(){var _0x52b59d=_0x5612de,_0x56cb86={};return _0x56cb86[_0x52b59d(0x270)]=_0x2a6ac2(),_0x56cb86[_0x52b59d(0x393)]=_0x226933(),_0x56cb86[_0x52b59d(0x208)]=_0x1e5a7f(),_0x56cb86[_0x52b59d(0x26b)]=_0x425568(),_0x56cb86[_0x52b59d(0x381)]=_0x487576(),_0x56cb86['plugins']=_0x1f9824(),_0x6caf['SECINFO']=_0x56cb86,_0x56cb86;}function _0x572e48(){var _0x151b17=_0x5612de;return w_0x5c3140(_0x151b17(0x1d1),{get 0x0(){return _0x6caf;},get 0x1(){return _0x18707d;},0x2:Date,get 0x3(){return _0x325f58;},get 0x4(){return _0x328bde;},get 0x5(){return _0xdda738;},0x6:JSON,0x7:arguments},this);}var _0x522d20={'kCallTypeDirect':0x0,'kCallTypeInterceptor':0x1},_0x2e10da={'kHttp':0x0,'kWebsocket':0x1},_0x3f742e=_0x45b94b;function _0x5646fa(_0x29e841){var _0x2ecc2b=_0x5612de;for(var _0x5d379d,_0x5e2317,_0x34abf7=[],_0x448a08=-0x25ac+0x1*-0x1a4+-0x128*-0x22;_0x448a08<_0x29e841[_0x2ecc2b(0x259)];_0x448a08++){_0x5d379d=_0x29e841[_0x2ecc2b(0x195)](_0x448a08),_0x5e2317=[];do{_0x5e2317[_0x2ecc2b(0x36e)](-0x8d*-0x3e+0x24d*-0x1+-0x1eda&_0x5d379d),_0x5d379d>>=-0x6d*0x11+0x75*0x11+0x10*-0x8;}while(_0x5d379d);_0x34abf7=_0x34abf7[_0x2ecc2b(0x2b8)](_0x5e2317['reverse']());}return _0x34abf7;}function _0x6bc4ae(_0x4e8b7c){}function _0x5b890d(_0x592717){}function _0x16f345(_0x1a40af){}function _0x361214(_0x426175){}function _0xc35122(_0xe69c60,_0x3e14a1,_0x2d525a){}var _0x5dde58={'WEB_DEVICE_INFO':0x8};function _0x4df596(_0x14c951,_0x4c06b0){var _0x3cd5a5=_0x5612de;return JSON[_0x3cd5a5(0x1e6)]({'magic':0x20200422,'version':0x1,'dataType':_0x14c951,'strData':_0x4c06b0,'tspFromClient':new Date()[_0x3cd5a5(0x16d)]()});}function _0x29a5ac(_0x1a16de,_0x1af868,_0x2e45c3,_0x2a9bcc){var _0x233cbb=_0x5612de;return _0x32af0e(_0x233cbb(0x3a1),_0x1a16de,_0x1af868,_0x2e45c3,_0x2a9bcc);}function _0x32af0e(_0x58e2fa,_0x35c6ef,_0x5ce009,_0xba3041,_0x20e80f){var _0x393ad1=_0x5612de,_0x4841fc=new XMLHttpRequest();if(_0x4841fc[_0x393ad1(0x18b)](_0x58e2fa,_0x35c6ef,!(-0xb*0x8b+-0x233+0x82c)),_0x20e80f&&(_0x4841fc[_0x393ad1(0x1c6)]=!(0x1*-0x260f+-0x2*0xcdf+0x3fcd)),_0xba3041)for(var _0x3f91e1=-0x2ce+-0x1*0xe0f+0x10dd,_0x2f8bec=Object['keys'](_0xba3041);_0x3f91e1<_0x2f8bec[_0x393ad1(0x259)];_0x3f91e1++){var _0x475f86=_0x2f8bec[_0x3f91e1],_0x42c2b4=_0xba3041[_0x475f86];_0x4841fc[_0x393ad1(0x3ab)](_0x475f86,_0x42c2b4);}_0x4841fc[_0x393ad1(0x20a)](_0x5ce009);}function _0x2cd488(_0x751be6,_0x4c12d4){var _0x253a32=_0x5612de;return _0x4c12d4||(_0x4c12d4=null),!!navigator['sendBeacon']&&(navigator[_0x253a32(0x24b)](_0x751be6,_0x4c12d4),!(-0x1da1+0x1e93+-0xf2));}function _0x4a2daf(_0x1ea426,_0x5a460b){var _0x22c57e=_0x5612de;try{window[_0x22c57e(0x1dc)]&&window[_0x22c57e(0x1dc)][_0x22c57e(0x1c4)](_0x1ea426,_0x5a460b);}catch(_0xe6e06d){}}function _0x34f60a(_0x1e7505){var _0x389acb=_0x5612de;try{window[_0x389acb(0x1dc)]&&window[_0x389acb(0x1dc)][_0x389acb(0x2c3)](_0x1e7505);}catch(_0x5daf1b){}}function _0x3d13cf(_0x3480fd){var _0x1ccc66=_0x5612de;try{return window[_0x1ccc66(0x1dc)]?window['localStorage']['getItem'](_0x3480fd):null;}catch(_0x3bb8f0){return null;}}function _0x21db29(_0x584f42,_0xde0f43){var _0x4464a1=_0x5612de;for(var _0x2b8d40,_0x22bd9c=[],_0x13f024=0x1856*0x1+-0x6a*0x20+-0x16*0x81,_0x5648fb='',_0x4c41a3=-0x2*-0xc67+-0xf91+-0x5*0x1d9;_0x4c41a3<-0x1*-0x322+-0x25ce+0x23ac;_0x4c41a3++)_0x22bd9c[_0x4c41a3]=_0x4c41a3;for(var _0x4d4c13=-0x18e4+-0x3*-0x10f+0x15b7;_0x4d4c13<-0x36a*-0x8+0x1cf7+0x126d*-0x3;_0x4d4c13++)_0x13f024=(_0x13f024+_0x22bd9c[_0x4d4c13]+_0x584f42[_0x4464a1(0x195)](_0x4d4c13%_0x584f42[_0x4464a1(0x259)]))%(-0x577*0x3+0x4eb+0xc7a),_0x2b8d40=_0x22bd9c[_0x4d4c13],_0x22bd9c[_0x4d4c13]=_0x22bd9c[_0x13f024],_0x22bd9c[_0x13f024]=_0x2b8d40;var _0x11b95d=-0x7d8+-0x256+-0x517*-0x2;_0x13f024=-0x1bef+-0xbc5*-0x3+-0x760*0x1;for(var _0x15a79f=0xd*0x12e+0x80*-0x3d+0xf2a;_0x15a79f<_0xde0f43[_0x4464a1(0x259)];_0x15a79f++)_0x13f024=(_0x13f024+_0x22bd9c[_0x11b95d=(_0x11b95d+(0x8ed+-0x1*-0x836+-0x22*0x81))%(0x32b*0x7+0x2311+-0x2*0x1c1f)])%(-0x1*-0x106d+0x35e*0x5+-0x2043),_0x2b8d40=_0x22bd9c[_0x11b95d],_0x22bd9c[_0x11b95d]=_0x22bd9c[_0x13f024],_0x22bd9c[_0x13f024]=_0x2b8d40,_0x5648fb+=String[_0x4464a1(0x1e2)](_0xde0f43[_0x4464a1(0x195)](_0x15a79f)^_0x22bd9c[(_0x22bd9c[_0x11b95d]+_0x22bd9c[_0x13f024])%(0x179*-0x7+0x2385+-0x2*0xc1b)]);return _0x5648fb;}var _0x45bf15=_0x21db29,_0x48a082={};function _0x641e3d(_0x21dbac,_0x4a67ec){var _0x51016d=_0x5612de;return w_0x5c3140(_0x51016d(0x3c4),{0x0:String,0x1:Math,get 0x2(){return _0x45bf15;},get 0x3(){return _0x389396;},0x4:arguments,0x5:_0x21dbac,0x6:_0x4a67ec},this);}_0x48a082['pb']=-0xd0f*-0x1+0xade*0x2+-0x22c9,_0x48a082['json']=0x405+-0x1*-0xe59+-0x125d;var _0x216650={'kNoMove':0x2,'kNoClickTouch':0x4,'kNoKeyboardEvent':0x8,'kMoveFast':0x10,'kKeyboardFast':0x20,'kFakeOperations':0x40},_0x5dc9cc={'sTm':0x0,'acc':0x0};function _0x18a9f7(){var _0x4e02b7=_0x5612de;try{var _0x41737c=_0x3d13cf(_0x4e02b7(0x343));_0x41737c?Object[_0x4e02b7(0x2a0)](_0x5dc9cc,JSON[_0x4e02b7(0x3b1)](_0x41737c)):(_0x5dc9cc[_0x4e02b7(0x293)]=new Date()[_0x4e02b7(0x16d)](),_0x5dc9cc[_0x4e02b7(0x27d)]=-0x175c+-0x1066+0x27c2);}catch(_0x3da833){_0x5dc9cc[_0x4e02b7(0x293)]=new Date()[_0x4e02b7(0x16d)](),_0x5dc9cc[_0x4e02b7(0x27d)]=-0x1*-0x1129+-0x1f1f+-0x1*-0xdf6,_0x26e186();}}function _0x26e186(){var _0x48e1a8=_0x5612de;_0x4a2daf(_0x48e1a8(0x343),JSON['stringify'](_0x5dc9cc));}var _0xf63a81={'T_MOVE':0x1,'T_CLICK':0x2,'T_KEYBOARD':0x3},_0x2786f5=!(0x1f31*-0x1+0x1*0x22c7+0x83*-0x7),_0x9fb121=[],_0x207cc5=[],_0x191fa5=[],_0xe06992={'ubcode':0x0},_0x388856=function(_0x4e0342,_0x5ca571){return _0x4e0342+_0x5ca571;},_0x20a3d9=function(_0x2309ca){return _0x2309ca*_0x2309ca;};function _0x9c0be2(_0x232ed8,_0x432cf8){var _0x59688f=_0x5612de;if(_0x232ed8[_0x59688f(0x259)]>0x1*0xa93+0x1d7a+-0xd17*0x3&&_0x232ed8['splice'](0x1ab7+0x1*-0x2147+-0x1a4*-0x4,0x617+0x280+0x1*-0x833),_0x232ed8[_0x59688f(0x259)]>-0x1c57+0x1059+0xbfe){var _0x97b7e7=_0x232ed8[_0x232ed8['length']-(-0xc0e+0xb26*0x2+0xa3d*-0x1)];if(_0x432cf8['d']-_0x97b7e7['d']<=0x5*-0x1a3+-0x91*-0x32+0x5*-0x407||'y'in _0x432cf8&&_0x432cf8['x']===_0x97b7e7['x']&&_0x432cf8['y']===_0x97b7e7['y'])return;}_0x232ed8[_0x59688f(0x36e)](_0x432cf8);}function _0x1d26db(_0x261f4b,_0x3c3c83,_0x131716){var _0x10f2ca=_0x5612de;if(_0x462335['enableTrack']){if(_0x131716!==_0xf63a81['T_MOVE'])return _0x131716===_0xf63a81[_0x10f2ca(0x1f0)]?(_0x261f4b[_0x10f2ca(0x259)]>=0x1167+-0x856*-0x4+0x1*-0x30cb&&_0x450b73(),void _0x261f4b[_0x10f2ca(0x36e)](_0x3c3c83)):_0x131716===_0xf63a81[_0x10f2ca(0x355)]?(_0x261f4b[_0x10f2ca(0x259)]>-0x1b6*0x16+-0x115*0x17+0x11*0x3cb&&_0x450b73(),void _0x261f4b['push'](_0x3c3c83)):void(-0x15a4+-0xc9f+0x2243);if(_0x261f4b['length']>=-0x5b9+-0x1b5+-0x2*-0x4b1&&_0x450b73(),_0x261f4b[_0x10f2ca(0x259)]>0x13e*0x6+0x4a5*-0x2+0x1d6){var _0x40dc65=_0x261f4b[_0x261f4b[_0x10f2ca(0x259)]-(-0x233e+0x6bf+0x1c80)],_0x243563=_0x40dc65['x'],_0x7ef629=_0x40dc65['y'],_0x293666=_0x40dc65['ts'];if(_0x243563===_0x3c3c83['x']&&_0x7ef629===_0x3c3c83['y'])return;if(_0x3c3c83['ts']-_0x293666<-0x1*-0x9dc+-0x7*-0x32d+-0x1e23)return;}_0x261f4b['push'](_0x3c3c83);}}var _0x19ffaf={'init':0x0,'running':0x1,'exit':0x2,'flush':0x3};function _0x450b73(_0xd6648d){var _0x5b1d4d=_0x5612de;return w_0x5c3140(_0x5b1d4d(0x3a2),{get 0x0(){return _0x6caf;},get 0x1(){return _0x19ffaf;},0x2:Date,get 0x3(){return _0x5dc9cc;},get 0x4(){return _0x462335;},get 0x5(){return _0x26e186;},0x6:Object,get 0x7(){return _0x4df596;},get 0x8(){return _0x5dde58;},get 0x9(){return _0x641e3d;},0xa:JSON,get 0xb(){return _0x48a082;},get 0xc(){return _0x2cd488;},get 0xd(){return _0x29a5ac;},0xe:arguments,0xf:_0xd6648d},this);}function _0x58c311(){var _0x30dab3=_0x5612de;_0x462335[_0x30dab3(0x37f)]&&_0x450b73(_0x19ffaf['exit']);}var _0x1a755e={};_0x1a755e[_0x5612de(0x2d4)]=_0x3dcfd5,_0x1a755e['touchmove']=_0x3dcfd5,_0x1a755e[_0x5612de(0x364)]=_0x56114b,_0x1a755e[_0x5612de(0x1d6)]=_0x19d89b,_0x1a755e[_0x5612de(0x2d3)]=_0x19d89b;var _0x18582a=!(0x1796+-0xf7d+-0x818);function _0x1dbe74(){var _0x101f5b=_0x5612de;if(document&&document[_0x101f5b(0x2f6)]&&!_0x18582a){for(var _0x1dfebe=0x1*-0xe36+0x6e8+-0xa*-0xbb,_0x5af0b0=Object[_0x101f5b(0x17f)](_0x1a755e);_0x1dfebe<_0x5af0b0[_0x101f5b(0x259)];_0x1dfebe++){var _0x2ecc3c=_0x5af0b0[_0x1dfebe];document[_0x101f5b(0x2f6)](_0x2ecc3c,_0x1a755e[_0x2ecc3c]);}_0x18582a=!(-0x1*-0xfcb+0x21*0x65+-0x1cd0);}}function _0x3dcfd5(_0x1f9f7e){var _0x4f99fd=_0x5612de,_0x395631=_0x1f9f7e,_0x1f0c31=_0x1f9f7e['type'];_0x1f9f7e[_0x4f99fd(0x379)]&&_0x4f99fd(0x299)===_0x1f0c31&&(_0x395631=_0x1f9f7e['touches'][-0x9*-0x3f4+0x1*0xc73+-0x3007],_0x2786f5=!(-0x129a+0x1bca+-0x930));var _0x5d0e65={'x':Math[_0x4f99fd(0x1cf)](_0x395631['clientX']),'y':Math[_0x4f99fd(0x1cf)](_0x395631['clientY']),'d':Date[_0x4f99fd(0x34d)]()};_0x9c0be2(_0x9fb121,_0x5d0e65),_0x1d26db(_0x6caf[_0x4f99fd(0x21d)],{'ts':_0x5d0e65['d'],'x':_0x5d0e65['x'],'y':_0x5d0e65['y']},_0xf63a81[_0x4f99fd(0x2be)]);}function _0x56114b(_0x35e1c1){var _0x3de3de=_0x5612de,_0x411a0a=0x22e7+-0x5*-0x133+-0x28e6;(_0x35e1c1[_0x3de3de(0x28c)]||_0x35e1c1['ctrlKey']||_0x35e1c1['metaKey']||_0x35e1c1[_0x3de3de(0x22e)])&&(_0x411a0a=0xe7c+-0x3*0x143+-0xab2);var _0x5239f5={'x':_0x411a0a,'d':Date['now']()};_0x9c0be2(_0x191fa5,_0x5239f5),_0x1d26db(_0x6caf[_0x3de3de(0x391)],{'ts':_0x5239f5['d']},_0xf63a81[_0x3de3de(0x355)]);}function _0x19d89b(_0x39a86c){var _0x3bb659=_0x5612de,_0x4a9de2=_0x39a86c,_0x33eb04=_0x39a86c['type'];_0x39a86c[_0x3bb659(0x379)]&&'touchstart'===_0x33eb04&&(_0x4a9de2=_0x39a86c['touches'][0x1491+0x1*-0x1216+-0x1*0x27b],_0x2786f5=!(-0x1*-0x853+0x12df*0x2+-0x2e11));var _0xf57aea={'x':Math['floor'](_0x4a9de2[_0x3bb659(0x2bf)]),'y':Math[_0x3bb659(0x1cf)](_0x4a9de2['clientY']),'d':Date['now']()};_0x9c0be2(_0x207cc5,_0xf57aea),_0x1d26db(_0x6caf[_0x3bb659(0x3b6)],{'ts':_0xf57aea['d'],'x':_0xf57aea['x'],'y':_0xf57aea['y']},_0xf63a81[_0x3bb659(0x1f0)]);}function _0x42fe9b(_0x320405){var _0x2bf333=_0x5612de;return _0x320405['reduce'](_0x388856)/_0x320405[_0x2bf333(0x259)];}function _0x3ea7d6(_0xbdd4a6){var _0x1e5c1e=_0x5612de;if(_0xbdd4a6[_0x1e5c1e(0x259)]<=-0x1*-0x2420+0xc87*-0x1+0x5e6*-0x4)return-0x1*-0x309+0x15*0x9d+-0xfea*0x1;var _0x3deca4=_0x42fe9b(_0xbdd4a6),_0x58472b=_0xbdd4a6['map'](function(_0x508d28){return _0x508d28-_0x3deca4;});return Math[_0x1e5c1e(0x317)](_0x58472b['map'](_0x20a3d9)[_0x1e5c1e(0x376)](_0x388856)/(_0xbdd4a6[_0x1e5c1e(0x259)]-(0xa*0x127+-0x2103+-0xe*-0x189)));}function _0x52f064(_0x2031c9,_0x5a5379,_0xb6ab78){var _0x1d0db5=_0x5612de,_0xcfe257=0xae8+-0xb*-0x6f+-0xfad,_0x4f9c60=-0x894+-0x12a7+0x1*0x1b3b;if(_0x2031c9[_0x1d0db5(0x259)]>_0x5a5379){for(var _0x147b41=[],_0x5ca0e0=-0x10a8+-0x212f+0x3*0x109d;_0x5ca0e0<_0x2031c9[_0x1d0db5(0x259)]-(0x557*-0x7+-0x5*-0xc6+0x14a*0x1a);_0x5ca0e0++){var _0x2a170e=_0x2031c9[_0x5ca0e0+(-0x20c6*-0x1+0x1f31+-0x3ff6)],_0x1f8ecf=_0x2031c9[_0x5ca0e0],_0x1b82ec=_0x2a170e['d']-_0x1f8ecf['d'];_0x1b82ec&&(_0xb6ab78?_0x147b41[_0x1d0db5(0x36e)]((0xf98+0x90f+0x2*-0xc53)/_0x1b82ec):_0x147b41[_0x1d0db5(0x36e)](Math[_0x1d0db5(0x317)](_0x20a3d9(_0x2a170e['x']-_0x1f8ecf['x'])+_0x20a3d9(_0x2a170e['y']-_0x1f8ecf['y']))/_0x1b82ec));}_0xcfe257=_0x42fe9b(_0x147b41),0x5*-0x79d+0xf2d*-0x1+-0xeb*-0x3a===(_0x4f9c60=_0x3ea7d6(_0x147b41))&&(_0x4f9c60=0xcc5+-0x1*-0x9e1+-0x16a6+0.01);}return[_0xcfe257,_0x4f9c60];}function _0x26d461(){var _0x3f77b6=_0x5612de,_0x5e9ee6=!(0x9d*-0x6+0x16b0+-0x1301),_0x499168=-0x21e+-0x1b6a+0x28*0xbd;try{document&&document['createEvent']&&(document[_0x3f77b6(0x188)](_0x3f77b6(0x240)),_0x5e9ee6=!(0x195e+-0x5*-0x371+-0x3f*0xad));}catch(_0x4b2baa){}var _0x462d0a=_0x52f064(_0x9fb121,0x6a*0x2+-0x1b*0x16b+0x2576),_0x136148=_0x52f064(_0x191fa5,-0x94c+-0xd*0x51+0xd6e,!(0x1*0x7ed+-0xab4+0x2c7)),_0x17d8e6=-0x2*0xb95+-0xda0+0x24cb;!_0x5e9ee6&&_0x2786f5&&(_0x17d8e6|=0x2468+-0x1*0x2c2+-0x2166,_0x499168|=_0x216650[_0x3f77b6(0x2f2)]),-0x293*-0x3+0x1*0xdad+-0x1566===_0x9fb121['length']?(_0x17d8e6|=-0x1cf3*0x1+0x12e1+0xa14,_0x499168|=_0x216650[_0x3f77b6(0x2fe)]):_0x462d0a[-0x17f8+-0x1eae+0x36a6]>0xbab+-0x1fb9+0x3*0x6c0&&(_0x17d8e6|=0x20*-0x5c+0xb22+0x6e,_0x499168|=_0x216650['kMoveFast']),0x2297*-0x1+0x57b*0x3+0x65*0x2e===_0x207cc5['length']&&(_0x17d8e6|=0x15a5+0x557+-0x1af8,_0x499168|=_0x216650['kNoClickTouch']),0x20bb+0x1e5f+0x1f8d*-0x2===_0x191fa5[_0x3f77b6(0x259)]?(_0x17d8e6|=-0x1*0x1b73+-0x43e+0x1fb9,_0x499168|=_0x216650['kNoKeyboardEvent']):_0x136148[0x16ee+-0x1*0x1e99+0x7ab]>0x1*0xb77+-0x82+-0xaf5+0.5&&(_0x17d8e6|=0x176a+0x183+0x7*-0x38b,_0x499168|=_0x216650[_0x3f77b6(0x3bf)]),_0xe06992['ubcode']=_0x499168;var _0x9340b9=_0x17d8e6[_0x3f77b6(0x3ae)](-0x2018+-0x543*0x7+0x450d);return 0xc8e*0x1+-0x3*0x3ca+-0x12f===_0x9340b9['length']?_0x9340b9='00'+_0x9340b9:-0x117a+0x19*-0x57+0x8a9*0x3===_0x9340b9[_0x3f77b6(0x259)]&&(_0x9340b9='0'+_0x9340b9),_0x9340b9;}function _0x5047d8(){_0x450b73(-0x1b05+0x103*0x1c+-0x4*0x53);}function _0x4145f8(_0x30c511,_0x35c283){var _0x46d631=_0x5612de;for(var _0x3d95be=_0x35c283[_0x46d631(0x259)],_0x3c9714=new ArrayBuffer(_0x3d95be+(-0x2ef*-0x2+0x1bbd+0x2e*-0xbb)),_0x1054b9=new Uint8Array(_0x3c9714),_0x5633c2=-0x114+-0x2096+0x21aa,_0x28d040=0x10a*-0x1a+0x5*0x5cf+-0x207*0x1;_0x28d040<_0x3d95be;_0x28d040++)_0x1054b9[_0x28d040]=_0x35c283[_0x28d040],_0x5633c2^=_0x35c283[_0x28d040];_0x1054b9[_0x3d95be]=_0x5633c2;var _0x280d0b=-0xe*-0x98+0x1f*0x9e+0x6f*-0x3d&Math[_0x46d631(0x1cf)]((-0x1*0x13ed+-0x2*0x581+0x1fee)*Math[_0x46d631(0x18e)]()),_0x418204=String[_0x46d631(0x1e2)][_0x46d631(0x207)](null,_0x1054b9),_0x250194=_0x21db29(String[_0x46d631(0x1e2)](_0x280d0b),_0x418204),_0x37237c='';return _0x37237c+=String[_0x46d631(0x1e2)](_0x30c511),_0x37237c+=String[_0x46d631(0x1e2)](_0x280d0b),_0x389396(_0x37237c+=_0x250194,'s1');}function _0x1633f2(_0x48f290,_0x504655,_0x4fa808,_0x1a5c57,_0x3a4737){var _0x5a9e67=_0x5612de;_0x5863d1(),_0x26d461(),void(0x247a+-0x3f2+-0x2088)!==_0x1a5c57&&''!==_0x1a5c57&&(_0x1a5c57='');var _0x21ca02=_0x5dd467(_0x1a5c57);_0x3a4737||(_0x3a4737='00000000000000000000000000000000');var _0x1fb174=new ArrayBuffer(0x1cc4+-0x1109+-0x3e6*0x3),_0x1ffaa7=new Uint8Array(_0x1fb174),_0xeefda2=0x22cb+0x1*-0x32c+0x1f9f*-0x1|_0x48f290<<-0x228e+0x22f4*0x1+-0x60|_0x504655<<0x1075+-0x2c*-0x26+-0x16f8|(-0x261a+0x1e95+0x786&Math[_0x5a9e67(0x1cf)]((0x705+0x1069+-0x3*0x7ae)*Math[_0x5a9e67(0x18e)]()))<<0x3f4*0x5+0x59a+-0x195a|-0x1*-0x2033+0x284*0x5+0xeed*-0x3;_0x6caf[_0x5a9e67(0x326)]++;var _0x5eb39b=-0x55+0x8a3+-0x80f&_0x6caf[_0x5a9e67(0x326)];_0x1ffaa7[0x18e0*0x1+0xd8d+-0x266d]=_0x4fa808<<-0x1*0x1117+-0x144f+0x256c|_0x5eb39b,_0x1ffaa7[-0x177d+0x3*0xb39+-0xa2d]=_0x6caf['envcode']>>-0x163b+0x2c3+0x1380&0x15b3+-0x1efd+0xa49*0x1,_0x1ffaa7[-0x5e3+-0x1*0x1535+0x1b1a*0x1]=0x64a+-0xc*0xa3+0x259&_0x6caf[_0x5a9e67(0x380)],_0x1ffaa7[0x81*0x11+0xa49*0x1+-0x12d7]=_0xe06992[_0x5a9e67(0x382)];var _0x13f487=_0x42e709[_0x5a9e67(0x2b7)](_0x5dd467(_0x42e709[_0x5a9e67(0x2b7)](_0x21ca02)));_0x1ffaa7[-0xf6b*0x2+0x1c4+0x1d16]=_0x13f487[-0x81a+-0x24ee+-0x18e*-0x1d],_0x1ffaa7[0x73d*-0x3+0x2335+-0x1*0xd79]=_0x13f487[-0x16*-0x4+-0xfdd+0x3e5*0x4];var _0x136ce1=_0x42e709['decode'](_0x5dd467(_0x42e709['decode'](_0x3a4737)));return _0x1ffaa7[0x17*-0x35+0x220e+-0x1d45]=_0x136ce1[0xe7*-0x11+0x1*-0x17a1+0x2706],_0x1ffaa7[0x1b8+-0x2*-0x665+0x151*-0xb]=_0x136ce1[-0x276*0x9+-0x1335+-0x39*-0xba],_0x1ffaa7[0x1fa8+0x776+0x1*-0x2716]=-0x23cd+-0x69d+0x2b69&Math[_0x5a9e67(0x1cf)]((0x9c+0x77b*0x4+-0x1*0x1d89)*Math[_0x5a9e67(0x18e)]()),_0x4145f8(_0xeefda2,_0x1ffaa7);}function _0x34c70a(_0xf6b3d0,_0x289075,_0x2c48ed){var _0x1fe583=_0x5612de;return{'X-Bogus':_0x1633f2(_0x2e10da[_0x1fe583(0x289)],_0x462335['initialized'],_0xf6b3d0,null,_0x2c48ed)};}function _0x11233a(_0x34d47b,_0x1b3ba5,_0x55dcd4){var _0x3e598d=_0x5612de;return{'X-Bogus':_0x1633f2(_0x2e10da[_0x3e598d(0x1df)],_0x462335[_0x3e598d(0x1fc)],_0x34d47b,_0x1b3ba5,_0x55dcd4)};}function _0x5c2014(_0x1fa689){return w_0x5c3140('484e4f4a403f524300362d0a5f00233c0000000029b6a730000000630214000103001400020700001400030700011400041101031100031347000d11010311000313140001450023110103110004134700130211010011010311000413430114000145000607000214000102110101110002021100014303140005110005420003096b1e7e601e606766710c6b1e7e601e63726a7f7c7277200303030303030303030303030303030303030303030303030303030303030303',{get 0x0(){return _0x5dd467;},get 0x1(){return _0x34c70a;},0x2:arguments,0x3:_0x1fa689},this);}function _0x3c875d(_0x17e2b2,_0x1e7967){var _0x36f0c4=_0x5612de,_0x292251=new Uint8Array(-0x1d76+0xa81*-0x3+0x3cfc);return _0x292251[0xff*0x25+0x1ee0*-0x1+-0x5fb]=_0x17e2b2/(0x1126*-0x2+-0x1*0x20f6+-0x2*-0x2221),_0x292251[0x29*-0xde+0x25a6+0x6b*-0x5]=_0x17e2b2%(-0x5*0x705+0x10bd+-0xb1*-0x1c),_0x292251[-0x1*-0x1fb5+-0x1921+-0x692]=_0x1e7967%(0x1615+-0xda0+0x1*-0x775),String[_0x36f0c4(0x1e2)][_0x36f0c4(0x207)](null,_0x292251);}function _0x4b49f3(_0xe64465){var _0x2fa114=_0x5612de;return String[_0x2fa114(0x1e2)](_0xe64465);}function _0x26151b(_0x51896f,_0x3a647f,_0x2db6f6){return _0x4b49f3(_0x51896f)+_0x4b49f3(_0x3a647f)+_0x2db6f6;}function _0xc38697(_0x5bf566,_0x20667e){return _0x389396(_0x5bf566,_0x20667e);}function _0x538c80(_0x213380,_0x5bb06d,_0x2807a8,_0x2bbe8d,_0x43c759,_0x510d57,_0x5b433d,_0x30b81d,_0x392407,_0x124f15,_0x22fc4c,_0x56f654,_0x8ab411,_0x1c9de4,_0x24f978,_0x27f46c,_0x1bd4f9,_0x572854,_0x34f6a0){var _0x2e1799=_0x5612de,_0x432584=new Uint8Array(0x22bb+0xd*-0x2bd+0x1*0xf1);return _0x432584[-0xc70+0x5e9+0x22d*0x3]=_0x213380,_0x432584[-0x2*-0x127c+-0x1*0x1f61+-0x596]=_0x22fc4c,_0x432584[-0x1af*0xb+0x97b+0xc1*0xc]=_0x5bb06d,_0x432584[-0xfc5*-0x2+-0x5e5+-0x19a2]=_0x56f654,_0x432584[-0x35*0xa3+-0x10c9*-0x2+0x1*0x31]=_0x2807a8,_0x432584[0x1d1d+0x124a+-0x1*0x2f62]=_0x8ab411,_0x432584[-0x1*-0xb49+-0x5d1+-0x572]=_0x2bbe8d,_0x432584[-0x18e*-0xe+0xff*0x22+-0x379b]=_0x1c9de4,_0x432584[-0x697*0x2+0xcc8+0x5*0x16]=_0x43c759,_0x432584[0x2*0x8bf+0xcc4+-0x1e39]=_0x24f978,_0x432584[0x63*-0x43+-0x242+0x3*0x967]=_0x510d57,_0x432584[0x1aa9+0xe38+0x1*-0x28d6]=_0x27f46c,_0x432584[-0xdee+-0x1*0x105b+0x1e55*0x1]=_0x5b433d,_0x432584[0x1*-0x1543+-0x1*0x1b5b+0x1*0x30ab]=_0x1bd4f9,_0x432584[0xc86*-0x1+0x1c9+0xacb]=_0x30b81d,_0x432584[0x1581+-0x70c+-0xe66]=_0x572854,_0x432584[0x1dc1+0xd*0xb5+-0x26e2]=_0x392407,_0x432584[-0x36*0x4a+0x268a+-0x16dd]=_0x34f6a0,_0x432584[0xf*-0x1ca+-0x16c2+0x31aa]=_0x124f15,String['fromCharCode'][_0x2e1799(0x207)](null,_0x432584);}var _0x3c4305=!(-0xc6b+0x270e*0x1+-0x1aa2);function _0x8edc3d(_0x22218f,_0x1e70b9){var _0x364732=_0x5612de;return w_0x5c3140(_0x364732(0x2b2),{get 0x0(){return _0x5dd467;},get 0x1(){return _0x3c4305;},set 0x1(_0x539e8c){_0x3c4305=_0x539e8c;},get 0x2(){return _0x462335;},get 0x3(){return _0x5863d1;},get 0x4(){return _0x26d461;},get 0x5(){return _0xe06992;},get 0x6(){return _0x6caf;},get 0x7(){return _0x42e709;},0x8:String,get 0x9(){return navigator;},get 0xa(){return _0x3c875d;},get 0xb(){return _0x21db29;},get 0xc(){return _0xc38697;},0xd:Date,get 0xe(){return _0x18b4be;},get 0xf(){return _0x538c80;},get 0x10(){return _0x4b49f3;},get 0x11(){return _0x26151b;},get 0x12(){return _0x389396;},0x13:arguments,0x14:_0x22218f,0x15:_0x1e70b9,0x16:RegExp},this);}function _0x556182(_0x5e8c32){var _0x4a1328=_0x5612de;_0x4a2daf(_0x4a1328(0x28a),_0x5e8c32);}function _0x5141ac(){var _0x2770a6=_0x3d13cf('xmst');return _0x2770a6||'';}function _0x50686a(_0x193aca){var _0x460363=_0x5612de;return _0x460363(0x331)===Object[_0x460363(0x344)][_0x460363(0x3ae)][_0x460363(0x334)](_0x193aca);}function _0x2195cd(_0x383b5f,_0x4a7858){var _0x20b490=_0x5612de;if(_0x383b5f){var _0x11f5aa=_0x383b5f[_0x4a7858];if(_0x11f5aa){var _0x4fcde1=_0x1db123(_0x11f5aa);return _0x20b490(0x17d)===_0x4fcde1||_0x20b490(0x1ee)===_0x4fcde1?-0x1*-0x4e8+0x18eb+-0x1dd2:'string'===_0x4fcde1?_0x4fcde1[_0x20b490(0x259)]>-0x124c+0x20c3+-0xe77?0x35*0x34+0x1ec8+-0x298b:-0x8a*0x31+-0x18c7+-0x1111*-0x3:_0x50686a(_0x11f5aa)?-0x1*-0xc83+-0x3*0x466+0x4*0x2c:0x1018+-0x371*-0x9+0x1*-0x2f0f;}}return-0x155a+0x4c*-0x6c+0x4*0xd5b;}function _0xdc4d4c(_0x3c0aaa){var _0x11952c=_0x5612de;try{var _0x57243b=Object[_0x11952c(0x344)][_0x11952c(0x3ae)][_0x11952c(0x334)](_0x3c0aaa);return _0x11952c(0x30e)===_0x57243b?!(-0x1*0x7c5+0x1*-0x853+0x338*0x5)===_0x3c0aaa?-0xa1*-0x23+0x2*0xaa9+-0x2b54:-0x1e71*0x1+0x1*0x1885+0x5ee:_0x11952c(0x1af)===_0x57243b?-0xce*0x25+0x1e1a+0x3*-0x1b:'[object\x20Undefined]'===_0x57243b?-0x1*0x159a+-0x684+0x1c22:_0x11952c(0x1ce)===_0x57243b?-0x3a*-0x70+0x4b1+0xa04*-0x3:'[object\x20String]'===_0x57243b?''===_0x3c0aaa?0x12*0xad+0x3b*0x71+-0x262e:0x3*0xf8+0x22ec+-0x25cc:_0x11952c(0x331)===_0x57243b?-0x1*-0x874+-0x1*0x2197+0x1923===_0x3c0aaa['length']?-0xf6f+-0x1ee3+0x2e5b:-0x10c3+-0x466*-0x4+0x7*-0x1d:_0x11952c(0x37b)===_0x57243b?0x1*0x22e6+0x1*0x2669+0x824*-0x9:_0x11952c(0x1fe)===_0x57243b?0x2392+-0x15db+-0xdab:_0x11952c(0x17d)===_0x1db123(_0x3c0aaa)?-0x3*0xd7+-0x17b0+0x1a98:-(-0x8e4+0x2*0xd1b+-0x1151);}catch(_0x320465){return-(-0x62e*0x1+0x3*-0x3ec+0x11f4);}}var _0x2d8bb4={};function _0x241339(){var _0x5d55e6=_0x5612de;return document[_0x5d55e6(0x373)]?'IE':-0xc04+-0xbe8+0x17ec;}function _0x5011f8(){var _0x52b3d6=_0x5612de;return eval['toString']()[_0x52b3d6(0x259)];}function _0x58210c(_0x4d43be,_0x3faa08,_0x3d51f5){var _0x16b761=_0x5612de;for(var _0x4738ee={},_0x793fa7=0x111+-0x209*0x1+0x1f*0x8;_0x793fa7<_0x3faa08[_0x16b761(0x259)];_0x793fa7++){var _0x15231e=void(-0xf02*-0x1+0xe35+-0x1d37),_0x2d01fa=void(-0x782+0x5*-0x143+0xdd1),_0x17bcc5=_0x3faa08[_0x793fa7];try{_0x4d43be&&(_0x15231e=_0x4d43be[_0x17bcc5]);}catch(_0x1de94f){}if('string'===_0x3d51f5)_0x2d01fa=''+_0x15231e;else{if(_0x16b761(0x28b)===_0x3d51f5)_0x2d01fa=_0x15231e?Math[_0x16b761(0x1cf)](_0x15231e):-(-0x19f0+0x1ada+0xe9*-0x1);else{if(_0x16b761(0x1ab)!==_0x3d51f5)throw Error(_0x16b761(0x256));_0x2d01fa=_0xdc4d4c(_0x15231e);}}_0x4738ee[_0x17bcc5]=_0x2d01fa;}return _0x4738ee;}function _0x3a2b92(){var _0x53dacb=_0x5612de,_0x1c216f;Object[_0x53dacb(0x2a0)](_0x2d8bb4[_0x53dacb(0x270)],_0x58210c(navigator,[_0x53dacb(0x36b),_0x53dacb(0x350),_0x53dacb(0x184),'appVersion',_0x53dacb(0x32d),'doNotTrack',_0x53dacb(0x39a),_0x53dacb(0x38c),_0x53dacb(0x17a),_0x53dacb(0x246),'productSub','cpuClass',_0x53dacb(0x212),_0x53dacb(0x1d5),_0x53dacb(0x1de),_0x53dacb(0x210),_0x53dacb(0x314),_0x53dacb(0x1c9),'webdriver'],'string')),Object['assign'](_0x2d8bb4[_0x53dacb(0x270)],_0x58210c(navigator,[_0x53dacb(0x1e1),'vibrate',_0x53dacb(0x2e2),_0x53dacb(0x292),_0x53dacb(0x390),_0x53dacb(0x38f)],_0x53dacb(0x1ab))),Object[_0x53dacb(0x2a0)](_0x2d8bb4[_0x53dacb(0x270)],_0x58210c(navigator,[_0x53dacb(0x360),_0x53dacb(0x33b)],_0x53dacb(0x28b))),_0x2d8bb4['navigator'][_0x53dacb(0x398)]=''+navigator[_0x53dacb(0x398)];try{document[_0x53dacb(0x188)](_0x53dacb(0x240)),_0x1c216f=-0xa2*-0x5+0x26*-0xe8+-0x11*-0x1d7;}catch(_0x264380){_0x1c216f=0xbc0+-0x26b+-0xd9*0xb;}_0x2d8bb4['navigator'][_0x53dacb(0x34b)]=_0x1c216f;var _0x5ac61e=_0x53dacb(0x1ea)in window?0x2424+0x1*0xa5c+-0x2e7f:-0x86b*0x1+-0x195+0xa02;_0x2d8bb4[_0x53dacb(0x270)][_0x53dacb(0x1d6)]=_0x5ac61e;}function _0x5abc93(){var _0x279235=_0x5612de;Object['assign'](_0x2d8bb4['window'],_0x58210c(window,['Image',_0x279235(0x309),_0x279235(0x29f),_0x279235(0x399),_0x279235(0x33a),'external','mozRTCPeerConnection','postMessage',_0x279235(0x284),_0x279235(0x2de),_0x279235(0x2ff),_0x279235(0x1dc),_0x279235(0x3be),_0x279235(0x2e9)],_0x279235(0x1ab))),Object[_0x279235(0x2a0)](_0x2d8bb4[_0x279235(0x393)],_0x58210c(window,[_0x279235(0x3c0)],_0x279235(0x28b))),_0x2d8bb4[_0x279235(0x393)][_0x279235(0x3c2)]=window[_0x279235(0x3c2)][_0x279235(0x290)];}function _0x5d3845(){var _0x4ceb6d=_0x5612de;try{var _0x55434c=document,_0x2f0012=window[_0x4ceb6d(0x328)],_0x5ac16c=window[_0x4ceb6d(0x306)]>>>0x1d2c+-0x21*0xa7+-0x1*0x7a5,_0x44cace=window[_0x4ceb6d(0x222)]>>>-0xcd0+-0x1f70+0x162*0x20,_0x5953b9=window[_0x4ceb6d(0x1aa)]>>>0x5*0x2e3+-0x1*-0x1f7+-0x1*0x1066,_0x1dfce9=window[_0x4ceb6d(0x26d)]>>>0x1545+-0x8d7+-0xc6e,_0x29911c=Math[_0x4ceb6d(0x1cf)](window['screenX']),_0x340972=Math['floor'](window[_0x4ceb6d(0x366)]),_0x4bc0b8=window[_0x4ceb6d(0x258)]>>>-0x1*-0x1548+-0x21*-0x7+-0x162f,_0x39df7d=window[_0x4ceb6d(0x282)]>>>0x1dc7*0x1+-0x2113+0x34c,_0x13b30d=_0x2f0012['availWidth']>>>0xc84+0x1*-0x644+-0x640,_0x3f047e=_0x2f0012[_0x4ceb6d(0x276)]>>>-0x23c+0xc*0x1c4+-0x12f4*0x1,_0x3e7595=_0x2f0012['width']>>>0x61*0x4+-0x2469+0x22e5*0x1,_0xa6e264=_0x2f0012[_0x4ceb6d(0x245)]>>>0x882+0x2646+-0x5d9*0x8;return{'innerWidth':void(-0xa40+0x192e+-0x31*0x4e)!==_0x5ac16c?_0x5ac16c:-(0x135c+-0xd*-0x115+-0x45*0x7c),'innerHeight':void(-0x2176+-0x281*-0xc+0x17*0x26)!==_0x44cace?_0x44cace:-(-0x17a1+0xe8*0x8+0x576*0x3),'outerWidth':void(-0x167c+-0x6f*0x49+0x3623)!==_0x5953b9?_0x5953b9:-(0x3*-0x795+0x4*-0x53b+0x2bac),'outerHeight':void(-0x26*-0x5f+-0xf71+-0x1*-0x157)!==_0x1dfce9?_0x1dfce9:-(0xebd+-0x18cd+0x3*0x35b),'screenX':void(-0xf9*-0x22+0xd3*-0x2c+0x332)!==_0x29911c?_0x29911c:-(-0x188+-0x24e3+0x266c),'screenY':void(-0x5*-0x105+0x2*0x11fe+-0x2915)!==_0x340972?_0x340972:-(-0x3cc*-0x6+0x1561*0x1+-0x2c28),'pageXOffset':void(0xb15+-0x64d*0x5+-0x2*-0xa36)!==_0x4bc0b8?_0x4bc0b8:-(0x647+-0x1*-0x1ced+-0x2333),'pageYOffset':void(-0x1337*0x1+-0x10c1+-0x1*-0x23f8)!==_0x39df7d?_0x39df7d:-(0x3b*0x5b+-0x7*0x3a6+0x492),'availWidth':void(-0xd*0x267+0x1a67+-0x2*-0x26a)!==_0x13b30d?_0x13b30d:-(0xe48+-0x7d*-0x23+-0x1f5e),'availHeight':void(-0x192a+0x5*-0x5b3+0xf1*0x39)!==_0x3f047e?_0x3f047e:-(0x4*0x5b+-0x3*-0x13+-0x1a4),'sizeWidth':void(-0x1a38*0x1+-0x20cb+0x1*0x3b03)!==_0x3e7595?_0x3e7595:-(-0x663+-0x2659+0x2cbd*0x1),'sizeHeight':void(0x857*0x3+-0x65*-0xa+-0x1cf7)!==_0xa6e264?_0xa6e264:-(0xafa+0x305*0x5+-0x1a12),'clientWidth':_0x55434c['body']?_0x55434c[_0x4ceb6d(0x189)][_0x4ceb6d(0x1a2)]>>>-0xe38+0x1839+-0xa01:-(0x1338+0x21c1+0x69f*-0x8),'clientHeight':_0x55434c[_0x4ceb6d(0x189)]?_0x55434c[_0x4ceb6d(0x189)][_0x4ceb6d(0x3bb)]>>>-0x1*0x156+0x31*0x43+-0xb7d:-(0x1202+-0x32b+-0xed6),'colorDepth':_0x2f0012[_0x4ceb6d(0x17e)]>>>-0x1d*-0xcb+0x197*0x11+0x26*-0x151,'pixelDepth':_0x2f0012[_0x4ceb6d(0x30a)]>>>-0x254c+0x469+0x20e3*0x1};}catch(_0x35aa5a){return{};}}function _0x573065(){var _0x109cfa=_0x5612de;Object[_0x109cfa(0x2a0)](_0x2d8bb4[_0x109cfa(0x208)],_0x58210c(document,[_0x109cfa(0x335),_0x109cfa(0x253),'documentMode'],_0x109cfa(0x33c))),Object['assign'](_0x2d8bb4[_0x109cfa(0x208)],_0x58210c(document,[_0x109cfa(0x325),_0x109cfa(0x183),'images'],_0x109cfa(0x1ab)));}function _0x47f354(){var _0xad51a5=_0x5612de,_0x5ad193={};try{var _0x344158=document[_0xad51a5(0x260)](_0xad51a5(0x24d))[_0xad51a5(0x2f9)](_0xad51a5(0x26b)),_0x45ab53=_0x344158[_0xad51a5(0x1d7)](_0xad51a5(0x375)),_0x3724de=_0x344158['getParameter'](_0x45ab53['UNMASKED_VENDOR_WEBGL']),_0x41fa79=_0x344158[_0xad51a5(0x383)](_0x45ab53[_0xad51a5(0x39e)]);_0x5ad193['vendor']=_0x3724de,_0x5ad193[_0xad51a5(0x2c2)]=_0x41fa79;}catch(_0x22684e){}return _0x5ad193;}function _0x58bcf8(){var _0x3fa932=_0x5612de,_0xf9ae75=_0x11a1d6();if(_0xf9ae75){var _0x3c4406={'antialias':_0xf9ae75[_0x3fa932(0x1c5)]()[_0x3fa932(0x1a5)]?-0x1d2e+-0x4b1+0x21e0:-0x1*-0x87c+0x4b*0x49+-0x5*0x5f9,'blueBits':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x27e)]),'depthBits':_0xf9ae75['getParameter'](_0xf9ae75[_0x3fa932(0x252)]),'greenBits':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75['GREEN_BITS']),'maxAnisotropy':_0x39c3d8(_0xf9ae75),'maxCombinedTextureImageUnits':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75['MAX_COMBINED_TEXTURE_IMAGE_UNITS']),'maxCubeMapTextureSize':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x300)]),'maxFragmentUniformVectors':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x286)]),'maxRenderbufferSize':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x2da)]),'maxTextureImageUnits':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75['MAX_TEXTURE_IMAGE_UNITS']),'maxTextureSize':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x203)]),'maxVaryingVectors':_0xf9ae75['getParameter'](_0xf9ae75[_0x3fa932(0x264)]),'maxVertexAttribs':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75['MAX_VERTEX_ATTRIBS']),'maxVertexTextureImageUnits':_0xf9ae75['getParameter'](_0xf9ae75[_0x3fa932(0x205)]),'maxVertexUniformVectors':_0xf9ae75['getParameter'](_0xf9ae75[_0x3fa932(0x19d)]),'shadingLanguageVersion':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x179)]),'stencilBits':_0xf9ae75['getParameter'](_0xf9ae75[_0x3fa932(0x2bb)]),'version':_0xf9ae75[_0x3fa932(0x383)](_0xf9ae75[_0x3fa932(0x201)])};Object['assign'](_0x2d8bb4['webgl'],_0x3c4406);}Object[_0x3fa932(0x2a0)](_0x2d8bb4[_0x3fa932(0x26b)],_0x47f354());}function _0x75957(){var _0x122393=_0x5612de;if(window[_0x122393(0x29f)]){for(var _0x2963d5=-0x81a+-0xad*-0x1+0x76f;_0x2963d5<-0x1629+0x1*0x31c+0x1317;_0x2963d5++)try{return!!new window[(_0x122393(0x29f))](_0x122393(0x3b4)+_0x2963d5)&&_0x2963d5[_0x122393(0x3ae)]();}catch(_0x2c5722){}try{return!!new window[(_0x122393(0x29f))]('PDF.PdfCtrl.1')&&'4';}catch(_0x1be01e){}try{return!!new window[(_0x122393(0x29f))](_0x122393(0x345))&&'7';}catch(_0x19cffa){}}return'0';}function _0x1555d9(){return{'plugin':_0x30412e(),'pv':_0x75957()};}function _0x1f01ce(_0x371dd1){var _0x5c76bb=_0x5612de;try{var _0xf71d16=window[_0x371dd1],_0x28a4d1=_0x5c76bb(0x1f9);return _0xf71d16[_0x5c76bb(0x1c4)](_0x28a4d1,_0x28a4d1),_0xf71d16[_0x5c76bb(0x2c3)](_0x28a4d1),!(0x1e7a+-0x1824+0x2*-0x32b);}catch(_0x1dd803){return!(0x6e5+0x599*-0x2+-0x1*-0x44e);}}function _0x3ffe15(){return w_0x5c3140('484e4f4a403f5243003c20117d3adeac000000004e770f390000003a030014000102110100070000430147000b11000103012f170001354902110100070001430147000e110001030103012b2f17000135491100014200020c45464a48457a5d465b484e4c0e5a4c5a5a4046477a5d465b484e4c',{get 0x0(){return _0x1f01ce;},0x1:arguments},this);}function _0x252788(_0x468bb4,_0x392758,_0x269720){var _0x16947c=_0x5612de;for(var _0x4dea11=0x1*0x1705+0x29*-0xbf+0x72*0x11,_0x1f88e5=0x65*-0x7+0xb50+-0x88d;_0x1f88e5<_0x392758['length'];_0x1f88e5++){var _0x1dd5f9=_0x2195cd(_0x468bb4,_0x392758[_0x1f88e5]);if(_0x1dd5f9&&(_0x4dea11|=_0x1dd5f9<<_0x269720+_0x1f88e5,_0x269720+_0x1f88e5>=0xd5a+0x7c+-0x1*0xdb6)){console[_0x16947c(0x310)]('abort\x2032');break;}}return _0x4dea11;}function _0x484054(){return w_0x5c3140('484e4f4a403f5243002c3b0a6f4f88290000000044c410000000011f1101001400010700000700010700020700030700040700050700060700070700080700090c000a14000207000a14000307000b14000407000a110101110004163e000414000a413d00d11100014a07000c1307000d43010300131400050c0000140006030014000711000711000207000e13274700691100014a07000f130700104301140008110002110007131400091100084a0700111307001207001311000918430249110004070014181100091807001518110008070016161100054a070017131100084301491100064a07001813110008430149170007214945ff891101011100041317000335490300170007354911000711000207000e132747001a1100054a0700191311000611000713430149170007214945ffd84111000342001a037e617e037e617d037e617c037e617b037e617a037e6179037e6178037e6177037e6176037d617f0014262b20213b242120382138272e3b263c3b27263c14282a3b0a232a222a213b3c0d361b2e28012e222a04272a2e2b06232a21283b270d2c3d2a2e3b2a0a232a222a213b063c2c3d263f3b0c3c2a3b0e3b3b3d262d3a3b2a08232e21283a2e282a0a052e392e1c2c3d263f3b02726d016d043b2a373b0b2e3f3f2a212b0c2726232b043f3a3c270b3d2a2220392a0c2726232b',{get 0x0(){return document;},get 0x1(){return window;},0x2:arguments},this);}_0x2d8bb4[_0x5612de(0x270)]={},_0x2d8bb4[_0x5612de(0x23e)]={},_0x2d8bb4[_0x5612de(0x393)]={},_0x2d8bb4[_0x5612de(0x26b)]={},_0x2d8bb4['document']={},_0x2d8bb4[_0x5612de(0x328)]={},_0x2d8bb4[_0x5612de(0x1ba)]={},_0x2d8bb4['custom']={};var _0x548676=null;function _0x491716(){var _0x4f58c9=_0x5612de;return w_0x5c3140(_0x4f58c9(0x174),{get 0x0(){return self;},get 0x1(){return window;},get 0x2(){return parent;},0x3:arguments},this);}function _0x2d2578(){!(function(){var _0x236733=w_0x25f3,_0x460c07={},_0x3606e1=navigator[_0x236733(0x359)]||navigator[_0x236733(0x2f0)];if(_0x3606e1){try{_0x460c07[_0x236733(0x3ac)]=_0x3606e1[_0x236733(0x3ac)]?0x147a+-0x5*0x60f+0x9d2:-0x169c+0x26d6+-0x1038,_0x460c07[_0x236733(0x213)]=Math[_0x236733(0x369)]((0x31*0x75+0x14c7+-0x2ac8)*_0x3606e1[_0x236733(0x213)]),_0x460c07[_0x236733(0x2b0)]=''+_0x3606e1[_0x236733(0x2b0)],_0x460c07[_0x236733(0x191)]=''+_0x3606e1['dischargingTime'];}catch(_0x2c301c){}_0x2d8bb4[_0x236733(0x359)]={},Object[_0x236733(0x2a0)](_0x2d8bb4[_0x236733(0x359)],_0x460c07);}else{if(_0x236733(0x384)!=typeof navigator&&navigator[_0x236733(0x1b2)])try{navigator[_0x236733(0x1b2)]()[_0x236733(0x1ed)](function(_0x369fc5){var _0x34e3e9=_0x236733;try{_0x460c07[_0x34e3e9(0x3ac)]=_0x369fc5[_0x34e3e9(0x3ac)]?0x20aa+-0x11*-0x86+0x1*-0x298f:0xd51+0x152c+0xd*-0x2a7,_0x460c07[_0x34e3e9(0x213)]=Math[_0x34e3e9(0x369)]((0x1205+-0x41*0x32+-0x3*0x1a5)*_0x369fc5[_0x34e3e9(0x213)]),_0x460c07[_0x34e3e9(0x2b0)]=''+_0x369fc5[_0x34e3e9(0x2b0)],_0x460c07[_0x34e3e9(0x191)]=''+_0x369fc5['dischargingTime'];}catch(_0x2f63d1){}_0x2d8bb4['battery']={},Object[_0x34e3e9(0x2a0)](_0x2d8bb4[_0x34e3e9(0x359)],_0x460c07);});}catch(_0xfc012d){}}}()),'undefined'!=typeof Promise&&(_0x548676=new Promise(function(_0x1df02e){try{_0x5bbaf0()['then'](function(_0x27ab47){var _0xbee19f=w_0x25f3;Object[_0xbee19f(0x2a0)](_0x2d8bb4['wID'],{'rtcIP':_0x27ab47});});}catch(_0x7bc12a){}_0x1df02e('');}));}function _0x5c328e(){var _0x378ae2=_0x5612de;return w_0x5c3140(_0x378ae2(0x1ad),{get 0x0(){return window;},get 0x1(){return navigator;},get 0x2(){var _0xa78dc5=_0x378ae2;return _0xa78dc5(0x384)!=typeof InstallTrigger?InstallTrigger:void(0x26ae*0x1+-0xad*0x2e+-0x6c*0x12);},0x3:Object,get 0x4(){return _0x241339;},get 0x5(){return _0x2d8bb4;},get 0x6(){return document;},0x7:Promise,0x8:Date,get 0x9(){return _0x252788;},get 0xa(){return _0x5011f8;},get 0xb(){return _0x4f323e;},get 0xc(){return _0x5090f5;},0xd:Math,get 0xe(){return _0x3ffe15;},get 0xf(){return _0x18b4be;},get 0x10(){return _0x484054;},get 0x11(){return _0x491716;},get 0x12(){return _0x462335;},get 0x13(){return _0x24dc34;},get 0x14(){return _0x6caf;},get 0x15(){return _0x2d2578;},get 0x16(){return _0x3a2b92;},get 0x17(){return _0x5abc93;},get 0x18(){return _0x573065;},get 0x19(){return _0x58bcf8;},get 0x1a(){return _0x1555d9;},get 0x1b(){return _0x5d3845;},0x1c:parseInt,get 0x1d(){return _0x3d13cf;},get 0x1e(){return _0x4a2daf;},get 0x1f(){return _0x641e3d;},0x20:JSON,get 0x21(){return _0x48a082;},get 0x22(){return _0x4df596;},get 0x23(){return _0x5dde58;},get 0x24(){return _0x548676;},get 0x25(){return _0x29a5ac;},0x26:arguments},this);}function _0x25a792(_0x2f25f2){var _0x329005=_0x5612de;return _0x462335[_0x329005(0x185)]&&_0x462335[_0x329005(0x185)][_0x329005(0x2dc)]&&-(-0x4f2*0x5+0x254f+-0xc94)!==_0x2f25f2[_0x329005(0x2c5)](_0x462335[_0x329005(0x185)][_0x329005(0x2dc)])?_0x39693d['sec']:_0x39693d[_0x329005(0x1b4)];}function _0xd287a1(_0x35260d){var _0x241816=_0x5612de,_0x40ac17=_0x462335[_0x241816(0x185)][_0x241816(0x2dc)];return!(!_0x40ac17||!_0x35260d||-(0x116*-0x2+-0x1af8*-0x1+-0x18cb)===_0x35260d[_0x241816(0x2c5)](_0x40ac17));}function _0x2b13af(_0x52c947){var _0x221375=_0x5612de,_0x7eff84=_0x52c947;decodeURIComponent(_0x52c947)===_0x52c947&&(_0x7eff84=encodeURI(_0x52c947));var _0x5e1d88=_0x7eff84[_0x221375(0x2c5)]('?');if(_0x5e1d88>0x1673*0x1+0x2150+-0x37c3){var _0xc7170=_0x7eff84[_0x221375(0x3a7)](-0x80c+0x71e*0x2+-0x630,_0x5e1d88+(0x2*0x919+0x4*-0x931+0x1293)),_0x8fbad9=_0x7eff84[_0x221375(0x3a7)](_0x5e1d88+(-0x1de4+-0x2*-0x1163+0x4e1*-0x1));_0x7eff84=_0xc7170+_0x8fbad9[_0x221375(0x342)]('\x27')['join'](_0x221375(0x37e));}return _0x7eff84;}function _0x1958a5(_0x42413b,_0x5e3de6){var _0x406220=_0x5612de;for(var _0x32045a='',_0xbe63df='',_0x5623ae=0x5f6+-0x220e+0x1c18;_0x5623ae<_0x5e3de6[_0x406220(0x259)];_0x5623ae++)_0x5623ae%(0x951+0xb15+0x105*-0x14)==-0x10de+0x15*0x185+0x1*-0xf0b?_0xbe63df=_0x5e3de6[_0x5623ae]:_0x32045a+='&'+_0xbe63df+'='+_0x5e3de6[_0x5623ae];var _0x4ddc33=_0x42413b;if(_0x32045a[_0x406220(0x259)]>-0x135d+0x8e1+-0x2c*-0x3d){var _0x50fb42=-(0x2*-0x1252+0x13*0x85+0x95*0x2e)===_0x42413b['indexOf']('?')?'?':'&';_0x4ddc33=_0x42413b+_0x50fb42+_0x32045a[_0x406220(0x3a7)](0x324+-0xf1*-0x17+0x1*-0x18ca);}return _0x4ddc33;}function _0x288415(_0x5a419e){var _0x259f53=_0x5612de,_0x4465ef=_0x5a419e['indexOf']('?');return-(-0x78c+-0x1a4c+-0x6c5*-0x5)!==_0x4465ef?_0x5a419e[_0x259f53(0x3a7)](_0x4465ef+(0x907+0x137b*0x1+-0x1c81)):'';}function _0x6a7375(_0x2a9a57){var _0x447596=_0x5612de;for(var _0x1ca622=-0x16a3*-0x1+0x87b+-0x1f1e;_0x1ca622<_0x462335['_enablePathListRegex']['length'];_0x1ca622++)if(_0x462335[_0x447596(0x1a9)][_0x1ca622]['test'](_0x2a9a57))return!(-0x5ed+-0x3d*-0x41+-0x990);return!(-0x1*-0x26ff+-0x11c1*0x1+-0x153d);}function _0x7d8404(_0x34967f){var _0x5e578e=_0x5612de;return _0x5e578e(0x186)===_0x34967f||'application/json'===_0x34967f;}function _0x3af1be(){var _0x1347f7=_0x5612de;return w_0x5c3140(_0x1347f7(0x368),{get 0x0(){return window;},get 0x1(){return _0x6a7375;},get 0x2(){return _0x6caf;},get 0x3(){return _0x1958a5;},get 0x4(){return _0x2b13af;},get 0x5(){return _0x288415;},get 0x6(){return _0x8edc3d;},get 0x7(){return _0x462335;},get 0x8(){return _0x45e0e9;},get 0x9(){return _0x7d8404;},get 0xa(){return _0x1294ff;},get 0xb(){return _0x20cbf3;},get 0xc(){return _0x45b94b;},get 0xd(){return _0x572e48;},get 0xe(){return _0xd287a1;},get 0xf(){return _0x25a792;},get 0x10(){return _0x39693d;},get 0x11(){return _0x1f42cb;},get 0x12(){return _0x556182;},get 0x13(){return setTimeout;},get 0x14(){return _0x5c328e;},0x15:arguments,0x16:RegExp},this);}var _0x3c4266=_0x5612de(0x384)!=typeof URL&&URL instanceof Object,_0x3311d7='undefined'!=typeof Request&&Request instanceof Object,_0x4f1fa4='undefined'!=typeof Headers&&Headers instanceof Object;function _0x415adb(){var _0x51ab32=_0x5612de;return window[_0x51ab32(0x197)];}function _0x1d82ac(){var _0x2340b7=_0x5612de;return w_0x5c3140(_0x2340b7(0x234),{get 0x0(){return _0x415adb;},get 0x1(){return window;},get 0x2(){return _0xd287a1;},get 0x3(){return _0x25a792;},get 0x4(){return _0x39693d;},get 0x5(){return _0x6caf;},get 0x6(){return _0x1f42cb;},get 0x7(){return _0x556182;},get 0x8(){return setTimeout;},get 0x9(){return _0x5c328e;},get 0xa(){return _0x3311d7;},get 0xb(){return Request;},get 0xc(){return _0x3c4266;},get 0xd(){return URL;},get 0xe(){return _0x6a7375;},get 0xf(){return _0x1958a5;},get 0x10(){return _0x2b13af;},get 0x11(){return _0x288415;},get 0x12(){return _0x8edc3d;},get 0x13(){return _0x462335;},get 0x14(){return _0x45e0e9;},get 0x15(){return _0xb48e77;},get 0x16(){return _0x7d8404;},get 0x17(){return _0x1294ff;},get 0x18(){return _0x20cbf3;},get 0x19(){return _0x45b94b;},get 0x1a(){return _0x572e48;},0x1b:arguments},this);}function _0xb48e77(_0x29caaf,_0xa4ab82){var _0x1cf8d6=_0x5612de,_0x314297='';if(_0x3311d7&&_0x29caaf instanceof Request){var _0x1f374d=_0x29caaf[_0x1cf8d6(0x35b)][_0x1cf8d6(0x32b)](_0x1cf8d6(0x228));return _0x1f374d&&(_0x314297=_0x1f374d),_0x314297;}if(_0xa4ab82&&_0xa4ab82[_0x1cf8d6(0x35b)]){if(_0x4f1fa4&&_0xa4ab82[_0x1cf8d6(0x35b)]instanceof Headers){var _0x4dff82=_0xa4ab82[_0x1cf8d6(0x35b)][_0x1cf8d6(0x32b)](_0x1cf8d6(0x228));return _0x4dff82&&(_0x314297=_0x4dff82),_0x314297;}if(_0xa4ab82['headers']instanceof Array){for(var _0x6ae47c=0xd20+0x699*-0x5+-0x13dd*-0x1;_0x6ae47c<_0xa4ab82['headers'][_0x1cf8d6(0x259)];_0x6ae47c++)if(_0x1cf8d6(0x228)==_0xa4ab82[_0x1cf8d6(0x35b)][_0x6ae47c][-0x12f6+-0x207f+0x1127*0x3]['toLowerCase']())return _0xa4ab82[_0x1cf8d6(0x35b)][_0x6ae47c][-0xaa2+-0xe0e+0x18b1];}if(_0xa4ab82[_0x1cf8d6(0x35b)]instanceof Object){for(var _0x1506c8=-0x1ce4+0x8dc+0xa04*0x2,_0x32fecb=Object['keys'](_0xa4ab82['headers']);_0x1506c8<_0x32fecb[_0x1cf8d6(0x259)];_0x1506c8++){var _0x228b3d=_0x32fecb[_0x1506c8];if('content-type'===_0x228b3d['toLowerCase']())return _0xa4ab82[_0x1cf8d6(0x35b)][_0x228b3d];}return _0x314297;}}}function _0x1294ff(_0x1475ff,_0x5700d1,_0x563b6f){var _0x383b2a=_0x5612de;if(null==_0x563b6f||''===_0x563b6f)return _0x1475ff;if(_0x563b6f=_0x563b6f[_0x383b2a(0x3ae)](),_0x383b2a(0x186)===_0x5700d1){_0x1475ff[_0x383b2a(0x336)]=!(0xfc+-0x22d4+0x21d8);var _0x38ceac=_0x563b6f[_0x383b2a(0x342)]('&'),_0x1b01b6={};if(_0x38ceac){for(var _0x4c4745=-0x29*-0xd3+0x1*0xa01+-0x2bcc;_0x4c4745<_0x38ceac['length'];_0x4c4745++)_0x1b01b6[_0x38ceac[_0x4c4745][_0x383b2a(0x342)]('=')[0x2*-0x391+-0x1*-0x99f+-0x27d]]=decodeURIComponent(_0x38ceac[_0x4c4745][_0x383b2a(0x342)]('=')[0x1e44+-0x14e3*-0x1+-0x3326]);}_0x1475ff[_0x383b2a(0x189)]=_0x1b01b6;}else _0x1475ff[_0x383b2a(0x189)]=JSON['parse'](_0x563b6f);return _0x1475ff;}function _0x45e0e9(_0x2d3284,_0xb8d78){var _0x521f3f=_0x5612de,_0x3496a3=_0xb8d78;if(_0x462335[_0x521f3f(0x18d)][_0x521f3f(0x259)]>0x1f8f*0x1+-0x1*0x21dd+0xa*0x3b)for(var _0x335ec5=0xb*-0xd3+-0x1977*0x1+-0x41*-0x88;_0x335ec5<_0x462335[_0x521f3f(0x18d)][_0x521f3f(0x259)];_0x335ec5++){var _0x3f8686=_0x462335[_0x521f3f(0x18d)][_0x335ec5][-0x2023+0xcb*-0x10+-0x1cb*-0x19];if(_0x3f8686[_0x521f3f(0x22b)](_0xb8d78)){_0x3496a3=_0xb8d78[_0x521f3f(0x377)](_0x3f8686,_0x462335[_0x521f3f(0x18d)][_0x335ec5][0x1a12*-0x1+0x2361+-0x94e]),_0x2d3284&&_0x3d40ff[_0x521f3f(0x397)]['call'](_0x2d3284,_0x521f3f(0x243),_0x521f3f(0x223)+_0xb8d78+'\x0aREWRITED:\x20'+_0x3496a3);break;}}return _0x3496a3=_0x2b13af(_0x3496a3);}function _0x344a4d(){var _0x1b8e41=_0x5612de;return w_0x5c3140(_0x1b8e41(0x20d),{get 0x0(){return window;},get 0x1(){return _0x6a7375;},get 0x2(){return _0x6caf;},get 0x3(){return _0x1958a5;},get 0x4(){return _0x2b13af;},get 0x5(){return _0x288415;},get 0x6(){return _0x8edc3d;},0x7:arguments},this);}function _0x3f720d(){_0x3af1be(),_0x1d82ac(),_0x344a4d();}function _0x3bfecb(_0x4b21ed){var _0x3a0ee2=_0x5612de;this[_0x3a0ee2(0x341)]='ConfigException',this[_0x3a0ee2(0x312)]=_0x4b21ed;}var _0x589057={'cn':{'host':_0x5612de(0x2c9)}},_0x2bbf08=[_0x5612de(0x30c)],_0x3d70a4;function _0x43f5a3(_0x5a985f){var _0x20ecf9=_0x5612de,_0x3c43cc='';return{'host':_0x3c43cc=_0x5a985f[_0x20ecf9(0x1b6)]||_0x5a985f[_0x20ecf9(0x1ae)]?_0x5a985f[_0x20ecf9(0x1b3)]:_0x589057[_0x5a985f[_0x20ecf9(0x224)]][_0x20ecf9(0x2dc)],'pathList':_0x2bbf08,'reportUrl':_0x3c43cc+_0x2bbf08[0x1d7*0x7+0x1073+-0x1d54]};}var _0x383bd7=!(0x244*0xb+-0x16a*-0x4+-0x1e93),_0xd39ee2,_0x9e520d;function _0x53ee31(_0x817028){var _0x5d5e8d=_0x5612de;return w_0x5c3140(_0x5d5e8d(0x34f),{0x0:Object,0x1:Math,get 0x2(){return _0x3bfecb;},get 0x3(){return _0x6caf;},get 0x4(){return _0x462335;},get 0x5(){return _0x43f5a3;},get 0x6(){return setTimeout;},get 0x7(){return _0x5c328e;},get 0x8(){return _0x3d70a4;},set 0x8(_0x5982f0){_0x3d70a4=_0x5982f0;},get 0x9(){return clearInterval;},get 0xa(){return setInterval;},get 0xb(){return _0x450b73;},get 0xc(){return _0x1a39c4;},get 0xd(){return _0x3f720d;},get 0xe(){return _0x59992f;},get 0xf(){return _0x39d569;},get 0x10(){return _0x1dbe74;},get 0x11(){return _0x383bd7;},set 0x11(_0x488bc8){_0x383bd7=_0x488bc8;},get 0x12(){return _0x18707d;},get 0x13(){return _0x1c3b6d;},0x14:arguments,0x15:_0x817028},this);}function _0x3498af(_0x3e9bb9){}function _0x59992f(_0x10dd97){var _0x1beab7=_0x5612de;for(var _0x52c469=0xb*-0x262+-0x606+0x101e*0x2;_0x52c469<_0x10dd97[_0x1beab7(0x259)];_0x52c469++)_0x10dd97[_0x52c469]&&_0x462335['_enablePathListRegex']['push'](new RegExp(_0x10dd97[_0x52c469]));}function _0x39d569(_0x51ab06){var _0x59d7a1=_0x5612de;if(void(-0x11c8*0x1+0x1fa2*0x1+-0xdda)!==_0x51ab06){for(var _0x3e2076=-0x3ab*-0x5+-0x19d4+0x1b*0x47;_0x3e2076<_0x51ab06[_0x59d7a1(0x259)];_0x3e2076++)_0x462335[_0x59d7a1(0x18d)][_0x59d7a1(0x36e)]([new RegExp(_0x51ab06[_0x3e2076][-0xfb*-0x1+0x9a7*0x4+-0x5*0x7eb]),_0x51ab06[_0x3e2076][-0x57*0x55+0x1c99*0x1+0x4b]]);}}function _0x32e4a6(){var _0x494ee9=_0x5612de;return window[_0x494ee9(0x318)]||'';}function _0x1be1e1(_0x58db04){var _0x32bdb2=_0x5612de,_0x4558be=_0x6caf[_0x32bdb2(0x323)],_0x4ab91a=-0x5d*0x5e+-0xb06+-0x47*-0xa3;_0x32bdb2(0x2b6)===_0x58db04&&(_0x4ab91a=0x1754+0xd0*0x4+-0x1a93),_0x32bdb2(0x285)===_0x58db04&&(_0x4ab91a=-0x146*-0x13+0x13*0xef+-0x29ed);var _0x1741bb={'ts':new Date()[_0x32bdb2(0x16d)](),'v':_0x4ab91a};_0x4558be[_0x32bdb2(0x36e)](_0x1741bb);}function _0x4de7ef(){var _0x45ce69=_0x5612de,_0x5c1967,_0x586941;void(0xbc1+0x1*0x1309+-0x1eca)!==document[_0x45ce69(0x285)]?(_0x45ce69(0x285),_0x586941=_0x45ce69(0x1a0),_0x5c1967=_0x45ce69(0x1f6)):void(0x2056+0x2306+-0x435c)!==document[_0x45ce69(0x15f)]?(_0x45ce69(0x15f),_0x586941=_0x45ce69(0x1c8),_0x5c1967=_0x45ce69(0x33f)):void(0x68*0x53+0x3c7*0x3+-0x2d0d)!==document[_0x45ce69(0x2c6)]?(_0x45ce69(0x2c6),_0x586941=_0x45ce69(0x2cc),_0x5c1967=_0x45ce69(0x1a4)):void(-0x1bd7+0xc9*-0x2b+0x3d9a)!==document[_0x45ce69(0x22a)]&&(_0x45ce69(0x22a),_0x586941=_0x45ce69(0x322),_0x5c1967=_0x45ce69(0x166)),document[_0x45ce69(0x2f6)](_0x586941,function(){_0x1be1e1(document[_0x5c1967]);},!(0x1088+0x18c2+0xdc3*-0x3)),_0x1be1e1(document[_0x5c1967]);}function _0x3ff3f1(){_0x58c311();}function _0x4c727e(){var _0x3f5094=_0x5612de;function _0x115c6a(_0x2162b2){var _0x4cc12e=w_0x25f3;_0x462335['triggerUnload']||(_0x462335[_0x4cc12e(0x204)]=!(0x1*-0x632+0x3*-0xb71+-0x1c3*-0x17),_0x3ff3f1());}window&&window[_0x3f5094(0x2f6)]&&(window[_0x3f5094(0x2f6)](_0x3f5094(0x37d),_0x115c6a),window[_0x3f5094(0x2f6)](_0x3f5094(0x29b),_0x115c6a));}function _0x5b850b(){var _0x4d0be4=_0x5612de;for(var _0x35a39e=document[_0x4d0be4(0x272)][_0x4d0be4(0x342)](';'),_0x3046a3=[],_0x58914c=-0x923+-0x66a*-0x5+-0x16ef;_0x58914c<_0x35a39e[_0x4d0be4(0x259)];_0x58914c++)if(_0x4d0be4(0x3b5)==(_0x3046a3=_0x35a39e[_0x58914c][_0x4d0be4(0x342)]('='))[-0x7cd*-0x1+-0x1678+0xeab]['trim']()){_0x6caf[_0x4d0be4(0x3b5)]=_0x3046a3[0x200f*-0x1+0x926+0x16ea];break;}}function _0x498349(_0x5efcd6){return new _0x53ee31(_0x5efcd6);}function _0x475194(_0x56e1e3){-0xa7f+-0xb*0x18a+0x1b6d===_0x56e1e3?setTimeout(_0x5047d8,0x1*-0xb55+-0x1031+0x1bea*0x1):-0xef1+-0xfd3*-0x1+-0xe1===_0x56e1e3&&setTimeout(_0x5c328e,0x6*0x38d+-0x49*-0x2b+-0x95*0x39);}function _0x4a4111(_0x2ba688,_0x1e135c){var _0x18e9b1=_0x5612de;-0xfab+0x9*-0x2a2+0x275e===_0x2ba688&&(_0x462335[_0x18e9b1(0x34a)]=Object['assign']({},_0x462335[_0x18e9b1(0x34a)],_0x1e135c));}function _0x271dea(_0x20563e){void(-0x1aa2+-0x2407+0x3*0x14e3)!==_0x20563e&&''!=_0x20563e&&(_0x6caf['ttwid']=_0x20563e);}function _0x3a4a1a(_0x5b82b5){var _0x30adbb=_0x5612de;void(0xb*-0x17d+-0x227d+0x32dc)!==_0x5b82b5&&''!=_0x5b82b5&&(_0x6caf[_0x30adbb(0x316)]=_0x5b82b5);}function _0x3f0a66(_0x53e069){var _0x38d5b6=_0x5612de;void(-0x7eb*0x3+0xc4*0x2+-0x1*-0x1639)!==_0x53e069&&''!=_0x53e069&&(_0x6caf[_0x38d5b6(0x339)]=_0x53e069);}_0x53ee31[_0x5612de(0x344)][_0x5612de(0x1a3)]=_0x5c2014,_0x53ee31[_0x5612de(0x344)]['getReferer']=_0x32e4a6,_0x53ee31[_0x5612de(0x344)]['setUserMode']=_0x3498af,_0xd39ee2=_0x24dc34(_0x45b94b['refererKey'])||'',_0x2ecc5a(_0x45b94b['refererKey']),_0x5612de(0x2d6)===_0xd39ee2?_0xd39ee2='':''===_0xd39ee2&&(_0xd39ee2=document[_0x5612de(0x2fa)]),_0xd39ee2&&(window[_0x5612de(0x318)]=_0xd39ee2),_0x9e520d=_0x5141ac(),_0x9e520d&&(_0x6caf[_0x5612de(0x2b4)]=_0x9e520d,_0x6caf['msStatus']=_0x39693d['asgw']),setTimeout(function(){_0x18a9f7(),_0x1dbe74(),_0x4de7ef(),_0x4c727e(),_0x21fa28();},-0x1a83+0x15ee+-0xd*-0x141),_0x5b850b(),_0x59992f([_0x5612de(0x30c)]);var _0x1649bc=!(-0xc6b*-0x1+-0x2315+0x16aa);_0x1d18f2['frontierSign']=_0x5c2014,_0x1d18f2[_0x5612de(0x2e8)]=_0x32e4a6,_0x1d18f2[_0x5612de(0x3b9)]=_0x498349,_0x1d18f2[_0x5612de(0x1f8)]=_0x1649bc,_0x1d18f2['report']=_0x475194,_0x1d18f2[_0x5612de(0x298)]=_0x4a4111,_0x1d18f2[_0x5612de(0x1a7)]=_0x3a4a1a,_0x1d18f2['setTTWebidV2']=_0x3f0a66,_0x1d18f2[_0x5612de(0x244)]=_0x271dea,_0x1d18f2[_0x5612de(0x378)]=_0x3498af,Object['defineProperty'](_0x1d18f2,_0x5612de(0x3bd),{'value':!(0x1*0x2069+-0x1403+-0xc66)});});}
diff --git a/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/sign-livews.js b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/sign-livews.js
new file mode 100644
index 0000000..0432245
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Platforms/Douyin/Signing/sign-livews.js
@@ -0,0 +1,163 @@
+const fs = require("fs");
+const path = require("path");
+const vm = require("vm");
+
+const msStub = (process.argv[2] || "").trim();
+if (!msStub) {
+ console.error("Douyin live websocket signer requires an X-MS-STUB value.");
+ process.exit(1);
+}
+
+const mssdkPath = path.join(__dirname, "mssdk.js");
+if (!fs.existsSync(mssdkPath)) {
+ console.error(`Douyin live websocket signer could not find mssdk.js at ${mssdkPath}`);
+ process.exit(1);
+}
+
+function createCanvasElement() {
+ return {
+ style: {},
+ width: 300,
+ height: 150,
+ getContext(type) {
+ if (type === "2d") {
+ return {
+ fillRect() {},
+ fillText() {},
+ measureText() { return { width: 0 }; },
+ beginPath() {},
+ arc() {},
+ stroke() {},
+ closePath() {},
+ rect() {},
+ clearRect() {},
+ drawImage() {},
+ createLinearGradient() { return { addColorStop() {} }; },
+ getImageData() { return { data: [] }; }
+ };
+ }
+
+ return null;
+ },
+ toDataURL() {
+ return "data:image/png;base64,";
+ }
+ };
+}
+
+function createContext() {
+ const context = {
+ console,
+ setTimeout,
+ clearTimeout,
+ setInterval,
+ clearInterval,
+ Buffer,
+ URL,
+ URLSearchParams,
+ TextEncoder,
+ TextDecoder,
+ Promise,
+ Date,
+ Math,
+ JSON,
+ Array,
+ Object,
+ String,
+ Number,
+ Boolean,
+ RegExp,
+ parseInt,
+ parseFloat,
+ encodeURIComponent,
+ decodeURIComponent,
+ encodeURI,
+ decodeURI,
+ navigator: {
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
+ appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
+ appCodeName: "Mozilla",
+ language: "zh-CN",
+ languages: ["zh-CN", "zh"],
+ platform: "Win32",
+ onLine: true,
+ cookieEnabled: true,
+ hardwareConcurrency: 8,
+ maxTouchPoints: 0,
+ webdriver: false,
+ plugins: [],
+ mimeTypes: []
+ },
+ screen: {
+ width: 1920,
+ height: 1080,
+ availWidth: 1920,
+ availHeight: 1040,
+ colorDepth: 24,
+ pixelDepth: 24
+ },
+ location: {
+ href: "https://live.douyin.com/",
+ origin: "https://live.douyin.com",
+ host: "live.douyin.com",
+ hostname: "live.douyin.com",
+ protocol: "https:",
+ pathname: "/",
+ search: "",
+ hash: ""
+ },
+ document: {
+ referrer: "https://live.douyin.com/",
+ cookie: "",
+ compatMode: "CSS1Compat",
+ documentElement: { clientWidth: 1920, clientHeight: 969 },
+ body: { clientWidth: 1920, clientHeight: 969, appendChild() {}, removeChild() {} },
+ createElement(tag) {
+ if (tag === "canvas") {
+ return createCanvasElement();
+ }
+
+ return {
+ style: {},
+ appendChild() {},
+ removeChild() {},
+ setAttribute() {},
+ getContext() { return null; }
+ };
+ },
+ addEventListener() {},
+ removeEventListener() {},
+ querySelector() { return null; },
+ getElementsByTagName() { return []; }
+ }
+ };
+
+ context.window = context;
+ context.self = context;
+ context.parent = context;
+ context.globalThis = context;
+ return context;
+}
+
+try {
+ const context = createContext();
+ const code = fs.readFileSync(mssdkPath, "utf8");
+ vm.createContext(context);
+ vm.runInContext(code, context, { timeout: 10000, filename: "mssdk.js" });
+
+ const signer = context.byted_acrawler && context.byted_acrawler.frontierSign;
+ if (typeof signer !== "function") {
+ throw new Error("frontierSign is not available after loading mssdk.js");
+ }
+
+ const result = signer({ "X-MS-STUB": msStub }) || {};
+ const signature = (result["X-Bogus"] || "").trim();
+ if (!signature) {
+ throw new Error("frontierSign did not return an X-Bogus signature.");
+ }
+
+ process.stdout.write(signature);
+} catch (error) {
+ console.error(error && error.stack ? error.stack : String(error));
+ process.exit(1);
+}
diff --git a/src/LiveRecorder.Infrastructure/Services/EventScriptService.cs b/src/LiveRecorder.Infrastructure/Services/EventScriptService.cs
new file mode 100644
index 0000000..acee9af
--- /dev/null
+++ b/src/LiveRecorder.Infrastructure/Services/EventScriptService.cs
@@ -0,0 +1,278 @@
+using System.Diagnostics;
+using LiveRecorder.Application.Abstractions.Logging;
+using LiveRecorder.Application.Abstractions.Scripting;
+using LiveRecorder.Application.Abstractions.Settings;
+using LiveRecorder.Domain.Entities;
+using LiveRecorder.Domain.Enums;
+using Microsoft.Extensions.Logging;
+
+namespace LiveRecorder.Infrastructure.Services;
+
+public sealed class EventScriptService : IEventScriptService
+{
+ private readonly ISystemSettingsService _settingsService;
+ private readonly ISystemLogService _systemLogService;
+ private readonly ILogger _logger;
+
+ public EventScriptService(
+ ISystemSettingsService settingsService,
+ ISystemLogService systemLogService,
+ ILogger logger)
+ {
+ _settingsService = settingsService;
+ _systemLogService = systemLogService;
+ _logger = logger;
+ }
+
+ public async Task RunLiveStartedAsync(LiveRoom liveRoom, DateTimeOffset occurredAt, CancellationToken cancellationToken = default)
+ {
+ var settings = await _settingsService.GetAsync(cancellationToken);
+ var environment = BuildLiveRoomEnvironment(liveRoom, occurredAt);
+ environment["LIVE_RECORDER_EVENT"] = "live_started";
+ await RunAsync(
+ settings.EnableEventScripts,
+ settings.LiveStartedScriptPath,
+ settings.EventScriptTimeoutSeconds,
+ "live_started",
+ environment,
+ liveRoom.Id,
+ recordSessionId: null,
+ recordTaskId: null,
+ cancellationToken);
+ }
+
+ public async Task RunLiveEndedAsync(LiveRoom liveRoom, DateTimeOffset occurredAt, CancellationToken cancellationToken = default)
+ {
+ var settings = await _settingsService.GetAsync(cancellationToken);
+ var environment = BuildLiveRoomEnvironment(liveRoom, occurredAt);
+ environment["LIVE_RECORDER_EVENT"] = "live_ended";
+ await RunAsync(
+ settings.EnableEventScripts,
+ settings.LiveEndedScriptPath,
+ settings.EventScriptTimeoutSeconds,
+ "live_ended",
+ environment,
+ liveRoom.Id,
+ recordSessionId: null,
+ recordTaskId: null,
+ cancellationToken);
+ }
+
+ public async Task RunSegmentCompletedAsync(
+ LiveRoom? liveRoom,
+ RecordSession recordSession,
+ RecordTask recordTask,
+ RecordResult? recordResult,
+ string segmentFilePath,
+ DateTimeOffset occurredAt,
+ CancellationToken cancellationToken = default)
+ {
+ var settings = await _settingsService.GetAsync(cancellationToken);
+ var environment = BuildLiveRoomEnvironment(liveRoom, occurredAt);
+ environment["LIVE_RECORDER_EVENT"] = "segment_completed";
+ environment["LIVE_RECORDER_RECORD_SESSION_ID"] = recordSession.Id.ToString();
+ environment["LIVE_RECORDER_RECORD_TASK_ID"] = recordTask.Id.ToString();
+ environment["LIVE_RECORDER_SEGMENT_INDEX"] = recordTask.SegmentIndex.ToString();
+ environment["LIVE_RECORDER_SEGMENT_FILE_PATH"] = NormalizePath(segmentFilePath);
+ environment["LIVE_RECORDER_DANMAKU_FILE_PATH"] = NormalizePath(recordResult?.DanmakuFilePath);
+ environment["LIVE_RECORDER_DURATION_SECONDS"] = recordResult?.DurationSeconds?.ToString("0.###") ?? recordTask.DurationSeconds?.ToString("0.###") ?? string.Empty;
+ environment["LIVE_RECORDER_FILE_SIZE_BYTES"] = recordResult?.FileSizeBytes?.ToString() ?? TryGetFileSize(segmentFilePath);
+ environment["LIVE_RECORDER_TASK_STATUS"] = recordTask.Status.ToString();
+ environment["LIVE_RECORDER_SESSION_STATUS"] = recordSession.Status.ToString();
+
+ await RunAsync(
+ settings.EnableEventScripts,
+ settings.SegmentCompletedScriptPath,
+ settings.EventScriptTimeoutSeconds,
+ "segment_completed",
+ environment,
+ liveRoom?.Id ?? recordSession.LiveRoomId,
+ recordSession.Id,
+ recordTask.Id,
+ cancellationToken);
+ }
+
+ private async Task RunAsync(
+ bool enabled,
+ string scriptPath,
+ int timeoutSeconds,
+ string eventName,
+ IReadOnlyDictionary environment,
+ Guid? liveRoomId,
+ Guid? recordSessionId,
+ Guid? recordTaskId,
+ CancellationToken cancellationToken)
+ {
+ if (!enabled || string.IsNullOrWhiteSpace(scriptPath))
+ {
+ return;
+ }
+
+ var normalizedScriptPath = NormalizePath(scriptPath);
+ if (!File.Exists(normalizedScriptPath))
+ {
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Warning,
+ "Script",
+ $"Event script was not found for {eventName}.",
+ normalizedScriptPath,
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ cancellationToken);
+ return;
+ }
+
+ var startInfo = CreateStartInfo(normalizedScriptPath);
+ foreach (var pair in environment)
+ {
+ startInfo.Environment[pair.Key] = pair.Value;
+ }
+
+ using var process = new Process
+ {
+ StartInfo = startInfo,
+ EnableRaisingEvents = true
+ };
+
+ try
+ {
+ process.Start();
+ using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ timeoutCts.CancelAfter(TimeSpan.FromSeconds(Math.Clamp(timeoutSeconds, 1, 3600)));
+ await process.WaitForExitAsync(timeoutCts.Token);
+
+ await _systemLogService.WriteAsync(
+ process.ExitCode == 0 ? SystemLogLevel.Info : SystemLogLevel.Warning,
+ "Script",
+ process.ExitCode == 0
+ ? $"Event script completed for {eventName}."
+ : $"Event script exited with code {process.ExitCode} for {eventName}.",
+ normalizedScriptPath,
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ cancellationToken);
+ }
+ catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
+ {
+ TryKill(process);
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Warning,
+ "Script",
+ $"Event script timed out for {eventName}.",
+ normalizedScriptPath,
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ CancellationToken.None);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Event script failed for {EventName}", eventName);
+ await _systemLogService.WriteAsync(
+ SystemLogLevel.Warning,
+ "Script",
+ $"Event script failed for {eventName}.",
+ ex.ToString(),
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ cancellationToken);
+ }
+ }
+
+ private static Dictionary BuildLiveRoomEnvironment(LiveRoom? liveRoom, DateTimeOffset occurredAt)
+ {
+ return new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["LIVE_RECORDER_EVENT"] = string.Empty,
+ ["LIVE_RECORDER_PLATFORM"] = liveRoom?.Platform.ToString() ?? string.Empty,
+ ["LIVE_RECORDER_LIVE_ROOM_ID"] = liveRoom?.Id.ToString() ?? string.Empty,
+ ["LIVE_RECORDER_ROOM_ID"] = liveRoom?.RoomId ?? string.Empty,
+ ["LIVE_RECORDER_TITLE"] = liveRoom?.Title ?? string.Empty,
+ ["LIVE_RECORDER_ANCHOR"] = liveRoom?.AnchorName ?? string.Empty,
+ ["LIVE_RECORDER_SOURCE_URL"] = liveRoom?.SourceUrl ?? liveRoom?.NormalizedUrl ?? string.Empty,
+ ["LIVE_RECORDER_OCCURRED_AT_UTC"] = occurredAt.ToString("O")
+ };
+ }
+
+ private static ProcessStartInfo CreateStartInfo(string scriptPath)
+ {
+ var extension = Path.GetExtension(scriptPath);
+ var startInfo = new ProcessStartInfo
+ {
+ UseShellExecute = false,
+ RedirectStandardError = false,
+ RedirectStandardOutput = false,
+ CreateNoWindow = true,
+ WorkingDirectory = Path.GetDirectoryName(scriptPath) ?? AppContext.BaseDirectory
+ };
+
+ if (OperatingSystem.IsWindows() && extension.Equals(".ps1", StringComparison.OrdinalIgnoreCase))
+ {
+ startInfo.FileName = "powershell";
+ startInfo.ArgumentList.Add("-NoProfile");
+ startInfo.ArgumentList.Add("-ExecutionPolicy");
+ startInfo.ArgumentList.Add("Bypass");
+ startInfo.ArgumentList.Add("-File");
+ startInfo.ArgumentList.Add(scriptPath);
+ return startInfo;
+ }
+
+ if (OperatingSystem.IsWindows() && (extension.Equals(".bat", StringComparison.OrdinalIgnoreCase) || extension.Equals(".cmd", StringComparison.OrdinalIgnoreCase)))
+ {
+ startInfo.FileName = "cmd";
+ startInfo.ArgumentList.Add("/c");
+ startInfo.ArgumentList.Add(scriptPath);
+ return startInfo;
+ }
+
+ if (!OperatingSystem.IsWindows() && extension.Equals(".sh", StringComparison.OrdinalIgnoreCase))
+ {
+ startInfo.FileName = "/bin/sh";
+ startInfo.ArgumentList.Add(scriptPath);
+ return startInfo;
+ }
+
+ startInfo.FileName = scriptPath;
+ return startInfo;
+ }
+
+ private static string NormalizePath(string? path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return string.Empty;
+ }
+
+ return Path.IsPathRooted(path)
+ ? path
+ : Path.GetFullPath(path, AppContext.BaseDirectory);
+ }
+
+ private static string TryGetFileSize(string? path)
+ {
+ var normalizedPath = NormalizePath(path);
+ if (string.IsNullOrWhiteSpace(normalizedPath) || !File.Exists(normalizedPath))
+ {
+ return string.Empty;
+ }
+
+ return new FileInfo(normalizedPath).Length.ToString();
+ }
+
+ private static void TryKill(Process process)
+ {
+ try
+ {
+ if (!process.HasExited)
+ {
+ process.Kill(true);
+ }
+ }
+ catch
+ {
+ }
+ }
+}
diff --git a/src/LiveRecorder.Infrastructure/Services/FfmpegService.Runtime.cs b/src/LiveRecorder.Infrastructure/Services/FfmpegService.Runtime.cs
index c1f6e54..e9373d1 100644
--- a/src/LiveRecorder.Infrastructure/Services/FfmpegService.Runtime.cs
+++ b/src/LiveRecorder.Infrastructure/Services/FfmpegService.Runtime.cs
@@ -2,6 +2,8 @@ using System.Diagnostics;
using LiveRecorder.Application.Abstractions.Logging;
using LiveRecorder.Application.Abstractions.Notifications;
using LiveRecorder.Application.Abstractions.Platforms;
+using LiveRecorder.Application.Abstractions.Recording;
+using LiveRecorder.Application.Abstractions.Scripting;
using LiveRecorder.Application.Abstractions.Settings;
using LiveRecorder.Application.Services;
using LiveRecorder.Domain.Entities;
@@ -15,6 +17,10 @@ namespace LiveRecorder.Infrastructure.Services;
public sealed partial class FfmpegService
{
+ private static readonly TimeSpan RuntimeSourceFailureWindow = TimeSpan.FromSeconds(20);
+ private static readonly TimeSpan RuntimeOfflineVerificationStopTimeout = TimeSpan.FromSeconds(20);
+ private static readonly TimeSpan RuntimeOfflineVerificationKillTimeout = TimeSpan.FromSeconds(8);
+
private async Task HandleProcessOutputAsync(SessionProcessRuntime runtime, string? line, bool isError)
{
if (string.IsNullOrWhiteSpace(line))
@@ -38,12 +44,74 @@ public sealed partial class FfmpegService
runtime.MarkStartupFailure(StartupFailureKind.StreamHandshake, line);
}
+ if (TryClassifyPersistedFfmpegLine(line, isError, out var level))
+ {
+ await PersistFfmpegLineAsync(runtime, line, level);
+ }
+
+ if (IsRuntimeSourceFailureLine(line) &&
+ runtime.RegisterRuntimeSourceFailure(DateTimeOffset.UtcNow, RuntimeSourceFailureWindow))
+ {
+ _ = ValidateRuntimeSourceFailureAsync(runtime, line);
+ }
+ }
+
+ private static bool TryClassifyPersistedFfmpegLine(string line, bool isError, out SystemLogLevel level)
+ {
+ if (IsRecoverableFfmpegWarningLine(line))
+ {
+ level = SystemLogLevel.Warning;
+ return true;
+ }
+
if (line.Contains("error", StringComparison.OrdinalIgnoreCase) ||
line.Contains("fail", StringComparison.OrdinalIgnoreCase) ||
line.Contains("timed out", StringComparison.OrdinalIgnoreCase))
{
- await PersistFfmpegLineAsync(runtime, line, isError ? SystemLogLevel.Error : SystemLogLevel.Warning);
+ level = isError ? SystemLogLevel.Error : SystemLogLevel.Warning;
+ return true;
}
+
+ level = default;
+ return false;
+ }
+
+ private static bool IsRecoverableFfmpegWarningLine(string line)
+ {
+ return line.Contains("Will reconnect at", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("keepalive request failed", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("retrying with new connection", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("IO error: Connection reset by peer", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Error in the push function", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Network is unreachable", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("HTTP error 404", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("404 Not Found", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to open segment", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to resolve hostname", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("error=Connection reset by peer", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("error=End of file", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Concatenated FLV detected", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("might fail to demux, decode and seek", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to parse temporal unit", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to read unit", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool IsRuntimeSourceFailureLine(string line)
+ {
+ if (line.Contains("Will reconnect at", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("retrying with new connection", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return line.Contains("HTTP error 404", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("404 Not Found", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Input/output error", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to open segment", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Network is unreachable", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Failed to resolve hostname", StringComparison.OrdinalIgnoreCase) ||
+ line.Contains("Connection to tcp://", StringComparison.OrdinalIgnoreCase) &&
+ line.Contains("failed", StringComparison.OrdinalIgnoreCase);
}
private async Task PersistFfmpegLineAsync(SessionProcessRuntime runtime, string line, SystemLogLevel level)
@@ -67,14 +135,109 @@ public sealed partial class FfmpegService
}
}
+ private async Task ValidateRuntimeSourceFailureAsync(SessionProcessRuntime runtime, string failureLine)
+ {
+ try
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var adapterFactory = scope.ServiceProvider.GetRequiredService();
+ var liveRoomStatusService = scope.ServiceProvider.GetRequiredService();
+ var logService = scope.ServiceProvider.GetRequiredService();
+
+ var session = await dbContext.RecordSessions
+ .Include(item => item.LiveRoom)
+ .Include(item => item.RecordTasks)
+ .FirstOrDefaultAsync(item => item.Id == runtime.RecordSessionId);
+ if (session?.LiveRoom is null || !IsActiveSessionStatus(session.Status) || !_processes.ContainsKey(session.Id))
+ {
+ return;
+ }
+
+ var observedAt = DateTimeOffset.UtcNow;
+ var adapter = adapterFactory.GetByPlatform(session.LiveRoom.Platform);
+ var liveStatus = await adapter.GetLiveStatusAsync(session.LiveRoom.RoomId);
+ await liveRoomStatusService.ApplySnapshotAsync(session.LiveRoom, liveStatus, observedAt);
+ await dbContext.SaveChangesAsync();
+
+ if (liveStatus.IsLive)
+ {
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "Repeated stream errors were detected, but the live room is still online.",
+ failureLine,
+ session.LiveRoomId,
+ session.Id,
+ runtime.CurrentTaskId);
+ return;
+ }
+
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "Repeated stream errors indicate the live room is offline. Completing the active recording session.",
+ failureLine,
+ session.LiveRoomId,
+ session.Id,
+ runtime.CurrentTaskId);
+
+ var stopped = await StopAndWaitAsync(
+ session.Id,
+ markAsCompletedOnExit: true,
+ RuntimeOfflineVerificationStopTimeout,
+ CancellationToken.None);
+ if (!stopped)
+ {
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "The recorder did not stop gracefully after offline verification. Force killing the ffmpeg process.",
+ failureLine,
+ session.LiveRoomId,
+ session.Id,
+ runtime.CurrentTaskId);
+
+ var killed = await KillAndWaitAsync(
+ session.Id,
+ RuntimeOfflineVerificationKillTimeout,
+ CancellationToken.None);
+ if (!killed)
+ {
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "The recorder did not exit before the forced-stop wait expired; the session will be finalized when the process exits.",
+ failureLine,
+ session.LiveRoomId,
+ session.Id,
+ runtime.CurrentTaskId);
+ return;
+ }
+ }
+
+ await TryReconcileInactiveSessionAsync(session.Id, CancellationToken.None);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Runtime source failure verification failed for session {RecordSessionId}", runtime.RecordSessionId);
+ }
+ finally
+ {
+ runtime.CompleteRuntimeSourceFailureVerification();
+ }
+ }
+
private async Task HandleSegmentOpenedAsync(SessionProcessRuntime runtime, string openedPath)
{
await runtime.Gate.WaitAsync();
try
{
+ var normalizedOpenedPath = NormalizeAbsolutePath(openedPath);
using var scope = _serviceScopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService();
var session = await dbContext.RecordSessions
+ .Include(item => item.LiveRoom)
.Include(item => item.RecordTasks)
.ThenInclude(item => item.Result)
.FirstOrDefaultAsync(item => item.Id == runtime.RecordSessionId);
@@ -99,6 +262,7 @@ public sealed partial class FfmpegService
session.AttachProcess(runtime.ProcessId, now);
session.MarkRunning(now);
session.ActivateSegment(1, now);
+ runtime.ResetCurrentRecorderSegmentPaths(normalizedOpenedPath);
if (!runtime.HasInitializedDanmaku)
{
await EnsureDanmakuSegmentAsync(runtime, currentTask, now);
@@ -109,9 +273,18 @@ public sealed partial class FfmpegService
return;
}
- var segmentIndex = string.Equals(openedPath, runtime.CurrentOutputFilePath, StringComparison.OrdinalIgnoreCase)
- ? Math.Max(1, runtime.CurrentSegmentIndex)
- : Math.Max(1, runtime.CurrentSegmentIndex + 1);
+ var detectedSegmentIndex = ExtractSegmentIndex(openedPath);
+ var segmentIndex = !runtime.HasOpenedFirstSegment
+ ? Math.Max(1, detectedSegmentIndex ?? runtime.CurrentSegmentIndex)
+ : Math.Max(
+ 1,
+ detectedSegmentIndex ??
+ (string.Equals(
+ NormalizeAbsolutePath(openedPath),
+ NormalizeAbsolutePath(runtime.CurrentOutputFilePath),
+ StringComparison.OrdinalIgnoreCase)
+ ? runtime.CurrentSegmentIndex
+ : runtime.CurrentSegmentIndex + 1));
if (!runtime.HasInitializedDanmaku)
{
await EnsureDanmakuSegmentAsync(runtime, currentTask, now);
@@ -121,20 +294,43 @@ public sealed partial class FfmpegService
if (segmentIndex == runtime.CurrentSegmentIndex)
{
runtime.HasOpenedFirstSegment = true;
- currentTask.MarkStarting(runtime.StreamUrl, openedPath, currentTask.StartedAt ?? now);
+ var finalOutputPath = NormalizeAbsolutePath(
+ currentTask.OutputFilePath ??
+ ResolveSegmentOutputPath(session.OutputPathPattern ?? runtime.OutputPathPattern, session.SaveMode, segmentIndex));
+ currentTask.MarkStarting(runtime.StreamUrl, finalOutputPath, currentTask.StartedAt ?? now);
currentTask.AttachProcess(runtime.ProcessId, now);
currentTask.MarkRunning(now);
session.AttachProcess(runtime.ProcessId, now);
session.MarkRunning(now);
session.ActivateSegment(segmentIndex, now);
- runtime.CurrentOutputFilePath = openedPath;
+ runtime.CurrentOutputFilePath = normalizedOpenedPath;
+ runtime.TrackCurrentRecorderSegment(normalizedOpenedPath);
await dbContext.SaveChangesAsync();
return;
}
var previousTask = currentTask;
var previousTaskId = previousTask.Id;
- var previousOutputPath = runtime.CurrentOutputFilePath;
+ var previousDurationSeconds = previousTask.StartedAt.HasValue
+ ? Math.Max(0, (now - previousTask.StartedAt.Value).TotalSeconds)
+ : (double?)null;
+ var previousEffectiveOutputPath = previousTask.OutputFilePath ??
+ NormalizeAbsolutePath(ResolveSegmentOutputPath(session.OutputPathPattern ?? runtime.OutputPathPattern, session.SaveMode, previousTask.SegmentIndex));
+
+ if (ShouldTreatAsUnexpectedFragmentRollover(runtime, previousDurationSeconds))
+ {
+ runtime.CurrentOutputFilePath = normalizedOpenedPath;
+ runtime.TrackCurrentRecorderSegment(normalizedOpenedPath);
+ currentTask.AttachProcess(runtime.ProcessId, now);
+ currentTask.MarkRunning(now);
+ session.AttachProcess(runtime.ProcessId, now);
+ session.MarkRunning(now);
+ session.ActivateSegment(runtime.CurrentSegmentIndex, now);
+ await dbContext.SaveChangesAsync();
+ return;
+ }
+
+ var previousRecorderSegmentPaths = runtime.CaptureCurrentRecorderSegments();
var newTask = new RecordTask(
session.LiveRoomId,
@@ -143,14 +339,22 @@ public sealed partial class FfmpegService
session.PreferredQuality,
session.OutputFormat,
now);
- newTask.MarkStarting(runtime.StreamUrl, openedPath, now);
+ var newTaskOutputPath = NormalizeAbsolutePath(
+ ResolveSegmentOutputPath(session.OutputPathPattern ?? runtime.OutputPathPattern, session.SaveMode, segmentIndex));
+ newTask.MarkStarting(runtime.StreamUrl, newTaskOutputPath, now);
newTask.AttachProcess(runtime.ProcessId, now);
newTask.MarkRunning(now);
await dbContext.RecordTasks.AddAsync(newTask);
- previousTask.MarkCompleted(
- now,
- previousTask.StartedAt.HasValue ? Math.Max(0, (now - previousTask.StartedAt.Value).TotalSeconds) : null);
+ if (session.OutputFormat == RecordOutputFormat.Mp4)
+ {
+ previousTask.MarkProcessing("MP4 finalization queued.", now);
+ }
+ else
+ {
+ previousTask.MarkCompleted(now, previousDurationSeconds);
+ }
+
previousTask.DetachProcess(now);
session.AttachProcess(runtime.ProcessId, now);
@@ -159,28 +363,62 @@ public sealed partial class FfmpegService
await dbContext.SaveChangesAsync();
+ runtime.CurrentTaskId = newTask.Id;
+ runtime.CurrentSegmentIndex = segmentIndex;
+ runtime.CurrentOutputFilePath = normalizedOpenedPath;
+ runtime.ResetCurrentRecorderSegmentPaths(normalizedOpenedPath);
+ runtime.HasOpenedFirstSegment = true;
+
if (runtime.DanmakuRecorder is not null)
{
- await runtime.DanmakuRecorder.StartSegmentAsync(newTask.Id, segmentIndex, openedPath, now);
+ await runtime.DanmakuRecorder.StartSegmentAsync(
+ newTask.Id,
+ segmentIndex,
+ NormalizeAbsolutePath(newTask.OutputFilePath ?? newTaskOutputPath),
+ now);
}
var previousDanmakuSummary = runtime.DanmakuRecorder?.TakeSummary(previousTaskId);
- UpsertRecordResult(
- previousTask,
- dbContext,
- previousOutputPath,
- CalculateFileSize(previousOutputPath),
- previousTask.DurationSeconds,
- previousDanmakuSummary?.FilePath,
- previousDanmakuSummary?.MessageCount ?? 0,
- now);
+ if (session.OutputFormat == RecordOutputFormat.Mp4)
+ {
+ SetPostProcessState(
+ session.Id,
+ previousTaskId,
+ "Queued",
+ 0,
+ $"Waiting for {Path.GetFileName(GetRecorderOutputPath(previousEffectiveOutputPath, session.OutputFormat, session.SaveMode))} to finish writing");
+ _ = FinalizeCompletedSegmentAsync(
+ session.Id,
+ previousTaskId,
+ previousDurationSeconds,
+ now,
+ previousDanmakuSummary,
+ previousRecorderSegmentPaths);
+ }
+ else
+ {
+ await UpsertRecordResultAsync(
+ previousTask,
+ dbContext,
+ previousEffectiveOutputPath,
+ CalculateFileSize(previousEffectiveOutputPath),
+ previousTask.DurationSeconds,
+ previousDanmakuSummary?.FilePath,
+ previousDanmakuSummary?.MessageCount ?? 0,
+ now);
- await dbContext.SaveChangesAsync();
-
- runtime.CurrentTaskId = newTask.Id;
- runtime.CurrentSegmentIndex = segmentIndex;
- runtime.CurrentOutputFilePath = openedPath;
- runtime.HasOpenedFirstSegment = true;
+ await dbContext.SaveChangesAsync();
+ ClearPostProcessState(previousTask.Id);
+ var previousResult = await LoadRecordResultAsync(dbContext, previousTask.Id);
+ var eventScriptService = scope.ServiceProvider.GetRequiredService();
+ await eventScriptService.RunSegmentCompletedAsync(
+ session.LiveRoom,
+ session,
+ previousTask,
+ previousResult,
+ previousEffectiveOutputPath,
+ now);
+ }
}
catch (Exception ex)
{
@@ -303,7 +541,8 @@ public sealed partial class FfmpegService
runtime.SelectedProtocol,
runtime.StreamUrl,
runtime.InputHeaders,
- Array.Empty());
+ Array.Empty(),
+ runtime.SelectedVideoCodec);
session.MarkStarting(retryStream.SelectedUrl, session.OutputPathPattern ?? runtime.OutputPathPattern, observedAt);
session.ActivateSegment(Math.Max(1, runtime.CurrentSegmentIndex), observedAt);
@@ -314,6 +553,7 @@ public sealed partial class FfmpegService
session,
currentTask,
retryStream,
+ runtime.RecordingSettings,
FfmpegInputOptionProfile.Minimal,
hasRetriedWithCompatibilityProfile: true,
hasRetriedWithRefreshedStream: runtime.HasRetriedWithRefreshedStream,
@@ -371,6 +611,7 @@ public sealed partial class FfmpegService
session,
currentTask,
refreshedStream,
+ runtime.RecordingSettings,
runtime.InputOptionProfile,
hasRetriedWithCompatibilityProfile: runtime.HasRetriedWithCompatibilityProfile,
hasRetriedWithRefreshedStream: true,
@@ -430,39 +671,67 @@ public sealed partial class FfmpegService
var endedAt = DateTimeOffset.UtcNow;
string? finalizationError = null;
- var effectiveOutputPath = runtime.CurrentOutputFilePath;
-
- if (session.SaveMode == RecordSaveMode.SingleFile &&
- session.OutputFormat == RecordOutputFormat.Mp4 &&
- !string.IsNullOrWhiteSpace(session.OutputPathPattern))
- {
- var finalOutputPath = Path.IsPathRooted(session.OutputPathPattern)
- ? session.OutputPathPattern
- : Path.GetFullPath(session.OutputPathPattern, AppContext.BaseDirectory);
- var finalizationResult = await TryFinalizeMp4Async(settings.FfmpegPath, runtime.RecorderOutputPath, finalOutputPath);
- effectiveOutputPath = finalizationResult.OutputPath;
- finalizationError = finalizationResult.ErrorMessage;
- }
-
- var fileSize = CalculateFileSize(effectiveOutputPath);
+ var effectiveOutputPath = currentTask.OutputFilePath ?? runtime.CurrentOutputFilePath;
var durationSeconds = currentTask.StartedAt.HasValue
? (double?)Math.Max(0, (endedAt - currentTask.StartedAt.Value).TotalSeconds)
: null;
- var danmakuPath = activeDanmakuSummary?.FilePath ?? currentTask.Result?.DanmakuFilePath ?? GuessDanmakuPath(currentTask.OutputFilePath);
+
+ if (session.OutputFormat == RecordOutputFormat.Mp4)
+ {
+ var recorderOutputPath = NormalizeAbsolutePath(GetRecorderOutputPath(
+ currentTask.OutputFilePath ?? runtime.CurrentOutputFilePath,
+ session.OutputFormat,
+ session.SaveMode));
+
+ if (runtime.StopRequested && runtime.WasForceKilled)
+ {
+ effectiveOutputPath = File.Exists(recorderOutputPath)
+ ? recorderOutputPath
+ : effectiveOutputPath;
+ finalizationError = "The active segment did not close gracefully before the recorder process was terminated. The intermediate TS file was kept without MP4 finalization.";
+ }
+ else
+ {
+ var recorderSegmentPaths = runtime.CaptureCurrentRecorderSegments();
+ await WaitForFileToStabilizeAsync(recorderOutputPath, CancellationToken.None);
+ var finalizationResult = await TryFinalizeTaskOutputAsync(
+ settings.FfmpegPath,
+ settings.MaxConcurrentFfmpegTranscodeTasks,
+ settings.Mp4FinalizeTimeoutMinutes,
+ session,
+ currentTask,
+ durationSeconds,
+ recorderSegmentPaths);
+ effectiveOutputPath = finalizationResult.OutputPath;
+ finalizationError = finalizationResult.ErrorMessage;
+ }
+ }
+
+ var fileSize = CalculateFileSize(effectiveOutputPath);
+ var hasUsableOutput = HasUsableOutput(effectiveOutputPath, fileSize);
+ var toleratedNonZeroExit = false;
+ var danmakuPath = activeDanmakuSummary?.FilePath ?? currentTask.Result?.DanmakuFilePath ?? GuessDanmakuPath(effectiveOutputPath);
var danmakuMessageCount = activeDanmakuSummary?.MessageCount ?? CountDanmakuMessages(danmakuPath);
- if (!string.IsNullOrWhiteSpace(finalizationError))
+ if (IsLowStoragePauseError(finalizationError))
+ {
+ currentTask.MarkProcessing(finalizationError, endedAt);
+ session.MarkStopped(endedAt, finalizationError);
+ }
+ else if (runtime.StopRequested)
+ {
+ currentTask.MarkStopped(endedAt, durationSeconds, finalizationError);
+ session.MarkStopped(endedAt, finalizationError);
+ }
+ else if (!string.IsNullOrWhiteSpace(finalizationError))
{
currentTask.MarkFailed(finalizationError, endedAt);
session.MarkFailed(finalizationError, endedAt);
}
- else if (runtime.StopRequested)
- {
- currentTask.MarkStopped(endedAt, durationSeconds);
- session.MarkStopped(endedAt);
- }
- else if (process.ExitCode == 0 && (runtime.CompletionRequested || !runtime.StopRequested))
+ else if ((process.ExitCode == 0 || runtime.CompletionRequested && hasUsableOutput) &&
+ (runtime.CompletionRequested || !runtime.StopRequested))
{
+ toleratedNonZeroExit = process.ExitCode != 0;
currentTask.MarkCompleted(endedAt, durationSeconds);
session.MarkCompleted(endedAt);
}
@@ -475,9 +744,23 @@ public sealed partial class FfmpegService
currentTask.DetachProcess(endedAt);
session.SyncSegmentCount(session.RecordTasks.Count, endedAt);
- UpsertRecordResult(currentTask, dbContext, effectiveOutputPath, fileSize, durationSeconds, danmakuPath, danmakuMessageCount, endedAt);
+ await UpsertRecordResultAsync(currentTask, dbContext, effectiveOutputPath, fileSize, durationSeconds, danmakuPath, danmakuMessageCount, endedAt);
await dbContext.SaveChangesAsync();
+ ClearPostProcessState(currentTask.Id);
+
+ if (currentTask.Status == RecordTaskStatus.Completed)
+ {
+ var recordResult = await LoadRecordResultAsync(dbContext, currentTask.Id);
+ var eventScriptService = scope.ServiceProvider.GetRequiredService();
+ await eventScriptService.RunSegmentCompletedAsync(
+ session.LiveRoom,
+ session,
+ currentTask,
+ recordResult,
+ effectiveOutputPath,
+ endedAt);
+ }
await logService.WriteAsync(
session.Status == RecordSessionStatus.Completed ? SystemLogLevel.Info :
@@ -485,7 +768,9 @@ public sealed partial class FfmpegService
SystemLogLevel.Error,
"FFmpeg",
$"Recording session exited with status={session.Status}.",
- $"exitCode={process.ExitCode}; output={effectiveOutputPath}; recorderOutput={runtime.RecorderOutputPath}",
+ toleratedNonZeroExit
+ ? $"exitCode={process.ExitCode}; output={effectiveOutputPath}; recorderOutput={runtime.RecorderOutputPath}; non-zero exit was tolerated because completion was requested and the output is usable"
+ : $"exitCode={process.ExitCode}; output={effectiveOutputPath}; recorderOutput={runtime.RecorderOutputPath}",
session.LiveRoomId,
session.Id,
currentTask.Id);
@@ -501,6 +786,155 @@ public sealed partial class FfmpegService
}
}
+ private async Task FinalizeCompletedSegmentAsync(
+ Guid recordSessionId,
+ Guid recordTaskId,
+ double? durationSeconds,
+ DateTimeOffset endedAt,
+ SessionDanmakuXmlRecorder.DanmakuSegmentSummary? danmakuSummary,
+ IReadOnlyList? recorderSegmentPaths)
+ {
+ try
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var settingsService = scope.ServiceProvider.GetRequiredService();
+ var logService = scope.ServiceProvider.GetRequiredService();
+ var settings = await settingsService.GetAsync();
+
+ var session = await dbContext.RecordSessions
+ .Include(item => item.LiveRoom)
+ .Include(item => item.RecordTasks)
+ .ThenInclude(item => item.Result)
+ .FirstOrDefaultAsync(item => item.Id == recordSessionId);
+
+ if (session is null)
+ {
+ return;
+ }
+
+ var recordTask = session.RecordTasks.FirstOrDefault(item => item.Id == recordTaskId);
+ if (recordTask is null)
+ {
+ return;
+ }
+
+ var effectiveOutputPath = recordTask.OutputFilePath;
+ if (string.IsNullOrWhiteSpace(effectiveOutputPath))
+ {
+ if (string.IsNullOrWhiteSpace(session.OutputPathPattern))
+ {
+ return;
+ }
+
+ effectiveOutputPath = NormalizeAbsolutePath(ResolveSegmentOutputPath(
+ session.OutputPathPattern,
+ session.SaveMode,
+ recordTask.SegmentIndex));
+ }
+
+ await WaitForFileToStabilizeAsync(
+ GetRecorderOutputPath(effectiveOutputPath, session.OutputFormat, session.SaveMode),
+ CancellationToken.None);
+
+ var finalizationResult = await TryFinalizeTaskOutputAsync(
+ settings.FfmpegPath,
+ settings.MaxConcurrentFfmpegTranscodeTasks,
+ settings.Mp4FinalizeTimeoutMinutes,
+ session,
+ recordTask,
+ durationSeconds,
+ recorderSegmentPaths);
+ effectiveOutputPath = finalizationResult.OutputPath;
+ var finalizationError = finalizationResult.ErrorMessage;
+ var fileSize = CalculateFileSize(effectiveOutputPath);
+ var danmakuPath = danmakuSummary?.FilePath ?? recordTask.Result?.DanmakuFilePath ?? GuessDanmakuPath(recordTask.OutputFilePath);
+ var danmakuMessageCount = danmakuSummary?.MessageCount ?? recordTask.Result?.DanmakuMessageCount ?? CountDanmakuMessages(danmakuPath);
+
+ if (IsLowStoragePauseError(finalizationError))
+ {
+ recordTask.MarkProcessing(finalizationError, endedAt);
+ }
+ else if (string.IsNullOrWhiteSpace(finalizationError))
+ {
+ recordTask.MarkCompleted(endedAt, durationSeconds);
+ }
+ else
+ {
+ recordTask.MarkFailed(finalizationError, endedAt);
+ }
+
+ await UpsertRecordResultAsync(
+ recordTask,
+ dbContext,
+ effectiveOutputPath,
+ fileSize,
+ durationSeconds,
+ danmakuPath,
+ danmakuMessageCount,
+ endedAt);
+
+ await dbContext.SaveChangesAsync();
+
+ if (recordTask.Status == RecordTaskStatus.Completed)
+ {
+ var recordResult = await LoadRecordResultAsync(dbContext, recordTask.Id);
+ var eventScriptService = scope.ServiceProvider.GetRequiredService();
+ await eventScriptService.RunSegmentCompletedAsync(
+ session.LiveRoom,
+ session,
+ recordTask,
+ recordResult,
+ effectiveOutputPath,
+ endedAt);
+ }
+
+ if (!string.IsNullOrWhiteSpace(finalizationError))
+ {
+ await logService.WriteAsync(
+ IsLowStoragePauseError(finalizationError) ? SystemLogLevel.Warning : SystemLogLevel.Warning,
+ "FFmpeg",
+ IsLowStoragePauseError(finalizationError)
+ ? "Segment MP4 finalization paused because storage is below threshold."
+ : "Segment MP4 finalization failed after rollover.",
+ finalizationError,
+ session.LiveRoomId,
+ session.Id,
+ recordTask.Id);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Finalize completed segment failed for session {RecordSessionId}, task {RecordTaskId}", recordSessionId, recordTaskId);
+
+ try
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var logService = scope.ServiceProvider.GetRequiredService();
+ var session = await dbContext.RecordSessions
+ .AsNoTracking()
+ .FirstOrDefaultAsync(item => item.Id == recordSessionId);
+ await logService.WriteAsync(
+ SystemLogLevel.Error,
+ "FFmpeg",
+ "Background segment finalization failed.",
+ ex.ToString(),
+ session?.LiveRoomId,
+ recordSessionId,
+ recordTaskId);
+ }
+ catch (Exception logEx)
+ {
+ _logger.LogWarning(logEx, "Persist segment finalization failure log failed for session {RecordSessionId}, task {RecordTaskId}", recordSessionId, recordTaskId);
+ }
+ }
+ finally
+ {
+ ClearPostProcessState(recordTaskId);
+ }
+ }
+
private async Task PersistProcessBindingAsync(Guid recordSessionId, Guid recordTaskId, int processId, CancellationToken cancellationToken)
{
using var scope = _serviceScopeFactory.CreateScope();
@@ -523,15 +957,14 @@ public sealed partial class FfmpegService
{
LiveRoom? liveRoom = null;
var includeNonChatEvents = true;
+ var firstWrittenEventLogged = 0;
using (var readScope = _serviceScopeFactory.CreateScope())
{
var dbContext = readScope.ServiceProvider.GetRequiredService();
var adapterFactory = readScope.ServiceProvider.GetRequiredService();
- var settingsService = readScope.ServiceProvider.GetRequiredService();
- var settings = await settingsService.GetAsync(cancellationToken);
- if (!settings.EnableDanmakuRecording)
+ if (!runtime.RecordingSettings.EnableDanmakuRecording)
{
return;
}
@@ -544,7 +977,7 @@ public sealed partial class FfmpegService
return;
}
- includeNonChatEvents = settings.DanmakuIncludeNonChatEvents;
+ includeNonChatEvents = runtime.RecordingSettings.DanmakuIncludeNonChatEvents;
var adapter = adapterFactory.GetByPlatform(liveRoom.Platform);
runtime.DanmakuConnection = await adapter.ConnectAsync(
new DanmakuConnectionContext(
@@ -555,8 +988,8 @@ public sealed partial class FfmpegService
liveRoom.AnchorName,
liveRoom.Title,
liveRoom.SourceUrl,
- settings.DanmakuMinPollIntervalMilliseconds,
- settings.DanmakuRetryDelayMaxSeconds),
+ runtime.RecordingSettings.DanmakuMinPollIntervalMilliseconds,
+ runtime.RecordingSettings.DanmakuRetryDelayMaxSeconds),
cancellationToken);
}
@@ -568,40 +1001,70 @@ public sealed partial class FfmpegService
await runtime.DanmakuRecorder.StartSegmentAsync(
initialTask.Id,
initialTask.SegmentIndex,
- initialTask.OutputFilePath ?? runtime.OutputPathPattern,
+ NormalizeAbsolutePath(initialTask.OutputFilePath ?? runtime.CurrentOutputFilePath),
initialTask.StartedAt ?? DateTimeOffset.UtcNow);
runtime.HasInitializedDanmaku = true;
runtime.DanmakuPumpTask = Task.Run(
- () => runtime.DanmakuConnection.StartAsync(
- danmakuEvent =>
+ async () =>
+ {
+ try
{
- if (runtime.DanmakuRecorder is null)
- {
- return Task.CompletedTask;
- }
+ await runtime.DanmakuConnection.StartAsync(
+ async danmakuEvent =>
+ {
+ if (runtime.DanmakuRecorder is null)
+ {
+ return;
+ }
- if (!includeNonChatEvents &&
- !string.Equals(danmakuEvent.Type, "chat", StringComparison.OrdinalIgnoreCase))
- {
- return Task.CompletedTask;
- }
+ if (!includeNonChatEvents &&
+ !string.Equals(danmakuEvent.Type, "chat", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
- return runtime.DanmakuRecorder.AppendAsync(danmakuEvent);
- },
- runtime.DanmakuCancellation.Token),
+ await runtime.DanmakuRecorder.AppendAsync(danmakuEvent);
+ if (Interlocked.Exchange(ref firstWrittenEventLogged, 1) == 0)
+ {
+ await WriteDanmakuSystemLogAsync(
+ SystemLogLevel.Info,
+ "First danmaku event was written to XML.",
+ $"type={danmakuEvent.Type}; user={TruncateDanmakuPreview(danmakuEvent.User, 80)}; content={TruncateDanmakuPreview(danmakuEvent.Content, 160)}",
+ runtime.LiveRoomId,
+ runtime.RecordSessionId,
+ initialTask.Id,
+ runtime.DanmakuCancellation.Token);
+ }
+ },
+ runtime.DanmakuCancellation.Token);
+ }
+ catch (OperationCanceledException) when (runtime.DanmakuCancellation.IsCancellationRequested)
+ {
+ }
+ catch (Exception ex)
+ {
+ await WriteDanmakuSystemLogAsync(
+ SystemLogLevel.Warning,
+ "Danmaku pump stopped unexpectedly.",
+ ex.ToString(),
+ runtime.LiveRoomId,
+ runtime.RecordSessionId,
+ runtime.CurrentTaskId,
+ CancellationToken.None);
+ throw;
+ }
+ },
runtime.DanmakuCancellation.Token);
- using var logScope = _serviceScopeFactory.CreateScope();
- var logService = logScope.ServiceProvider.GetRequiredService();
- await logService.WriteAsync(
+ await WriteDanmakuSystemLogAsync(
SystemLogLevel.Info,
- "Danmaku",
"Danmaku capture started for the recording session.",
- liveRoomId: liveRoom.Id,
- recordSessionId: runtime.RecordSessionId,
- recordTaskId: initialTask.Id,
- cancellationToken: cancellationToken);
+ null,
+ liveRoom.Id,
+ runtime.RecordSessionId,
+ initialTask.Id,
+ cancellationToken);
}
catch (Exception ex)
{
@@ -609,11 +1072,8 @@ public sealed partial class FfmpegService
try
{
- using var scope = _serviceScopeFactory.CreateScope();
- var logService = scope.ServiceProvider.GetRequiredService();
- await logService.WriteAsync(
+ await WriteDanmakuSystemLogAsync(
SystemLogLevel.Warning,
- "Danmaku",
"Danmaku capture startup failed. Recording will continue without live comments until retry succeeds.",
ex.ToString(),
runtime.LiveRoomId,
@@ -628,6 +1088,39 @@ public sealed partial class FfmpegService
}
}
+ private async Task WriteDanmakuSystemLogAsync(
+ SystemLogLevel level,
+ string message,
+ string? detail,
+ Guid? liveRoomId,
+ Guid? recordSessionId,
+ Guid? recordTaskId,
+ CancellationToken cancellationToken)
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var logService = scope.ServiceProvider.GetRequiredService();
+ await logService.WriteAsync(
+ level,
+ "Danmaku",
+ message,
+ detail,
+ liveRoomId,
+ recordSessionId,
+ recordTaskId,
+ cancellationToken);
+ }
+
+ private static string TruncateDanmakuPreview(string? value, int maxLength)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ return string.Empty;
+ }
+
+ var normalized = value.Trim().Replace(Environment.NewLine, " ", StringComparison.Ordinal);
+ return normalized.Length <= maxLength ? normalized : normalized[..maxLength];
+ }
+
private static bool IsRetryableStartupFailureLine(string line) =>
line.Contains("Error reading HTTP response", StringComparison.OrdinalIgnoreCase) ||
line.Contains("unexpected EOF", StringComparison.OrdinalIgnoreCase) ||
@@ -644,6 +1137,20 @@ public sealed partial class FfmpegService
line.Contains("Option not found", StringComparison.OrdinalIgnoreCase) ||
line.Contains("Unrecognized option", StringComparison.OrdinalIgnoreCase);
+ private static bool ShouldTreatAsUnexpectedFragmentRollover(
+ SessionProcessRuntime runtime,
+ double? previousDurationSeconds)
+ {
+ if (!runtime.HasOpenedFirstSegment || !previousDurationSeconds.HasValue)
+ {
+ return false;
+ }
+
+ var expectedSegmentDurationSeconds = Math.Max(60, runtime.RecordingSettings.SegmentDurationMinutes * 60);
+ var rolloverToleranceSeconds = Math.Min(30, Math.Max(5, expectedSegmentDurationSeconds * 0.1));
+ return previousDurationSeconds.Value + rolloverToleranceSeconds < expectedSegmentDurationSeconds;
+ }
+
private async Task EnsureDanmakuSegmentAsync(SessionProcessRuntime runtime, RecordTask recordTask, DateTimeOffset startedAt)
{
if (runtime.DanmakuRecorder is null)
@@ -654,10 +1161,162 @@ public sealed partial class FfmpegService
await runtime.DanmakuRecorder.StartSegmentAsync(
recordTask.Id,
recordTask.SegmentIndex,
- recordTask.OutputFilePath ?? runtime.CurrentOutputFilePath,
+ NormalizeAbsolutePath(recordTask.OutputFilePath ?? runtime.CurrentOutputFilePath),
startedAt);
}
+ private async Task RunManualFinalizeTaskAsync(Guid recordTaskId)
+ {
+ try
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var settingsService = scope.ServiceProvider.GetRequiredService();
+ var logService = scope.ServiceProvider.GetRequiredService();
+
+ var recordTask = await dbContext.RecordTasks
+ .Include(item => item.RecordSession)
+ .Include(item => item.LiveRoom)
+ .Include(item => item.Result)
+ .FirstOrDefaultAsync(item => item.Id == recordTaskId);
+
+ if (recordTask?.RecordSession is null)
+ {
+ ClearPostProcessState(recordTaskId);
+ return;
+ }
+
+ var session = recordTask.RecordSession;
+ var finalOutputPath = recordTask.OutputFilePath;
+ if (string.IsNullOrWhiteSpace(finalOutputPath))
+ {
+ ClearPostProcessState(recordTaskId);
+ return;
+ }
+
+ var recorderOutputPath = ResolveManualFinalizeSourcePath(recordTask, session);
+ if (!File.Exists(recorderOutputPath))
+ {
+ ClearPostProcessState(recordTaskId);
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "Manual MP4 finalization was skipped because the intermediate file was missing.",
+ recorderOutputPath,
+ recordTask.LiveRoomId,
+ recordTask.RecordSessionId,
+ recordTask.Id);
+ return;
+ }
+
+ await WaitForFileToStabilizeAsync(recorderOutputPath, CancellationToken.None);
+
+ var settings = await settingsService.GetAsync();
+ var endedAt = recordTask.EndedAt ?? DateTimeOffset.UtcNow;
+ var durationSeconds = recordTask.DurationSeconds ?? recordTask.Result?.DurationSeconds;
+ var finalizationResult = await TryFinalizeTaskOutputAsync(
+ settings.FfmpegPath,
+ settings.MaxConcurrentFfmpegTranscodeTasks,
+ settings.Mp4FinalizeTimeoutMinutes,
+ session,
+ recordTask,
+ durationSeconds,
+ [recorderOutputPath]);
+
+ var effectiveOutputPath = finalizationResult.OutputPath;
+ var finalizationError = finalizationResult.ErrorMessage;
+ var fileSize = CalculateFileSize(effectiveOutputPath);
+ var danmakuPath = recordTask.Result?.DanmakuFilePath ?? GuessDanmakuPath(recordTask.OutputFilePath);
+ var danmakuMessageCount = recordTask.Result?.DanmakuMessageCount ?? CountDanmakuMessages(danmakuPath);
+
+ if (IsLowStoragePauseError(finalizationError))
+ {
+ recordTask.MarkProcessing(finalizationError, endedAt);
+ }
+ else if (string.IsNullOrWhiteSpace(finalizationError))
+ {
+ if (recordTask.Status == RecordTaskStatus.Stopped)
+ {
+ recordTask.MarkStopped(endedAt, durationSeconds);
+ }
+ else
+ {
+ recordTask.MarkCompleted(endedAt, durationSeconds);
+ }
+ }
+ else if (recordTask.Status == RecordTaskStatus.Stopped)
+ {
+ recordTask.MarkStopped(endedAt, durationSeconds, finalizationError);
+ }
+ else
+ {
+ recordTask.MarkFailed(finalizationError, endedAt);
+ }
+
+ await UpsertRecordResultAsync(
+ recordTask,
+ dbContext,
+ effectiveOutputPath,
+ fileSize,
+ durationSeconds,
+ danmakuPath,
+ danmakuMessageCount,
+ endedAt);
+
+ await dbContext.SaveChangesAsync();
+
+ if (recordTask.Status == RecordTaskStatus.Completed)
+ {
+ var recordResult = await LoadRecordResultAsync(dbContext, recordTask.Id);
+ var eventScriptService = scope.ServiceProvider.GetRequiredService();
+ await eventScriptService.RunSegmentCompletedAsync(
+ recordTask.LiveRoom,
+ session,
+ recordTask,
+ recordResult,
+ effectiveOutputPath,
+ endedAt);
+ }
+
+ await logService.WriteAsync(
+ string.IsNullOrWhiteSpace(finalizationError) ? SystemLogLevel.Info : SystemLogLevel.Warning,
+ "FFmpeg",
+ string.IsNullOrWhiteSpace(finalizationError)
+ ? "Manual MP4 finalization completed."
+ : "Manual MP4 finalization finished with warnings.",
+ string.IsNullOrWhiteSpace(finalizationError)
+ ? effectiveOutputPath
+ : $"{effectiveOutputPath}; {finalizationError}",
+ recordTask.LiveRoomId,
+ recordTask.RecordSessionId,
+ recordTask.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Manual MP4 finalization failed for task {RecordTaskId}", recordTaskId);
+
+ try
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var logService = scope.ServiceProvider.GetRequiredService();
+ await logService.WriteAsync(
+ SystemLogLevel.Warning,
+ "FFmpeg",
+ "Manual MP4 finalization failed to start or complete.",
+ ex.ToString(),
+ recordTaskId: recordTaskId);
+ }
+ catch (Exception logEx)
+ {
+ _logger.LogWarning(logEx, "Persist manual MP4 finalization failure log failed for task {RecordTaskId}", recordTaskId);
+ }
+ }
+ finally
+ {
+ ClearPostProcessState(recordTaskId);
+ }
+ }
+
private sealed class SessionProcessRuntime : IDisposable
{
public SessionProcessRuntime(
@@ -673,7 +1332,9 @@ public sealed partial class FfmpegService
string currentOutputFilePath,
string selectedQuality,
string selectedProtocol,
+ string? selectedVideoCodec,
StreamInputHeaders? inputHeaders,
+ RecordingExecutionSettings recordingSettings,
FfmpegInputOptionProfile inputOptionProfile,
bool hasRetriedWithCompatibilityProfile,
bool hasRetriedWithRefreshedStream,
@@ -691,7 +1352,9 @@ public sealed partial class FfmpegService
CurrentOutputFilePath = currentOutputFilePath;
SelectedQuality = selectedQuality;
SelectedProtocol = selectedProtocol;
+ SelectedVideoCodec = selectedVideoCodec;
InputHeaders = inputHeaders;
+ RecordingSettings = recordingSettings;
InputOptionProfile = inputOptionProfile;
HasRetriedWithCompatibilityProfile = hasRetriedWithCompatibilityProfile;
HasRetriedWithRefreshedStream = hasRetriedWithRefreshedStream;
@@ -710,7 +1373,9 @@ public sealed partial class FfmpegService
public string CurrentOutputFilePath { get; set; }
public string SelectedQuality { get; }
public string SelectedProtocol { get; }
+ public string? SelectedVideoCodec { get; }
public StreamInputHeaders? InputHeaders { get; }
+ public RecordingExecutionSettings RecordingSettings { get; }
public FfmpegInputOptionProfile InputOptionProfile { get; }
public bool HasRetriedWithCompatibilityProfile { get; }
public bool HasRetriedWithRefreshedStream { get; }
@@ -718,6 +1383,7 @@ public sealed partial class FfmpegService
public int ProcessId => Process?.Id ?? 0;
public bool CompletionRequested { get; private set; }
public bool StopRequested { get; private set; }
+ public bool WasForceKilled { get; private set; }
public bool HasInitializedDanmaku { get; set; }
public bool HasOpenedFirstSegment { get; set; }
public int RetryAttemptCount { get; }
@@ -729,6 +1395,11 @@ public sealed partial class FfmpegService
public SessionDanmakuXmlRecorder? DanmakuRecorder { get; set; }
public ILiveDanmakuConnection? DanmakuConnection { get; set; }
public Task? DanmakuPumpTask { get; set; }
+ private List CurrentRecorderSegmentPaths { get; } = [];
+ private object RuntimeSourceFailureSync { get; } = new();
+ private DateTimeOffset RuntimeSourceFailureWindowStartedAt { get; set; }
+ private int RuntimeSourceFailureCount { get; set; }
+ private bool RuntimeSourceFailureVerificationInProgress { get; set; }
public void AttachProcess(Process process) => Process = process;
@@ -758,6 +1429,72 @@ public sealed partial class FfmpegService
LastStartupFailureLine = line;
}
+ public void MarkForceKilled() => WasForceKilled = true;
+
+ public void ResetCurrentRecorderSegmentPaths(string openedPath)
+ {
+ CurrentRecorderSegmentPaths.Clear();
+ CurrentRecorderSegmentPaths.Add(openedPath);
+ }
+
+ public void TrackCurrentRecorderSegment(string openedPath)
+ {
+ if (CurrentRecorderSegmentPaths.Count == 0 ||
+ !string.Equals(CurrentRecorderSegmentPaths[^1], openedPath, StringComparison.OrdinalIgnoreCase))
+ {
+ CurrentRecorderSegmentPaths.Add(openedPath);
+ }
+ }
+
+ public IReadOnlyList CaptureCurrentRecorderSegments()
+ {
+ if (CurrentRecorderSegmentPaths.Count == 0)
+ {
+ return [CurrentOutputFilePath];
+ }
+
+ return CurrentRecorderSegmentPaths.ToArray();
+ }
+
+ public bool RegisterRuntimeSourceFailure(DateTimeOffset observedAt, TimeSpan window)
+ {
+ lock (RuntimeSourceFailureSync)
+ {
+ if (RuntimeSourceFailureVerificationInProgress)
+ {
+ return false;
+ }
+
+ if (RuntimeSourceFailureWindowStartedAt == default ||
+ observedAt - RuntimeSourceFailureWindowStartedAt > window)
+ {
+ RuntimeSourceFailureWindowStartedAt = observedAt;
+ RuntimeSourceFailureCount = 0;
+ }
+
+ RuntimeSourceFailureCount++;
+ if (RuntimeSourceFailureCount < 3)
+ {
+ return false;
+ }
+
+ RuntimeSourceFailureVerificationInProgress = true;
+ RuntimeSourceFailureWindowStartedAt = observedAt;
+ RuntimeSourceFailureCount = 0;
+ return true;
+ }
+ }
+
+ public void CompleteRuntimeSourceFailureVerification()
+ {
+ lock (RuntimeSourceFailureSync)
+ {
+ RuntimeSourceFailureVerificationInProgress = false;
+ RuntimeSourceFailureWindowStartedAt = DateTimeOffset.UtcNow;
+ RuntimeSourceFailureCount = 0;
+ }
+ }
+
public void Dispose()
{
DanmakuCancellation.Dispose();
diff --git a/src/LiveRecorder.Infrastructure/Services/FfmpegService.Utils.cs b/src/LiveRecorder.Infrastructure/Services/FfmpegService.Utils.cs
index 3266e6a..316d738 100644
--- a/src/LiveRecorder.Infrastructure/Services/FfmpegService.Utils.cs
+++ b/src/LiveRecorder.Infrastructure/Services/FfmpegService.Utils.cs
@@ -1,18 +1,32 @@
-using System.Diagnostics;
+using System.Diagnostics;
+using System.Globalization;
using System.Text;
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
using LiveRecorder.Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
namespace LiveRecorder.Infrastructure.Services;
public sealed partial class FfmpegService
{
- private static async Task<(string OutputPath, string? ErrorMessage)> TryFinalizeMp4Async(
+ private const string LowStoragePauseErrorPrefix = "MP4 finalization paused because storage is below threshold.";
+ private static readonly TimeSpan Mp4FinalizeInactivityTimeout = TimeSpan.FromMinutes(10);
+ private static readonly TimeSpan Mp4FinalizePollInterval = TimeSpan.FromSeconds(1);
+
+ private async Task<(string OutputPath, string? ErrorMessage)> TryFinalizeMp4Async(
string ffmpegPath,
+ int maxConcurrentTranscodeTasks,
+ int mp4FinalizeTimeoutMinutes,
+ Guid recordSessionId,
+ Guid recordTaskId,
string sourcePath,
- string targetPath)
+ string targetPath,
+ double? expectedDurationSeconds,
+ CancellationToken cancellationToken)
{
if (!File.Exists(sourcePath))
{
@@ -28,24 +42,286 @@ public sealed partial class FfmpegService
File.Delete(tempPath);
}
- var remuxProcess = new Process
+ async Task GetLowStoragePauseMessageAsync()
{
- StartInfo = new ProcessStartInfo
+ using var storageScope = _serviceScopeFactory.CreateScope();
+ var settingsService = storageScope.ServiceProvider.GetRequiredService();
+ var settings = await settingsService.GetAsync(cancellationToken);
+ var sourceSizeBytes = new FileInfo(sourcePath).Length;
+ var storageCheck = _storageGuardService.CheckCanStartOrResume(settings, sourceSizeBytes);
+ return storageCheck.HasEnoughSpace
+ ? null
+ : $"{LowStoragePauseErrorPrefix} {storageCheck.Message}";
+ }
+
+ var lowStoragePauseMessage = await GetLowStoragePauseMessageAsync();
+ if (!string.IsNullOrWhiteSpace(lowStoragePauseMessage))
+ {
+ SetPostProcessState(recordSessionId, recordTaskId, "Paused: Low storage", null, lowStoragePauseMessage);
+ return (sourcePath, lowStoragePauseMessage);
+ }
+
+ SetPostProcessState(
+ recordSessionId,
+ recordTaskId,
+ "Queued",
+ null,
+ $"Waiting for an available ffmpeg transcode slot (max {Math.Clamp(maxConcurrentTranscodeTasks, 1, 16)}).");
+
+ using var transcodeSlot = await AcquireTranscodeSlotAsync(maxConcurrentTranscodeTasks, cancellationToken);
+
+ lowStoragePauseMessage = await GetLowStoragePauseMessageAsync();
+ if (!string.IsNullOrWhiteSpace(lowStoragePauseMessage))
+ {
+ SetPostProcessState(recordSessionId, recordTaskId, "Paused: Low storage", null, lowStoragePauseMessage);
+ return (sourcePath, lowStoragePauseMessage);
+ }
+
+ var stderrLines = new Queue();
+ var progressSync = new object();
+ double? processedSeconds = null;
+ var lastReportedWholePercent = -1;
+ var attemptStartedAt = DateTimeOffset.UtcNow;
+ var activeStage = "Finalizing MP4";
+ var maxDuration = TimeSpan.FromMinutes(Math.Clamp(mp4FinalizeTimeoutMinutes, 1, 1440));
+ long lastActivityTicks = attemptStartedAt.UtcTicks;
+
+ void TouchActivity()
+ {
+ System.Threading.Interlocked.Exchange(ref lastActivityTicks, DateTimeOffset.UtcNow.UtcTicks);
+ }
+
+ void ReportProgress(double? seconds, string? stageOverride = null)
+ {
+ lock (progressSync)
{
- FileName = ffmpegPath,
- Arguments = $"-hide_banner -y -i {Quote(sourcePath)} -c copy -movflags +faststart {Quote(tempPath)}",
- UseShellExecute = false,
- RedirectStandardError = true,
- RedirectStandardOutput = true,
- CreateNoWindow = true
+ TouchActivity();
+ if (seconds.HasValue)
+ {
+ processedSeconds = seconds;
+ }
+
+ double? progressPercent = null;
+ string? detail = null;
+
+ if (expectedDurationSeconds.HasValue && expectedDurationSeconds.Value > 0 && processedSeconds.HasValue)
+ {
+ progressPercent = Math.Clamp(processedSeconds.Value / expectedDurationSeconds.Value * 100d, 0d, 99d);
+ detail = $"Processed {processedSeconds.Value:F1}s / {expectedDurationSeconds.Value:F1}s";
+ }
+ else if (processedSeconds.HasValue)
+ {
+ detail = $"Processed {processedSeconds.Value:F1}s";
+ }
+
+ var wholePercent = progressPercent.HasValue ? (int)Math.Floor(progressPercent.Value) : -1;
+ if (stageOverride is null && wholePercent == lastReportedWholePercent)
+ {
+ return;
+ }
+
+ lastReportedWholePercent = wholePercent;
+ SetPostProcessState(
+ recordSessionId,
+ recordTaskId,
+ stageOverride ?? activeStage,
+ progressPercent,
+ detail ?? $"Optimizing MP4 index for {Path.GetFileName(targetPath)}");
}
- };
+ }
- remuxProcess.Start();
- await remuxProcess.WaitForExitAsync();
-
- if (remuxProcess.ExitCode == 0 && File.Exists(tempPath))
+ string? GetErrorDetail()
{
+ lock (stderrLines)
+ {
+ return stderrLines.Count == 0 ? null : string.Join(" | ", stderrLines);
+ }
+ }
+
+ void ClearErrorDetail()
+ {
+ lock (stderrLines)
+ {
+ stderrLines.Clear();
+ }
+ }
+
+ async Task RunFinalizeAttemptAsync(
+ Mp4FinalizeStrategy strategy,
+ string stage,
+ string detail)
+ {
+ if (File.Exists(tempPath))
+ {
+ File.Delete(tempPath);
+ }
+
+ ClearErrorDetail();
+ activeStage = stage;
+ processedSeconds = null;
+ lastReportedWholePercent = -1;
+ attemptStartedAt = DateTimeOffset.UtcNow;
+ System.Threading.Interlocked.Exchange(ref lastActivityTicks, attemptStartedAt.UtcTicks);
+
+ SetPostProcessState(recordSessionId, recordTaskId, stage, 0, detail);
+
+ using var finalizeProcess = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = ffmpegPath,
+ UseShellExecute = false,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ CreateNoWindow = true
+ }
+ };
+
+ foreach (var argument in BuildMp4FinalizeArgumentList(sourcePath, tempPath, strategy))
+ {
+ finalizeProcess.StartInfo.ArgumentList.Add(argument);
+ }
+
+ finalizeProcess.OutputDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrWhiteSpace(args.Data))
+ {
+ return;
+ }
+
+ TouchActivity();
+
+ if (TryParseFfmpegProgressSeconds(args.Data, out var seconds))
+ {
+ ReportProgress(seconds);
+ return;
+ }
+
+ if (args.Data.StartsWith("progress=", StringComparison.OrdinalIgnoreCase))
+ {
+ ReportProgress(processedSeconds);
+ }
+ };
+
+ finalizeProcess.ErrorDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrWhiteSpace(args.Data))
+ {
+ return;
+ }
+
+ TouchActivity();
+ _logger.LogDebug("ffmpeg-postprocess[{RecordTaskId}] {Line}", recordTaskId, args.Data);
+ lock (stderrLines)
+ {
+ stderrLines.Enqueue(args.Data.Trim());
+ while (stderrLines.Count > 10)
+ {
+ stderrLines.Dequeue();
+ }
+ }
+ };
+
+ try
+ {
+ finalizeProcess.Start();
+ finalizeProcess.BeginOutputReadLine();
+ finalizeProcess.BeginErrorReadLine();
+
+ while (!finalizeProcess.HasExited)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var now = DateTimeOffset.UtcNow;
+ var lastActivityAt = new DateTimeOffset(System.Threading.Interlocked.Read(ref lastActivityTicks), TimeSpan.Zero);
+ if (now - attemptStartedAt > maxDuration)
+ {
+ throw new TimeoutException($"MP4 finalization exceeded the maximum allowed duration of {maxDuration.TotalMinutes:F0} minutes.");
+ }
+
+ if (now - lastActivityAt > Mp4FinalizeInactivityTimeout)
+ {
+ throw new TimeoutException($"MP4 finalization did not report progress for more than {Mp4FinalizeInactivityTimeout.TotalMinutes:F0} minutes.");
+ }
+
+ await Task.Delay(Mp4FinalizePollInterval, cancellationToken);
+ }
+
+ await finalizeProcess.WaitForExitAsync(cancellationToken);
+ }
+ catch (TimeoutException ex)
+ {
+ try
+ {
+ if (!finalizeProcess.HasExited)
+ {
+ finalizeProcess.Kill(true);
+ }
+ }
+ catch (Exception killEx)
+ {
+ _logger.LogWarning(killEx, "Timed-out MP4 finalization process could not be killed for task {RecordTaskId}", recordTaskId);
+ }
+
+ return ex.Message;
+ }
+ catch (Exception ex)
+ {
+ try
+ {
+ if (!finalizeProcess.HasExited)
+ {
+ finalizeProcess.Kill(true);
+ }
+ }
+ catch (Exception killEx)
+ {
+ _logger.LogWarning(killEx, "Failed MP4 finalization process could not be killed for task {RecordTaskId}", recordTaskId);
+ }
+
+ return ex.Message;
+ }
+
+ if (finalizeProcess.ExitCode == 0 && File.Exists(tempPath))
+ {
+ return null;
+ }
+
+ if (File.Exists(tempPath))
+ {
+ File.Delete(tempPath);
+ }
+
+ return GetErrorDetail() ?? $"ffmpeg exited with code {finalizeProcess.ExitCode}.";
+ }
+
+ var finalizationError = await RunFinalizeAttemptAsync(
+ Mp4FinalizeStrategy.StreamCopy,
+ "Finalizing MP4",
+ $"Optimizing MP4 index for {Path.GetFileName(targetPath)}");
+
+ if (IsNoSpaceLeftError(finalizationError))
+ {
+ finalizationError = $"{LowStoragePauseErrorPrefix} {finalizationError}";
+ }
+
+ if (!string.IsNullOrWhiteSpace(finalizationError) && IsRepairableMp4FinalizeError(finalizationError))
+ {
+ var repairError = await RunFinalizeAttemptAsync(
+ Mp4FinalizeStrategy.RepairTranscode,
+ "Repairing MP4",
+ $"Repairing stream metadata for {Path.GetFileName(targetPath)}");
+
+ finalizationError = string.IsNullOrWhiteSpace(repairError)
+ ? null
+ : IsNoSpaceLeftError(repairError)
+ ? $"{LowStoragePauseErrorPrefix} {repairError}"
+ : $"{finalizationError} | fallback repair failed: {repairError}";
+ }
+
+ if (string.IsNullOrWhiteSpace(finalizationError) && File.Exists(tempPath))
+ {
+ ReportProgress(expectedDurationSeconds, "Writing MP4 index");
if (File.Exists(targetPath))
{
File.Replace(tempPath, targetPath, null, ignoreMetadataErrors: true);
@@ -60,6 +336,7 @@ public sealed partial class FfmpegService
File.Delete(sourcePath);
}
+ SetPostProcessState(recordSessionId, recordTaskId, "Completed", 100, "MP4 seek index is ready");
return (targetPath, null);
}
@@ -69,7 +346,285 @@ public sealed partial class FfmpegService
}
var fallbackPath = File.Exists(targetPath) ? targetPath : sourcePath;
- return (fallbackPath, "The MP4 file could not be finalized into a seekable output.");
+ string? errorDetail;
+ lock (stderrLines)
+ {
+ errorDetail = stderrLines.Count == 0 ? null : string.Join(" | ", stderrLines);
+ }
+
+ return (
+ fallbackPath,
+ string.IsNullOrWhiteSpace(finalizationError ?? errorDetail)
+ ? "The MP4 file could not be finalized into a seekable output."
+ : $"The MP4 file could not be finalized into a seekable output. {finalizationError ?? errorDetail}");
+ }
+
+ private async Task<(string OutputPath, string? ErrorMessage)> TryFinalizeTaskOutputAsync(
+ string ffmpegPath,
+ int maxConcurrentTranscodeTasks,
+ int mp4FinalizeTimeoutMinutes,
+ RecordSession recordSession,
+ RecordTask recordTask,
+ double? expectedDurationSeconds,
+ CancellationToken cancellationToken = default)
+ => await TryFinalizeTaskOutputAsync(
+ ffmpegPath,
+ maxConcurrentTranscodeTasks,
+ mp4FinalizeTimeoutMinutes,
+ recordSession,
+ recordTask,
+ expectedDurationSeconds,
+ recorderSegmentPaths: null,
+ cancellationToken);
+
+ private async Task<(string OutputPath, string? ErrorMessage)> TryFinalizeTaskOutputAsync(
+ string ffmpegPath,
+ int maxConcurrentTranscodeTasks,
+ int mp4FinalizeTimeoutMinutes,
+ RecordSession recordSession,
+ RecordTask recordTask,
+ double? expectedDurationSeconds,
+ IReadOnlyList? recorderSegmentPaths,
+ CancellationToken cancellationToken = default)
+ {
+ var fallbackOutputPath = recordTask.OutputFilePath ?? string.Empty;
+ if (recordSession.OutputFormat != RecordOutputFormat.Mp4)
+ {
+ return (fallbackOutputPath, null);
+ }
+
+ if (recordSession.SaveMode == RecordSaveMode.SingleFile)
+ {
+ if (string.IsNullOrWhiteSpace(recordSession.OutputPathPattern))
+ {
+ return (fallbackOutputPath, null);
+ }
+
+ var finalOutputPath = NormalizeAbsolutePath(recordSession.OutputPathPattern);
+ var recorderOutputPath = GetRecorderOutputPath(finalOutputPath, recordSession.OutputFormat, recordSession.SaveMode);
+ if (!File.Exists(recorderOutputPath))
+ {
+ return (File.Exists(finalOutputPath) ? finalOutputPath : fallbackOutputPath, null);
+ }
+
+ return await TryFinalizeMp4Async(
+ ffmpegPath,
+ maxConcurrentTranscodeTasks,
+ mp4FinalizeTimeoutMinutes,
+ recordSession.Id,
+ recordTask.Id,
+ recorderOutputPath,
+ finalOutputPath,
+ expectedDurationSeconds,
+ cancellationToken);
+ }
+
+ if (string.IsNullOrWhiteSpace(recordTask.OutputFilePath))
+ {
+ return (fallbackOutputPath, null);
+ }
+
+ var finalSegmentOutputPath = NormalizeAbsolutePath(recordTask.OutputFilePath);
+ var normalizedRecorderSegmentPaths = recorderSegmentPaths?
+ .Where(static item => !string.IsNullOrWhiteSpace(item))
+ .Select(NormalizeAbsolutePath)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Where(File.Exists)
+ .ToArray();
+
+ if (normalizedRecorderSegmentPaths is null || normalizedRecorderSegmentPaths.Length == 0)
+ {
+ normalizedRecorderSegmentPaths =
+ [
+ NormalizeAbsolutePath(
+ GetRecorderOutputPath(finalSegmentOutputPath, recordSession.OutputFormat, recordSession.SaveMode))
+ ];
+ }
+
+ normalizedRecorderSegmentPaths = normalizedRecorderSegmentPaths
+ .Where(File.Exists)
+ .ToArray();
+ if (normalizedRecorderSegmentPaths.Length == 0)
+ {
+ return (File.Exists(finalSegmentOutputPath) ? finalSegmentOutputPath : recordTask.OutputFilePath, null);
+ }
+
+ string? materializedSourcePath = null;
+ var cleanupMaterializedSource = false;
+ var preserveMaterializedSource = false;
+ try
+ {
+ materializedSourcePath = await MaterializeRecorderSegmentSourceAsync(
+ finalSegmentOutputPath,
+ normalizedRecorderSegmentPaths,
+ cancellationToken);
+ if (string.IsNullOrWhiteSpace(materializedSourcePath))
+ {
+ return (File.Exists(finalSegmentOutputPath) ? finalSegmentOutputPath : recordTask.OutputFilePath, null);
+ }
+
+ cleanupMaterializedSource =
+ !string.Equals(materializedSourcePath, normalizedRecorderSegmentPaths[0], StringComparison.OrdinalIgnoreCase);
+
+ var finalizationResult = await TryFinalizeMp4Async(
+ ffmpegPath,
+ maxConcurrentTranscodeTasks,
+ mp4FinalizeTimeoutMinutes,
+ recordSession.Id,
+ recordTask.Id,
+ materializedSourcePath,
+ finalSegmentOutputPath,
+ expectedDurationSeconds,
+ cancellationToken);
+ preserveMaterializedSource = IsLowStoragePauseError(finalizationResult.ErrorMessage);
+ return finalizationResult;
+ }
+ finally
+ {
+ if (cleanupMaterializedSource &&
+ !preserveMaterializedSource &&
+ !string.IsNullOrWhiteSpace(materializedSourcePath) &&
+ File.Exists(materializedSourcePath))
+ {
+ File.Delete(materializedSourcePath);
+ }
+ }
+ }
+
+ private static async Task MaterializeRecorderSegmentSourceAsync(
+ string finalOutputPath,
+ IReadOnlyList recorderSegmentPaths,
+ CancellationToken cancellationToken)
+ {
+ if (recorderSegmentPaths.Count == 0)
+ {
+ return null;
+ }
+
+ if (recorderSegmentPaths.Count == 1)
+ {
+ return recorderSegmentPaths[0];
+ }
+
+ var combinedPath = Path.Combine(
+ Path.GetDirectoryName(finalOutputPath)!,
+ $"{Path.GetFileNameWithoutExtension(finalOutputPath)}.concat.ts");
+
+ if (File.Exists(combinedPath))
+ {
+ File.Delete(combinedPath);
+ }
+
+ await using var outputStream = new FileStream(
+ combinedPath,
+ FileMode.CreateNew,
+ FileAccess.Write,
+ FileShare.None,
+ bufferSize: 1024 * 128,
+ useAsync: true);
+
+ foreach (var path in recorderSegmentPaths)
+ {
+ await using var inputStream = new FileStream(
+ path,
+ FileMode.Open,
+ FileAccess.Read,
+ FileShare.ReadWrite,
+ bufferSize: 1024 * 128,
+ useAsync: true);
+ await inputStream.CopyToAsync(outputStream, 1024 * 128, cancellationToken);
+ }
+
+ await outputStream.FlushAsync(cancellationToken);
+ return combinedPath;
+ }
+
+ private static IReadOnlyList BuildMp4FinalizeArgumentList(
+ string sourcePath,
+ string targetPath,
+ Mp4FinalizeStrategy strategy)
+ {
+ var arguments = new List
+ {
+ "-hide_banner",
+ "-y",
+ "-nostats",
+ "-progress",
+ "pipe:1",
+ "-analyzeduration",
+ "100M",
+ "-probesize",
+ "100M",
+ "-fflags",
+ "+genpts+igndts+discardcorrupt",
+ "-err_detect",
+ "ignore_err",
+ "-i",
+ sourcePath,
+ "-map",
+ "0:v:0",
+ "-map",
+ "0:a:0?",
+ "-dn",
+ "-sn"
+ };
+
+ if (strategy == Mp4FinalizeStrategy.RepairTranscode)
+ {
+ arguments.AddRange(
+ [
+ "-c:v", "libx264",
+ "-preset", "veryfast",
+ "-crf", "23",
+ "-c:a", "aac",
+ "-b:a", "128k"
+ ]);
+ }
+ else
+ {
+ arguments.AddRange(["-c", "copy"]);
+ }
+
+ arguments.AddRange(
+ [
+ "-movflags",
+ "+faststart",
+ "-avoid_negative_ts",
+ "make_zero",
+ targetPath
+ ]);
+
+ return arguments;
+ }
+
+ private static bool IsRepairableMp4FinalizeError(string errorDetail)
+ {
+ if (IsLowStoragePauseError(errorDetail))
+ {
+ return false;
+ }
+
+ return errorDetail.Contains("dimensions not set", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("Could not write header", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("incorrect codec parameters", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("codec parameters", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("Invalid data found when processing input", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("Error initializing output stream", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool IsLowStoragePauseError(string? errorDetail) =>
+ !string.IsNullOrWhiteSpace(errorDetail) &&
+ errorDetail.Contains(LowStoragePauseErrorPrefix, StringComparison.OrdinalIgnoreCase);
+
+ private static bool IsNoSpaceLeftError(string? errorDetail) =>
+ !string.IsNullOrWhiteSpace(errorDetail) &&
+ (errorDetail.Contains("No space left on device", StringComparison.OrdinalIgnoreCase) ||
+ errorDetail.Contains("database or disk is full", StringComparison.OrdinalIgnoreCase));
+
+ private enum Mp4FinalizeStrategy
+ {
+ StreamCopy,
+ RepairTranscode
}
private static IReadOnlyList BuildArgumentList(
@@ -83,6 +638,8 @@ public sealed partial class FfmpegService
int readWriteTimeoutMilliseconds,
int segmentDurationMinutes,
StreamInputHeaders? inputHeaders,
+ string? selectedProtocol,
+ string? selectedVideoCodec,
FfmpegInputOptionProfile inputOptionProfile)
{
var arguments = new List { "-hide_banner", "-y" };
@@ -107,7 +664,9 @@ public sealed partial class FfmpegService
}
}
- if (enableReconnect && inputOptionProfile == FfmpegInputOptionProfile.Baseline)
+ if (enableReconnect &&
+ inputOptionProfile == FfmpegInputOptionProfile.Baseline &&
+ ShouldEnableReconnect(streamUrl, selectedProtocol))
{
arguments.AddRange(
[
@@ -121,19 +680,30 @@ public sealed partial class FfmpegService
arguments.AddRange(["-rw_timeout", readWriteTimeoutMilliseconds.ToString(), "-fflags", "+discardcorrupt+genpts", "-i", streamUrl]);
arguments.AddRange(BuildCodecArguments(recordingTemplate));
+ var writesTransportStream = useIntermediateTransportStream || outputFormat == RecordOutputFormat.Ts;
+ var bitstreamFilter = ResolveTransportStreamBitstreamFilter(recordingTemplate, writesTransportStream, selectedVideoCodec);
+ if (!string.IsNullOrWhiteSpace(bitstreamFilter))
+ {
+ arguments.AddRange(["-bsf:v", bitstreamFilter]);
+ }
+
if (saveMode == RecordSaveMode.Segmented)
{
+ var segmentFormat = writesTransportStream
+ ? "mpegts"
+ : "mp4";
arguments.AddRange(
[
"-f", "segment",
"-segment_start_number", "1",
"-segment_time", Math.Max(60, segmentDurationMinutes * 60).ToString(),
+ "-break_non_keyframes", "0",
"-reset_timestamps", "1",
"-strftime", "0",
- "-segment_format", outputFormat == RecordOutputFormat.Ts ? "mpegts" : "mp4"
+ "-segment_format", segmentFormat
]);
- if (outputFormat == RecordOutputFormat.Mp4)
+ if (!useIntermediateTransportStream && outputFormat == RecordOutputFormat.Mp4)
{
arguments.AddRange(["-segment_format_options", $"movflags={BuildSegmentedMp4MovFlags()}"]);
}
@@ -173,6 +743,35 @@ public sealed partial class FfmpegService
]
};
+ private static string? ResolveTransportStreamBitstreamFilter(
+ RecordingTemplateType recordingTemplate,
+ bool writesTransportStream,
+ string? selectedVideoCodec)
+ {
+ if (!writesTransportStream || !UsesStreamCopy(recordingTemplate) || string.IsNullOrWhiteSpace(selectedVideoCodec))
+ {
+ return null;
+ }
+
+ var normalizedCodec = selectedVideoCodec.Trim();
+ if (normalizedCodec.Contains("h264", StringComparison.OrdinalIgnoreCase) ||
+ normalizedCodec.Contains("avc", StringComparison.OrdinalIgnoreCase))
+ {
+ return "h264_mp4toannexb";
+ }
+
+ if (normalizedCodec.Contains("hevc", StringComparison.OrdinalIgnoreCase) ||
+ normalizedCodec.Contains("h265", StringComparison.OrdinalIgnoreCase))
+ {
+ return "hevc_mp4toannexb";
+ }
+
+ return null;
+ }
+
+ private static bool UsesStreamCopy(RecordingTemplateType recordingTemplate) =>
+ recordingTemplate is RecordingTemplateType.StreamCopy or RecordingTemplateType.ArchiveTs;
+
private static long? CalculateFileSize(string? outputPath)
{
if (string.IsNullOrWhiteSpace(outputPath))
@@ -215,7 +814,7 @@ public sealed partial class FfmpegService
return false;
}
- private static void UpsertRecordResult(
+ private static async Task UpsertRecordResultAsync(
RecordTask recordTask,
LiveRecorderDbContext dbContext,
string? effectiveOutputPath,
@@ -223,71 +822,122 @@ public sealed partial class FfmpegService
double? durationSeconds,
string? danmakuFilePath,
int danmakuMessageCount,
- DateTimeOffset endedAt)
+ DateTimeOffset endedAt,
+ CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(effectiveOutputPath))
{
effectiveOutputPath = recordTask.OutputFilePath ?? string.Empty;
}
- if (recordTask.Result is null)
- {
- dbContext.RecordResults.Add(new RecordResult(
- recordTask.Id,
- effectiveOutputPath,
- fileSize,
- durationSeconds,
- danmakuFilePath,
- danmakuMessageCount,
- recordTask.Status,
- recordTask.ErrorMessage,
- endedAt));
- return;
- }
+ var resultId = Guid.NewGuid();
+ var normalizedDanmakuCount = Math.Max(0, danmakuMessageCount);
+ var finalStatus = (int)recordTask.Status;
- recordTask.Result.Update(
- effectiveOutputPath,
- fileSize,
- durationSeconds,
- danmakuFilePath,
- danmakuMessageCount,
- recordTask.Status,
- recordTask.ErrorMessage);
+ // Multiple background paths can reconcile the same segment after ffmpeg exits.
+ // Use SQLite's atomic upsert instead of EF Add-or-Update to avoid RecordTaskId
+ // unique constraint races between scoped DbContext instances.
+ await dbContext.Database.ExecuteSqlInterpolatedAsync($"""
+ INSERT INTO RecordResults
+ (Id, RecordTaskId, FilePath, FileSizeBytes, DurationSeconds, DanmakuFilePath, DanmakuMessageCount, FinalStatus, ErrorMessage, CreatedAt)
+ VALUES
+ ({resultId}, {recordTask.Id}, {effectiveOutputPath}, {fileSize}, {durationSeconds}, {danmakuFilePath}, {normalizedDanmakuCount}, {finalStatus}, {recordTask.ErrorMessage}, {endedAt})
+ ON CONFLICT(RecordTaskId) DO UPDATE SET
+ FilePath = excluded.FilePath,
+ FileSizeBytes = excluded.FileSizeBytes,
+ DurationSeconds = excluded.DurationSeconds,
+ DanmakuFilePath = excluded.DanmakuFilePath,
+ DanmakuMessageCount = excluded.DanmakuMessageCount,
+ FinalStatus = excluded.FinalStatus,
+ ErrorMessage = excluded.ErrorMessage;
+ """, cancellationToken);
}
+ private static Task LoadRecordResultAsync(
+ LiveRecorderDbContext dbContext,
+ Guid recordTaskId,
+ CancellationToken cancellationToken = default) =>
+ dbContext.RecordResults
+ .AsNoTracking()
+ .FirstOrDefaultAsync(item => item.RecordTaskId == recordTaskId, cancellationToken);
+
private static string BuildSingleFileMp4MovFlags() =>
"+faststart+frag_keyframe+empty_moov+default_base_moof";
private static string BuildSegmentedMp4MovFlags() =>
- "+faststart+frag_keyframe+empty_moov+default_base_moof";
+ "+faststart";
private static string GetRecorderOutputPath(
string finalOutputPath,
RecordOutputFormat outputFormat,
RecordSaveMode saveMode)
{
- if (saveMode == RecordSaveMode.SingleFile && outputFormat == RecordOutputFormat.Mp4)
+ if (outputFormat != RecordOutputFormat.Mp4)
+ {
+ return finalOutputPath;
+ }
+
+ if (saveMode == RecordSaveMode.SingleFile)
{
return Path.Combine(
Path.GetDirectoryName(finalOutputPath)!,
$"{Path.GetFileNameWithoutExtension(finalOutputPath)}.recording.ts");
}
- return finalOutputPath;
+ return Path.ChangeExtension(finalOutputPath, ".ts");
}
private static bool ShouldUseIntermediateTransportStream(
string outputPath,
RecordOutputFormat outputFormat,
RecordSaveMode saveMode) =>
- saveMode == RecordSaveMode.SingleFile &&
outputFormat == RecordOutputFormat.Mp4 &&
+ (saveMode == RecordSaveMode.SingleFile || saveMode == RecordSaveMode.Segmented) &&
outputPath.EndsWith(".ts", StringComparison.OrdinalIgnoreCase);
+ private static string ResolveSegmentOutputPath(
+ string outputPathPattern,
+ RecordSaveMode saveMode,
+ int segmentIndex)
+ {
+ if (saveMode != RecordSaveMode.Segmented)
+ {
+ return outputPathPattern;
+ }
+
+ return outputPathPattern.Replace("%05d", $"{Math.Max(1, segmentIndex):D5}", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static string ResolveRecorderSegmentOutputPath(
+ string outputPathPattern,
+ RecordOutputFormat outputFormat,
+ RecordSaveMode saveMode,
+ int segmentIndex) =>
+ NormalizeAbsolutePath(GetRecorderOutputPath(
+ ResolveSegmentOutputPath(outputPathPattern, saveMode, segmentIndex),
+ outputFormat,
+ saveMode));
+
private static bool IsHttpInput(string streamUrl) =>
streamUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
streamUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
+ private static bool ShouldEnableReconnect(string streamUrl, string? selectedProtocol)
+ {
+ if (!string.IsNullOrWhiteSpace(selectedProtocol) &&
+ selectedProtocol.Equals("hls", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return !streamUrl.Contains(".m3u8", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static string NormalizeAbsolutePath(string path) =>
+ Path.IsPathRooted(path)
+ ? path
+ : Path.GetFullPath(path, AppContext.BaseDirectory);
+
private static string? BuildCustomHeaderArgument(StreamInputHeaders? inputHeaders)
{
if (inputHeaders is null)
@@ -376,13 +1026,95 @@ public sealed partial class FfmpegService
return count;
}
+ private static async Task WaitForFileToStabilizeAsync(string? path, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return;
+ }
+
+ var absolutePath = NormalizeAbsolutePath(path);
+ if (!File.Exists(absolutePath))
+ {
+ return;
+ }
+
+ using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ timeoutCts.CancelAfter(TimeSpan.FromSeconds(4));
+
+ long? previousLength = null;
+ DateTime previousWriteTimeUtc = default;
+ var stableChecks = 0;
+
+ try
+ {
+ while (true)
+ {
+ timeoutCts.Token.ThrowIfCancellationRequested();
+
+ var fileInfo = new FileInfo(absolutePath);
+ if (!fileInfo.Exists)
+ {
+ return;
+ }
+
+ if (previousLength == fileInfo.Length && previousWriteTimeUtc == fileInfo.LastWriteTimeUtc)
+ {
+ stableChecks++;
+ if (stableChecks >= 2)
+ {
+ return;
+ }
+ }
+ else
+ {
+ previousLength = fileInfo.Length;
+ previousWriteTimeUtc = fileInfo.LastWriteTimeUtc;
+ stableChecks = 0;
+ }
+
+ await Task.Delay(250, timeoutCts.Token);
+ }
+ }
+ catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
+ {
+ }
+ }
+
private static bool IsActiveTaskStatus(RecordTaskStatus status) =>
status is RecordTaskStatus.Starting or RecordTaskStatus.Running or RecordTaskStatus.Stopping;
private static bool IsActiveSessionStatus(RecordSessionStatus status) =>
status is RecordSessionStatus.Starting or RecordSessionStatus.Running or RecordSessionStatus.Stopping;
- private static string Quote(string value) => $"\"{value.Replace("\"", "\\\"", StringComparison.Ordinal)}\"";
+ private static bool TryParseFfmpegProgressSeconds(string line, out double seconds)
+ {
+ if (line.StartsWith("out_time=", StringComparison.OrdinalIgnoreCase))
+ {
+ if (TimeSpan.TryParse(
+ line["out_time=".Length..],
+ CultureInfo.InvariantCulture,
+ out var timeSpan))
+ {
+ seconds = Math.Max(0, timeSpan.TotalSeconds);
+ return true;
+ }
+ }
+
+ if (line.StartsWith("out_time_ms=", StringComparison.OrdinalIgnoreCase) ||
+ line.StartsWith("out_time_us=", StringComparison.OrdinalIgnoreCase))
+ {
+ var raw = line[(line.IndexOf('=') + 1)..];
+ if (long.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
+ {
+ seconds = Math.Max(0, value / 1_000_000d);
+ return true;
+ }
+ }
+
+ seconds = 0;
+ return false;
+ }
private enum FfmpegInputOptionProfile
{
diff --git a/src/LiveRecorder.Infrastructure/Services/FfmpegService.cs b/src/LiveRecorder.Infrastructure/Services/FfmpegService.cs
index 38923d1..844e63f 100644
--- a/src/LiveRecorder.Infrastructure/Services/FfmpegService.cs
+++ b/src/LiveRecorder.Infrastructure/Services/FfmpegService.cs
@@ -6,6 +6,8 @@ using LiveRecorder.Application.Abstractions.Notifications;
using LiveRecorder.Application.Abstractions.Platforms;
using LiveRecorder.Application.Abstractions.Recording;
using LiveRecorder.Application.Abstractions.Settings;
+using LiveRecorder.Application.Abstractions.Storage;
+using LiveRecorder.Application.Services;
using LiveRecorder.Domain.Entities;
using LiveRecorder.Domain.Enums;
using LiveRecorder.Infrastructure.Persistence;
@@ -22,19 +24,46 @@ public sealed partial class FfmpegService : IFfmpegService
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
private readonly ConcurrentDictionary _processes = new();
+ private readonly ConcurrentDictionary _postProcessStates = new();
+ private readonly object _transcodeConcurrencyLock = new();
+ private readonly Queue> _transcodeWaiters = new();
+ private int _activeTranscodeTasks;
+ private int _maxConcurrentTranscodeTasks = 1;
private readonly IServiceScopeFactory _serviceScopeFactory;
+ private readonly IStorageGuardService _storageGuardService;
private readonly ILogger _logger;
public FfmpegService(
IServiceScopeFactory serviceScopeFactory,
+ IStorageGuardService storageGuardService,
ILogger logger)
{
_serviceScopeFactory = serviceScopeFactory;
+ _storageGuardService = storageGuardService;
_logger = logger;
}
public bool IsRunning(Guid recordSessionId) => _processes.ContainsKey(recordSessionId);
+ public IReadOnlyDictionary GetTaskRuntimeStates(IReadOnlyCollection recordTaskIds)
+ {
+ if (recordTaskIds.Count == 0)
+ {
+ return new Dictionary();
+ }
+
+ var snapshot = new Dictionary();
+ foreach (var taskId in recordTaskIds)
+ {
+ if (_postProcessStates.TryGetValue(taskId, out var state))
+ {
+ snapshot[taskId] = state.State;
+ }
+ }
+
+ return snapshot;
+ }
+
public Task CompleteAsync(Guid recordSessionId, CancellationToken cancellationToken = default) =>
RequestStopAsync(recordSessionId, markAsCompletedOnExit: true, cancellationToken);
@@ -42,11 +71,13 @@ public sealed partial class FfmpegService : IFfmpegService
RecordSession recordSession,
RecordTask initialTask,
StreamUrlResult streamUrlResult,
+ RecordingExecutionSettings recordingSettings,
CancellationToken cancellationToken = default) =>
StartInternalAsync(
recordSession,
initialTask,
streamUrlResult,
+ recordingSettings,
inputOptionProfile: FfmpegInputOptionProfile.Baseline,
hasRetriedWithCompatibilityProfile: false,
hasRetriedWithRefreshedStream: false,
@@ -57,6 +88,7 @@ public sealed partial class FfmpegService : IFfmpegService
RecordSession recordSession,
RecordTask initialTask,
StreamUrlResult streamUrlResult,
+ RecordingExecutionSettings recordingSettings,
FfmpegInputOptionProfile inputOptionProfile,
bool hasRetriedWithCompatibilityProfile,
bool hasRetriedWithRefreshedStream,
@@ -66,6 +98,7 @@ public sealed partial class FfmpegService : IFfmpegService
ArgumentNullException.ThrowIfNull(recordSession);
ArgumentNullException.ThrowIfNull(initialTask);
ArgumentNullException.ThrowIfNull(streamUrlResult);
+ ArgumentNullException.ThrowIfNull(recordingSettings);
if (string.IsNullOrWhiteSpace(streamUrlResult.SelectedUrl) || string.IsNullOrWhiteSpace(recordSession.OutputPathPattern))
{
@@ -106,10 +139,12 @@ public sealed partial class FfmpegService : IFfmpegService
recordSession.SaveMode,
initialTask.Id,
Math.Max(1, initialTask.SegmentIndex),
- initialTask.OutputFilePath ?? outputPathPattern,
+ ResolveRecorderSegmentOutputPath(outputPathPattern, recordSession.OutputFormat, recordSession.SaveMode, Math.Max(1, initialTask.SegmentIndex)),
streamUrlResult.SelectedQuality,
streamUrlResult.SelectedProtocol,
+ streamUrlResult.SelectedVideoCodec,
streamUrlResult.InputHeaders,
+ recordingSettings,
inputOptionProfile,
hasRetriedWithCompatibilityProfile,
hasRetriedWithRefreshedStream,
@@ -120,12 +155,14 @@ public sealed partial class FfmpegService : IFfmpegService
recorderOutputPath,
recordSession.OutputFormat,
recordSession.SaveMode,
- settings.RecordingTemplate,
- settings.EnableAutoReconnect,
- settings.ReconnectDelayMaxSeconds,
- settings.ReadWriteTimeoutMilliseconds,
- settings.SegmentDurationMinutes,
+ recordingSettings.RecordingTemplate,
+ recordingSettings.EnableAutoReconnect,
+ recordingSettings.ReconnectDelayMaxSeconds,
+ recordingSettings.ReadWriteTimeoutMilliseconds,
+ recordingSettings.SegmentDurationMinutes,
streamUrlResult.InputHeaders,
+ streamUrlResult.SelectedProtocol,
+ streamUrlResult.SelectedVideoCodec,
inputOptionProfile))
{
process.StartInfo.ArgumentList.Add(argument);
@@ -191,6 +228,7 @@ public sealed partial class FfmpegService : IFfmpegService
{
if (!process.HasExited)
{
+ runtime.MarkForceKilled();
process.Kill(true);
}
}
@@ -204,7 +242,7 @@ public sealed partial class FfmpegService : IFfmpegService
public async Task TryReconcileInactiveSessionAsync(Guid recordSessionId, CancellationToken cancellationToken = default)
{
- if (IsRunning(recordSessionId))
+ if (IsRunning(recordSessionId) || IsSessionUnderPostProcessing(recordSessionId))
{
return false;
}
@@ -232,37 +270,39 @@ public sealed partial class FfmpegService : IFfmpegService
.ToList();
var anyUsableOutput = false;
- string? finalizationError = null;
+ string? sessionFinalizationError = null;
foreach (var task in tasks.Where(item => IsActiveTaskStatus(item.Status)))
{
- var effectiveOutputPath = task.OutputFilePath ?? string.Empty;
- if (recordSession.SaveMode == RecordSaveMode.SingleFile &&
- recordSession.OutputFormat == RecordOutputFormat.Mp4 &&
- !string.IsNullOrWhiteSpace(recordSession.OutputPathPattern))
+ var durationSeconds = task.StartedAt.HasValue
+ ? (double?)Math.Max(0, (endedAt - task.StartedAt.Value).TotalSeconds)
+ : null;
+ var finalizationResult = await TryFinalizeTaskOutputAsync(
+ settings.FfmpegPath,
+ settings.MaxConcurrentFfmpegTranscodeTasks,
+ settings.Mp4FinalizeTimeoutMinutes,
+ recordSession,
+ task,
+ durationSeconds,
+ cancellationToken);
+ var effectiveOutputPath = finalizationResult.OutputPath;
+ var taskFinalizationError = finalizationResult.ErrorMessage;
+ if (!IsLowStoragePauseError(taskFinalizationError))
{
- var finalOutputPath = Path.IsPathRooted(recordSession.OutputPathPattern)
- ? recordSession.OutputPathPattern
- : Path.GetFullPath(recordSession.OutputPathPattern, AppContext.BaseDirectory);
- var recorderOutputPath = GetRecorderOutputPath(finalOutputPath, recordSession.OutputFormat, recordSession.SaveMode);
- if (File.Exists(recorderOutputPath))
- {
- var finalizationResult = await TryFinalizeMp4Async(settings.FfmpegPath, recorderOutputPath, finalOutputPath);
- effectiveOutputPath = finalizationResult.OutputPath;
- finalizationError ??= finalizationResult.ErrorMessage;
- }
+ sessionFinalizationError ??= taskFinalizationError;
}
var fileSize = CalculateFileSize(effectiveOutputPath);
var danmakuPath = task.Result?.DanmakuFilePath ?? GuessDanmakuPath(task.OutputFilePath);
var danmakuCount = CountDanmakuMessages(danmakuPath);
- var durationSeconds = task.StartedAt.HasValue
- ? (double?)Math.Max(0, (endedAt - task.StartedAt.Value).TotalSeconds)
- : null;
- if (!string.IsNullOrWhiteSpace(finalizationError))
+ if (IsLowStoragePauseError(taskFinalizationError))
{
- task.MarkFailed(finalizationError, endedAt);
+ task.MarkProcessing(taskFinalizationError, endedAt);
+ }
+ else if (!string.IsNullOrWhiteSpace(taskFinalizationError))
+ {
+ task.MarkFailed(taskFinalizationError, endedAt);
}
else if (HasUsableOutput(effectiveOutputPath, fileSize))
{
@@ -274,13 +314,14 @@ public sealed partial class FfmpegService : IFfmpegService
task.MarkStopped(endedAt, durationSeconds, "Recording process was no longer running when the session was reconciled.");
}
- UpsertRecordResult(task, dbContext, effectiveOutputPath, fileSize, durationSeconds, danmakuPath, danmakuCount, endedAt);
+ await UpsertRecordResultAsync(task, dbContext, effectiveOutputPath, fileSize, durationSeconds, danmakuPath, danmakuCount, endedAt, cancellationToken);
+ ClearPostProcessState(task.Id);
}
recordSession.SyncSegmentCount(tasks.Count, endedAt);
- if (!string.IsNullOrWhiteSpace(finalizationError))
+ if (!string.IsNullOrWhiteSpace(sessionFinalizationError))
{
- recordSession.MarkFailed(finalizationError, endedAt);
+ recordSession.MarkFailed(sessionFinalizationError, endedAt);
}
else if (anyUsableOutput)
{
@@ -295,6 +336,176 @@ public sealed partial class FfmpegService : IFfmpegService
return true;
}
+ public async Task StartManualFinalizeTaskAsync(Guid recordTaskId, CancellationToken cancellationToken = default)
+ {
+ if (_postProcessStates.ContainsKey(recordTaskId))
+ {
+ return false;
+ }
+
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var recordTask = await dbContext.RecordTasks
+ .Include(item => item.RecordSession)
+ .Include(item => item.Result)
+ .FirstOrDefaultAsync(item => item.Id == recordTaskId, cancellationToken);
+
+ if (recordTask?.RecordSession is null)
+ {
+ return false;
+ }
+
+ if (recordTask.OutputFormat != RecordOutputFormat.Mp4 ||
+ IsActiveTaskStatus(recordTask.Status) ||
+ IsRunning(recordTask.RecordSessionId))
+ {
+ return false;
+ }
+
+ if (string.IsNullOrWhiteSpace(recordTask.OutputFilePath))
+ {
+ return false;
+ }
+
+ var recorderOutputPath = ResolveManualFinalizeSourcePath(recordTask, recordTask.RecordSession);
+ if (!File.Exists(recorderOutputPath))
+ {
+ return false;
+ }
+
+ SetPostProcessState(
+ recordTask.RecordSessionId,
+ recordTask.Id,
+ "Queued",
+ null,
+ $"Manual MP4 finalization queued for {Path.GetFileName(recordTask.OutputFilePath)}");
+
+ _ = Task.Run(
+ async () => await RunManualFinalizeTaskAsync(recordTask.Id),
+ CancellationToken.None);
+
+ return true;
+ }
+
+ public async Task ResumePausedFinalizationsAsync(CancellationToken cancellationToken = default)
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ var settingsService = scope.ServiceProvider.GetRequiredService();
+ var logService = scope.ServiceProvider.GetRequiredService