@dan-uni/dan-any
Version:
A danmaku transformer lib, supporting danmaku from different platforms.
1,293 lines • 92.3 kB
JavaScript
import { __webpack_require__ } from "./rslib-runtime.js";
import "reflect-metadata/lite";
import { IsDate, IsEmail, IsEnum, IsInt, IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min, isEmail, isJSON, isObject, isString, validateOrReject } from "class-validator";
import { XMLBuilder, XMLParser } from "fast-xml-parser";
import json_bigint from "json-bigint";
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
import { file_google_protobuf_timestamp, timestampDate, timestampFromDate, timestampNow } from "@bufbuild/protobuf/wkt";
import { Expose, plainToInstance } from "class-transformer";
import hh_mm_ss from "hh-mm-ss";
import jssha from "jssha";
import { brotliCompressSync, brotliDecompressSync, gunzipSync, gzipSync } from "node:zlib";
import { decode, encode as external_base16384_encode } from "base16384";
import { fileDesc, messageDesc as codegenv2_messageDesc } from "@bufbuild/protobuf/codegenv2";
var dm_gen_namespaceObject = {};
__webpack_require__.r(dm_gen_namespaceObject);
__webpack_require__.d(dm_gen_namespaceObject, {
DMAttr: ()=>dm_gen_DMAttr,
ExtraDanUniChapterAction: ()=>dm_gen_ExtraDanUniChapterAction,
ExtraDanUniChapterType: ()=>dm_gen_ExtraDanUniChapterType,
Modes: ()=>dm_gen_Modes,
Pools: ()=>dm_gen_Pools,
UniDM: ()=>UniDM
});
var id_gen_namespaceObject = {};
__webpack_require__.r(id_gen_namespaceObject);
__webpack_require__.d(id_gen_namespaceObject, {
UniID: ()=>id_gen_UniID,
createDMID: ()=>createDMID
});
var platform_namespaceObject = {};
__webpack_require__.r(platform_namespaceObject);
__webpack_require__.d(platform_namespaceObject, {
PlatformDanmakuOnlySource: ()=>platform_PlatformDanmakuOnlySource,
PlatformDanmakuOnlySources: ()=>PlatformDanmakuOnlySources,
PlatformDanmakuSources: ()=>PlatformDanmakuSources,
PlatformInfoSource: ()=>platform_PlatformInfoSource,
PlatformInfoSources: ()=>PlatformInfoSources,
PlatformSources: ()=>PlatformSources,
PlatformVideoSource: ()=>platform_PlatformVideoSource,
PlatformVideoSources: ()=>PlatformVideoSources
});
var package_namespaceObject = JSON.parse('{"UU":"@dan-uni/dan-any","rE":"1.0.1","TB":"https://github.com/ani-uni/danuni/tree/master/packages/dan-any#readme"}');
const color_pad = (s)=>s.length < 2 ? `0${s}` : s;
const decimalToHex = (n)=>color_pad(n.toString(16));
const isDarkColor = ({ r, g, b })=>0.299 * r + 0.587 * g + 0.114 * b < 0x30;
const WHITE = {
r: 255,
g: 255,
b: 255
};
const BLACK = {
r: 0,
g: 0,
b: 0
};
const hexColorToRGB = (hex)=>{
if (0 === hex.indexOf('#')) hex = hex.slice(1);
const [r, g, b] = 3 === hex.length ? hex.split('').map((c)=>c + c) : hex.match(/../g) || [];
return {
r: Number.parseInt(r, 16),
g: Number.parseInt(g, 16),
b: Number.parseInt(b, 16)
};
};
const formatColor = ({ r, g, b }, opacity)=>{
const color = [
b,
g,
r
];
if (void 0 !== opacity) {
const alpha = Math.round((1 - opacity) * 255);
color.unshift(alpha);
}
return `&H${color.map(decimalToHex).join('').toUpperCase()}`;
};
const getDecoratingColor = (color)=>isDarkColor(color) ? WHITE : BLACK;
const isWhite = (color)=>255 === color.r && 255 === color.g && 255 === color.b;
var platform_PlatformInfoSource = /*#__PURE__*/ function(PlatformInfoSource) {
PlatformInfoSource["Bangumi"] = "bgm";
PlatformInfoSource["TMDB"] = "tmdb";
return PlatformInfoSource;
}({});
const PlatformInfoSources = Object.values(platform_PlatformInfoSource);
var platform_PlatformVideoSource = /*#__PURE__*/ function(PlatformVideoSource) {
PlatformVideoSource["Acfun"] = "acfun";
PlatformVideoSource["Baha"] = "baha";
PlatformVideoSource["Bilibili"] = "bili";
PlatformVideoSource["BilibiliGlobal"] = "bglobal";
PlatformVideoSource["Iqiyi"] = "iqiyi";
PlatformVideoSource["Tencent"] = "tencent";
PlatformVideoSource["Youku"] = "youku";
return PlatformVideoSource;
}({});
const PlatformVideoSources = Object.values(platform_PlatformVideoSource);
var platform_PlatformDanmakuOnlySource = /*#__PURE__*/ function(PlatformDanmakuOnlySource) {
PlatformDanmakuOnlySource["DanDanPlay"] = "ddplay";
PlatformDanmakuOnlySource["TuCao"] = "tucao";
return PlatformDanmakuOnlySource;
}({});
const PlatformDanmakuOnlySources = Object.values(platform_PlatformDanmakuOnlySource);
const PlatformDanmakuSources = [
...PlatformVideoSources,
...PlatformDanmakuOnlySources
];
const PlatformSources = [
...PlatformInfoSources,
...PlatformDanmakuSources
];
class id_gen_UniID {
constructor(id, domain){
this.id = id;
this.domain = domain;
}
static fromString(str) {
const [id, domain] = str.split('@');
return new id_gen_UniID(id, domain);
}
toString() {
return `${this.id}@${this.domain}`;
}
static fromNull(domain) {
return new id_gen_UniID('runtime' === domain ? 'runtime' : 'anonymous', domain || 'danuni');
}
static fromBili({ cid, mid, midHash }) {
if (cid) return new id_gen_UniID(cid.toString(), platform_PlatformVideoSource.Bilibili);
if (mid) return new id_gen_UniID(mid.toString(), platform_PlatformVideoSource.Bilibili);
if (midHash) return new id_gen_UniID(midHash, platform_PlatformVideoSource.Bilibili);
return this.fromNull(platform_PlatformVideoSource.Bilibili);
}
static fromUnknown(id, domain) {
if (id) return new id_gen_UniID(id, domain);
return this.fromNull(domain);
}
}
function createDMID(dan, slice = 8) {
return new jssha('SHA3-256', 'TEXT').update(`${dan.content}|${dan.mode}|${dan.pool}|${dan.platform}|${dan.extraStr}|${dan.senderID}|${UniDM.transCtime(dan.ctime).toISOString()}`).getHash('HEX').slice(0, slice);
}
function _ts_decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : null === desc ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function _ts_metadata(k, v) {
if ("object" == typeof Reflect && "function" == typeof Reflect.metadata) return Reflect.metadata(k, v);
}
const dm_gen_JSON = json_bigint({
useNativeBigInt: true
});
function cleanEmptyObjects(obj) {
if (null === obj || 'object' != typeof obj) return obj;
if (Array.isArray(obj)) return obj;
const cleaned = {};
for (const [key, value] of Object.entries(obj)){
const cleanedValue = cleanEmptyObjects(value);
if (void 0 !== cleanedValue && ('object' != typeof cleanedValue || 0 !== Object.keys(cleanedValue).length)) cleaned[key] = cleanedValue;
}
return Object.keys(cleaned).length > 0 ? cleaned : {};
}
class SetBin {
constructor(bin){
this.bin = bin;
}
set1(bit) {
this.bin |= 1 << bit;
}
set0(bit) {
this.bin &= ~(1 << bit);
}
}
const toBits = (number)=>{
const bits = [];
do {
bits.unshift(!!(1 & number));
number >>= 1;
}while (number);
return bits.toReversed();
};
var dm_gen_DMAttr = /*#__PURE__*/ function(DMAttr) {
DMAttr["Protect"] = "Protect";
DMAttr["FromLive"] = "FromLive";
DMAttr["HighLike"] = "HighLike";
DMAttr["Compatible"] = "Compatible";
DMAttr["Reported"] = "Reported";
DMAttr["Unchecked"] = "Unchecked";
DMAttr["HasEvent"] = "HasEvent";
DMAttr["Hide"] = "Hide";
return DMAttr;
}({});
const DMAttrUtils = {
fromBin (bin = 0, format) {
const array = toBits(bin);
const attr = [];
if ('bili' === format) {
if (array[0]) attr.push("Protect");
if (array[1]) attr.push("FromLive");
if (array[2]) attr.push("HighLike");
}
return attr;
},
toBin (attr = [], format) {
const bin = new SetBin(0);
if ('bili' === format) {
if (attr.includes("Protect")) bin.set1(0);
if (attr.includes("FromLive")) bin.set1(1);
if (attr.includes("HighLike")) bin.set1(2);
}
return bin.bin;
}
};
var dm_gen_ExtraDanUniChapterType = /*#__PURE__*/ function(ExtraDanUniChapterType) {
ExtraDanUniChapterType["Chapter"] = "ch";
ExtraDanUniChapterType["Review"] = "rev";
ExtraDanUniChapterType["OP"] = "op";
ExtraDanUniChapterType["Intermission"] = "int";
ExtraDanUniChapterType["ED"] = "ed";
ExtraDanUniChapterType["Preview"] = "prvw";
ExtraDanUniChapterType["Cut"] = "cut";
ExtraDanUniChapterType["Duplicate"] = "dup";
ExtraDanUniChapterType["AdBiz"] = "biz";
ExtraDanUniChapterType["AdUnpaid"] = "promo";
return ExtraDanUniChapterType;
}({});
const ExtraDanUniChapterTypeDict = {
chs: {
ch: '其它片段',
rev: '回顾',
op: '片头',
int: '中场',
ed: '片尾',
prvw: '预告',
cut: '删减',
dup: '补档',
biz: '商业广告',
promo: '推广'
}
};
var dm_gen_ExtraDanUniChapterAction = /*#__PURE__*/ function(ExtraDanUniChapterAction) {
ExtraDanUniChapterAction[ExtraDanUniChapterAction["Disabled"] = -1] = "Disabled";
ExtraDanUniChapterAction[ExtraDanUniChapterAction["ShowOverlay"] = 0] = "ShowOverlay";
ExtraDanUniChapterAction[ExtraDanUniChapterAction["ManualSkip"] = 1] = "ManualSkip";
ExtraDanUniChapterAction[ExtraDanUniChapterAction["AutoSkip"] = 2] = "AutoSkip";
return ExtraDanUniChapterAction;
}({});
var dm_gen_Modes = /*#__PURE__*/ function(Modes) {
Modes[Modes["Normal"] = 0] = "Normal";
Modes[Modes["Bottom"] = 1] = "Bottom";
Modes[Modes["Top"] = 2] = "Top";
Modes[Modes["Reverse"] = 3] = "Reverse";
Modes[Modes["Ext"] = 4] = "Ext";
return Modes;
}({});
var dm_gen_Pools = /*#__PURE__*/ function(Pools) {
Pools[Pools["Def"] = 0] = "Def";
Pools[Pools["Sub"] = 1] = "Sub";
Pools[Pools["Adv"] = 2] = "Adv";
Pools[Pools["Ix"] = 3] = "Ix";
return Pools;
}({});
class UniDM {
init(options) {
this.options = options || this.options;
const def = new UniDM();
if (void 0 === this.options.dmid || true === this.options.dmid) this.options.dmid = createDMID;
this.content = String(this.content);
this.ctime = UniDM.transCtime(this.ctime);
if (!this.SOID) this.SOID = def.SOID;
if (!this.progress) this.progress = def.progress;
if (!this.mode) this.mode = def.mode;
if (!this.fontsize) this.fontsize = def.mode;
if (!this.color) this.color = def.color;
if (!this.senderID) this.senderID = def.senderID;
if (!this.content) this.content = def.content;
if (!this.ctime) this.ctime = def.ctime;
if (!this.weight) this.weight = def.weight;
if (!this.pool) this.pool = def.pool;
if (!this.attr) this.attr = def.attr;
if (!this.DMID && false !== this.options.dmid) this.DMID = this.toDMID();
this.progress = Number.parseFloat(this.progress.toFixed(3));
if (this.extraStr) this.extraStr = dm_gen_JSON.stringify(cleanEmptyObjects(dm_gen_JSON.parse(this.extraStr)));
if ('{}' === this.extraStr) this.extraStr = void 0;
else if (4 !== this.mode) {
const checkExtraBili = (obj)=>obj ? [
'adv',
'bas',
'code',
'command'
].some((k)=>obj[k]) : false;
if (this.extra.artplayer || this.extra.danuni?.chapter || checkExtraBili(this.extra.bili)) this.mode = 4;
}
return this;
}
async validate() {
return validateOrReject(this);
}
static create(pjson, options) {
return pjson ? plainToInstance(UniDM, pjson.extra ? {
...pjson,
extraStr: pjson.extra ? dm_gen_JSON.stringify(pjson.extra) : pjson.extraStr
} : pjson, {
excludeExtraneousValues: true
}).init(options) : new UniDM();
}
get extra() {
const extra = dm_gen_JSON.parse(this.extraStr || '{}');
return extra;
}
get isFrom3rdPlatform() {
if (this.platform && PlatformDanmakuSources.includes(this.platform)) return true;
return false;
}
toDMID() {
if (false === this.options.dmid) return;
if (true === this.options.dmid) return createDMID(this);
if ('number' == typeof this.options.dmid) return createDMID(this, this.options.dmid);
return this.options.dmid(this);
}
isSameAs(dan, options) {
if (this === dan) return true;
if (4 === this.mode || 4 === dan.mode) return false;
if (!options?.skipDanuniMerge && (this.extra.danuni?.merge || dan.extra.danuni?.merge)) return false;
if (this.extra.bili?.dmid && dan.extra.bili?.dmid) {
if (this.extra.bili.dmid && !dan.extra.bili.dmid || !this.extra.bili.dmid && dan.extra.bili.dmid) return false;
if (this.extra.bili.dmid === dan.extra.bili.dmid) return true;
return false;
}
if (this.extra.artplayer && !dan.extra.artplayer || !this.extra.artplayer && dan.extra.artplayer) return false;
if (this.extra.artplayer && dan.extra.artplayer && (this.extra.artplayer.border !== dan.extra.artplayer.border || dm_gen_JSON.stringify(this.extra.artplayer.style) !== dm_gen_JSON.stringify(dan.extra.artplayer.style))) return false;
const isSame = (k)=>this[k] === dan[k];
const checks = [
'SOID',
'content',
'mode',
'pool',
'platform'
].every((k)=>isSame(k));
return checks;
}
minify() {
const def = new UniDM();
const result = {
SOID: this.SOID
};
if (this.progress !== def.progress) result.progress = this.progress;
if (this.mode !== def.mode) result.mode = this.mode;
if (this.fontsize !== def.fontsize) result.fontsize = this.fontsize;
if (this.color !== def.color) result.color = this.color;
if (this.senderID !== def.senderID) result.senderID = this.senderID;
if (this.content !== def.content) result.content = this.content;
if (this.weight !== def.weight) result.weight = this.weight;
if (this.pool !== def.pool) result.pool = this.pool;
if (this.attr.length > 0) result.attr = this.attr;
if (void 0 !== this.platform) result.platform = this.platform;
if (this.extraStr && '{}' !== this.extraStr) result.extraStr = this.extraStr;
if (void 0 !== this.DMID) result.DMID = this.DMID;
return result;
}
downgradeAdvcancedDan({ include, exclude, cleanExtra = false } = {}) {
if (!this.extra) return this;
{
if (!include) include = [];
if (!exclude) exclude = [];
const check = (k)=>include?.includes(k) || !exclude?.includes(k);
const clone = UniDM.create(this);
clone.mode = 2;
if (check('danuni') && clone.extra.danuni) {
const danuni = clone.extra.danuni;
if (danuni.merge) {
const merge = danuni.merge;
clone.content = `${this.content} x${merge.count}`;
} else if (danuni.chapter) {
const chapter = danuni.chapter;
if ("cut" === chapter.type) clone.content = `[提示]${clone.platform}源${ExtraDanUniChapterTypeDict.chs[chapter.type]}了${chapter.duration}秒`;
else if ("dup" === chapter.type) clone.content = `[提示(${ExtraDanUniChapterTypeDict.chs[chapter.type]})]${clone.platform}源-${chapter.duration}秒`;
else clone.content = `[空降(${ExtraDanUniChapterTypeDict.chs[chapter.type]})]${hh_mm_ss.fromS(clone.progress + chapter.duration)}`;
}
} else if (check('bili') && clone.extra.bili) {
const bili = clone.extra.bili;
if (7 === bili.mode && bili.adv) clone.content = `[B站高级弹幕]${dm_gen_JSON.parse(bili.adv)[4] || ''}`;
else if (bili.command) {
const command = bili.command;
clone.content = `[B站指令弹幕]${command.content}`;
clone.fontsize = 36;
}
}
clone.senderID = 'compat[bot]@dan-any';
clone.attr.push("Compatible");
if (cleanExtra) clone.extraStr = void 0;
return clone;
}
}
static transCtime(oriCtime, tsUnit) {
function isMsTs(ts) {
if ('ms' === tsUnit) return true;
if ('s' === tsUnit) return false;
return ts < 100000000;
}
if ('number' == typeof oriCtime || 'bigint' == typeof oriCtime) if (isMsTs(oriCtime)) return new Date(Number(oriCtime));
else return new Date(1000 * Number(oriCtime));
if ('string' != typeof oriCtime) return oriCtime;
if (/^\d+n$/.test(oriCtime)) return new Date(Number(oriCtime.slice(0, -1)));
return new Date(oriCtime);
}
static transMode(oriMode, fmt) {
let mode = 0;
switch(fmt){
case 'bili':
switch(oriMode){
case 4:
mode = 1;
break;
case 5:
mode = 2;
break;
case 6:
mode = 3;
break;
case 7:
mode = 4;
break;
case 8:
mode = 4;
break;
case 9:
mode = 4;
break;
}
break;
case 'dplayer':
if (1 === oriMode) mode = 2;
else if (2 === oriMode) mode = 1;
break;
case 'artplayer':
if (1 === oriMode) mode = 2;
else if (2 === oriMode) mode = 1;
break;
case 'ddplay':
mode = this.transMode(oriMode, 'bili');
break;
default:
mode = 0;
break;
}
return mode;
}
static parseBiliSingle(p, c) {
const p_arr = p.split(',');
return {
content: c,
progress: Number.parseFloat(p_arr[0]),
mode: Number.parseInt(p_arr[1]),
fontsize: Number.parseInt(p_arr[2]),
color: Number.parseInt(p_arr[3]),
ctime: BigInt(p_arr[4]),
pool: Number.parseInt(p_arr[5]),
midHash: p_arr[6],
id: BigInt(p_arr[7]),
weight: Number.parseInt(p_arr[8])
};
}
static fromBili(args, cid, options, recSOID) {
if (args.oid && !cid) cid = args.oid;
const SOID = recSOID || `def_${platform_PlatformVideoSource.Bilibili}+${id_gen_UniID.fromBili({
cid
})}`;
const senderID = isEmail(args.midHash, {
require_tld: false
}) ? args.midHash : id_gen_UniID.fromBili({
midHash: args.midHash
});
let mode = 0;
const pool = args.pool;
const extra = {
bili: {
mode: args.mode,
pool: args.pool,
dmid: args.id,
attr: args.attr,
mid: args.mid
}
};
switch(args.mode){
case 4:
mode = 1;
break;
case 5:
mode = 2;
break;
case 6:
mode = 3;
break;
case 7:
mode = 4;
extra.bili.adv = args.content;
break;
case 8:
mode = 4;
extra.bili.code = args.content;
break;
case 9:
mode = 4;
extra.bili.bas = args.content;
break;
default:
mode = 0;
break;
}
return this.create({
...args,
SOID,
mode,
senderID: senderID.toString(),
ctime: this.transCtime(args.ctime, 's'),
weight: args.weight || (3 === pool ? 1 : 0),
pool,
attr: DMAttrUtils.fromBin(args.attr, platform_PlatformVideoSource.Bilibili),
platform: platform_PlatformVideoSource.Bilibili,
extra
}, options);
}
toBiliXML(options) {
if (options?.skipBiliCommand && this.extra.bili?.command) return null;
const recMode = (mode, extra)=>{
switch(mode){
case 0:
return 1;
case 1:
return 4;
case 2:
return 5;
case 3:
return 6;
case 4:
if (!extra) return 1;
if (extra.adv) return 7;
if (extra.code) return 8;
else if (extra.bas) return 9;
else return 1;
default:
return 1;
}
};
const rMode = this.extra.bili?.mode || recMode(this.mode, this.extra?.bili);
let content;
switch(rMode){
case 7:
content = this.extra?.bili?.adv;
break;
case 8:
content = this.extra?.bili?.code;
break;
case 9:
content = this.extra?.bili?.bas;
break;
default:
content = this.content;
break;
}
return {
'#text': content ?? this.content,
'@_p': [
this.progress,
rMode,
this.fontsize,
this.color,
this.ctime.getTime() / 1000,
this.extra.bili?.pool || this.pool,
options?.avoidSenderIDWithAt ? this.senderID.replaceAll(`@${platform_PlatformVideoSource.Bilibili}`, '') : this.senderID,
this.extra.bili?.dmid || this.DMID || this.toDMID(),
this.weight
].join(',')
};
}
static fromBiliCommand(args, cid, options) {
if (args.oid && !cid) cid = args.oid;
const SOID = `def_${platform_PlatformVideoSource.Bilibili}+${id_gen_UniID.fromBili({
cid
})}`;
const senderID = id_gen_UniID.fromBili({
mid: args.mid
});
return this.create({
...args,
SOID,
mode: 4,
senderID: senderID.toString(),
ctime: new Date(`${args.ctime} GMT+0800`),
weight: 10,
pool: 2,
attr: [
"Protect"
],
platform: platform_PlatformVideoSource.Bilibili,
extra: {
bili: {
command: args
}
}
}, options);
}
static fromDplayer(args, playerID, domain, options) {
const SOID = id_gen_UniID.fromUnknown(playerID, domain);
const senderID = id_gen_UniID.fromUnknown(args.midHash, domain);
return this.create({
...args,
SOID: SOID.toString(),
mode: this.transMode(args.mode, 'dplayer'),
senderID: senderID.toString(),
platform: domain
}, options);
}
toDplayer() {
let mode = 0;
if (2 === this.mode) mode = 1;
else if (1 === this.mode) mode = 2;
return {
mode,
progress: this.progress,
color: this.color,
midHash: this.senderID,
content: this.content
};
}
static fromArtplayer(args, playerID, domain, options) {
const SOID = id_gen_UniID.fromUnknown(playerID, domain);
const senderID = id_gen_UniID.fromUnknown('', domain);
let extra = args.border ? {
artplayer: {
border: args.border,
style: {}
}
} : void 0;
if (args.style) extra = extra ? {
...extra,
artplayer: {
...extra.artplayer,
style: args.style
}
} : {
artplayer: {
style: args.style
}
};
return this.create({
...args,
SOID: SOID.toString(),
mode: this.transMode(args.mode, 'artplayer'),
senderID: senderID.toString(),
platform: domain,
extra
}, options);
}
toArtplayer() {
let mode = 0;
if (2 === this.mode) mode = 1;
else if (1 === this.mode) mode = 2;
return {
progress: this.progress,
mode,
color: this.color,
content: this.content,
style: this.extra.artplayer?.style
};
}
static fromDDplay(args, episodeId, domain = platform_PlatformDanmakuOnlySource.DanDanPlay, options) {
const SOID = id_gen_UniID.fromUnknown(`def_${platform_PlatformDanmakuOnlySource.DanDanPlay}+${episodeId}`, domain);
return this.create({
...args,
SOID: SOID.toString(),
mode: this.transMode(args.mode, 'ddplay'),
senderID: args.uid,
content: args.m,
platform: domain,
DMID: args.cid.toString()
}, options);
}
toDDplay() {
let mode = 1;
if (2 === this.mode) mode = 5;
else if (1 === this.mode) mode = 4;
return {
progress: this.progress,
mode,
color: this.color,
uid: this.senderID,
m: this.content,
cid: this.DMID ? BigInt(`0x${Buffer.from(this.DMID).toString('hex')}`) : 0n
};
}
constructor(){
this.SOID = id_gen_UniID.fromNull().toString();
this.progress = 0;
this.mode = 0;
this.fontsize = 25;
this.color = 16777215;
this.senderID = id_gen_UniID.fromNull().toString();
this.content = '';
this.ctime = new Date();
this.weight = 0;
this.pool = 0;
this.attr = [];
this.options = {
dmid: createDMID
};
}
}
_ts_decorate([
IsEmail({
require_tld: false
}),
IsString(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", String)
], UniDM.prototype, "SOID", void 0);
_ts_decorate([
Min(0),
IsNumber(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "progress", void 0);
_ts_decorate([
IsEnum(dm_gen_Modes),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "mode", void 0);
_ts_decorate([
Max(64),
Min(12),
IsNumber(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "fontsize", void 0);
_ts_decorate([
IsNumber(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "color", void 0);
_ts_decorate([
IsEmail({
require_tld: false
}),
IsString(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", String)
], UniDM.prototype, "senderID", void 0);
_ts_decorate([
IsString(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", String)
], UniDM.prototype, "content", void 0);
_ts_decorate([
IsDate(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", "u" < typeof Date ? Object : Date)
], UniDM.prototype, "ctime", void 0);
_ts_decorate([
Max(11),
Min(0),
IsInt(),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "weight", void 0);
_ts_decorate([
IsEnum(dm_gen_Pools),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Number)
], UniDM.prototype, "pool", void 0);
_ts_decorate([
IsEnum(dm_gen_DMAttr, {
each: true
}),
IsNotEmpty(),
Expose(),
_ts_metadata("design:type", Array)
], UniDM.prototype, "attr", void 0);
_ts_decorate([
IsString(),
IsOptional(),
Expose(),
_ts_metadata("design:type", Object)
], UniDM.prototype, "platform", void 0);
_ts_decorate([
IsString(),
IsOptional(),
Expose(),
_ts_metadata("design:type", String)
], UniDM.prototype, "extraStr", void 0);
_ts_decorate([
IsString(),
IsOptional(),
Expose(),
_ts_metadata("design:type", String)
], UniDM.prototype, "DMID", void 0);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
"u" < typeof Options ? Object : Options
]),
_ts_metadata("design:returntype", void 0)
], UniDM.prototype, "init", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", Promise)
], UniDM.prototype, "validate", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", "u" < typeof Extra ? Object : Extra),
_ts_metadata("design:paramtypes", [])
], UniDM.prototype, "extra", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", void 0),
_ts_metadata("design:paramtypes", [])
], UniDM.prototype, "isFrom3rdPlatform", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", void 0)
], UniDM.prototype, "toDMID", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
Object,
Object
]),
_ts_metadata("design:returntype", Boolean)
], UniDM.prototype, "isSameAs", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", void 0)
], UniDM.prototype, "minify", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
Object
]),
_ts_metadata("design:returntype", void 0)
], UniDM.prototype, "downgradeAdvcancedDan", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
Object
]),
_ts_metadata("design:returntype", void 0)
], UniDM.prototype, "toBiliXML", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", "u" < typeof DMDplayer ? Object : DMDplayer)
], UniDM.prototype, "toDplayer", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", "u" < typeof DMArtplayer ? Object : DMArtplayer)
], UniDM.prototype, "toArtplayer", null);
_ts_decorate([
Expose(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", []),
_ts_metadata("design:returntype", "u" < typeof DMDDplay ? Object : DMDDplay)
], UniDM.prototype, "toDDplay", null);
const DanmakuType = {
SCROLL: 1,
BOTTOM: 2,
TOP: 3
};
const FontSize = {
SMALL: 0,
NORMAL: 1,
LARGE: 2
};
function decimalToRGB888(decimal) {
const r = decimal >> 16 & 0xff;
const g = decimal >> 8 & 0xff;
const b = 0xff & decimal;
return {
r,
g,
b
};
}
function UniPool2DanmakuLists(UP) {
const dans = UP.dans;
let type = DanmakuType.SCROLL;
return dans.map((d)=>{
if (d.mode === dm_gen_Modes.Bottom) type = DanmakuType.BOTTOM;
else if (d.mode === dm_gen_Modes.Top) type = DanmakuType.TOP;
return {
time: d.progress,
type,
fontSizeType: d.fontsize,
content: d.content,
color: decimalToRGB888(d.color),
extra: d
};
});
}
function DanmakuList2UniPool(d, options) {
return new UniPool(d.map((d)=>UniDM.create(d.extra)), options);
}
const lang_assign = (source, ...targets)=>{
for (const target of targets)for (const key of Object.keys(target))source[key] = target[key];
return source;
};
const arrayOfLength = (length, defaultValue)=>{
const array = new Array(length);
for(let i = 0; i < length; i++)array[i] = defaultValue;
return array;
};
const computeScrollInTime = (rectWidth, screenWidth, scrollTime)=>{
const speed = (screenWidth + rectWidth) / scrollTime;
return rectWidth / speed;
};
const computeScrollOverTime = (rectWidth, screenWidth, scrollTime)=>{
const speed = (screenWidth + rectWidth) / scrollTime;
return screenWidth / speed;
};
const splitGrids = ({ fontSize, padding, playResY, bottomSpace })=>{
const defaultFontSize = fontSize[FontSize.NORMAL];
const paddingTop = padding[0];
const paddingBottom = padding[2];
const linesCount = Math.floor((playResY - bottomSpace) / (defaultFontSize + paddingTop + paddingBottom));
return {
1: arrayOfLength(linesCount, {
start: 0,
end: 0,
width: 0
}),
3: arrayOfLength(linesCount, 0),
2: arrayOfLength(linesCount, 0)
};
};
const measureTextWidthConstructor = (canvasContext)=>{
const supportTextMeasure = !!canvasContext.measureText('中');
if (supportTextMeasure) return (fontName, fontSize, bold, text)=>{
canvasContext.font = `${bold ? 'bold' : 'normal'} ${fontSize}px ${fontName}`;
const textWidth = canvasContext.measureText(text).width;
return Math.round(textWidth);
};
console.warn('[Warn] node-canvas is installed without text measure support, layout may not be correct');
return (_fontName, fontSize, _bold, text)=>text.length * fontSize;
};
const resolveAvailableFixGrid = (grids, time)=>{
for (const [i, grid] of grids.entries())if (grid <= time) return i;
return -1;
};
const resolveAvailableScrollGrid = (grids, rectWidth, screenWidth, time, duration)=>{
for (const [i, previous] of grids.entries()){
const previousInTime = previous.start + computeScrollInTime(previous.width, screenWidth, duration);
const currentOverTime = time + computeScrollOverTime(rectWidth, screenWidth, duration);
if (time >= previousInTime && currentOverTime >= previous.end) return i;
}
return -1;
};
const initializeLayout = (config, canvasCtx)=>{
const { playResX, playResY, fontName, fontSize, bold, padding, scrollTime, fixTime, bottomSpace } = config;
const [paddingTop, paddingRight, paddingBottom, paddingLeft] = padding;
const defaultFontSize = fontSize[FontSize.NORMAL];
const grids = splitGrids(config);
const gridHeight = defaultFontSize + paddingTop + paddingBottom;
return (danmaku)=>{
const targetGrids = grids[danmaku.type];
const danmakuFontSize = fontSize[danmaku.fontSizeType];
const rectWidth = measureTextWidthConstructor(canvasCtx)(fontName, danmakuFontSize, bold, danmaku.content) + paddingLeft + paddingRight;
const verticalOffset = paddingTop + Math.round((defaultFontSize - danmakuFontSize) / 2);
if (danmaku.type === DanmakuType.SCROLL) {
const scrollGrids = targetGrids;
const gridNumber = resolveAvailableScrollGrid(scrollGrids, rectWidth, playResX, danmaku.time, scrollTime);
if (gridNumber < 0) return null;
targetGrids[gridNumber] = {
width: rectWidth,
start: danmaku.time,
end: danmaku.time + scrollTime
};
const top = gridNumber * gridHeight + verticalOffset;
const start = playResX + paddingLeft;
const end = -rectWidth;
return {
...danmaku,
top,
start,
end
};
}
{
const gridNumber = resolveAvailableFixGrid(targetGrids, danmaku.time);
if (gridNumber < 0) return null;
if (danmaku.type === DanmakuType.TOP) {
targetGrids[gridNumber] = danmaku.time + fixTime;
const top = gridNumber * gridHeight + verticalOffset;
const left = Math.round(playResX / 2);
return {
...danmaku,
top,
left
};
}
targetGrids[gridNumber] = danmaku.time + fixTime;
const top = playResY - bottomSpace - gridHeight * gridNumber - gridHeight + verticalOffset;
const left = Math.round(playResX / 2);
return {
...danmaku,
top,
left
};
}
};
};
const layoutDanmaku = (inputList, config, canvasCtx)=>{
const list = [
...UniPool2DanmakuLists(inputList)
].toSorted((x, y)=>x.time - y.time);
const layout = initializeLayout(config, canvasCtx);
return DanmakuList2UniPool(list.map(layout).filter((danmaku)=>!!danmaku));
};
const formatTime = (seconds)=>{
const div = (i, j)=>Math.floor(i / j);
const pad = (n)=>n < 10 ? `0${n}` : String(n);
const integer = Math.floor(seconds);
const hour = div(integer, 3600);
const minute = div(integer, 60) % 60;
const second = integer % 60;
const minorSecond = Math.floor((seconds - integer) * 100);
return `${hour}:${pad(minute)}:${pad(second)}.${minorSecond}`;
};
const encode = (text)=>text.toString().replaceAll('{', '{').replaceAll('}', '}').replaceAll(/\r|\n/g, '');
const scrollCommand = ({ start, end, top })=>String.raw`\move(${start},${top},${end},${top})`;
const fixCommand = ({ top, left })=>String.raw`\an8\pos(${left},${top})`;
const colorCommand = (color)=>String.raw`\c${formatColor(color)}`;
const borderColorCommand = (color)=>String.raw`\3c${formatColor(color)}`;
const dialogue = (danmaku, config)=>{
const { fontSizeType, content, time } = danmaku;
const { scrollTime, fixTime } = config;
const commands = [
danmaku.type === DanmakuType.SCROLL ? scrollCommand({
start: danmaku.start,
end: danmaku.end,
top: danmaku.top
}) : fixCommand({
top: danmaku.top,
left: danmaku.left
}),
isWhite(danmaku.color) ? '' : colorCommand(danmaku.color),
isWhite(danmaku.color) ? '' : borderColorCommand(getDecoratingColor(danmaku.color))
];
const fields = [
0,
formatTime(time),
formatTime(time + (danmaku.type === DanmakuType.SCROLL ? scrollTime : fixTime)),
`F${fontSizeType}`,
'',
'0000',
'0000',
'0000',
'',
`{${commands.join('')}}${encode(content)}`
];
return `Dialogue: ${fields.join(',')}`;
};
const calculateDanmakuPosition = (danmaku, config)=>{
const { playResX, playResY, scrollTime, fixTime } = config;
switch(danmaku.type){
case DanmakuType.SCROLL:
{
const start = playResX;
const end = -playResX / 10;
const top = danmaku.fontSizeType * playResY / 20;
return {
...danmaku,
start,
end,
top,
left: 0,
duration: scrollTime
};
}
case DanmakuType.TOP:
case DanmakuType.BOTTOM:
{
const left = playResX / 2;
const top = danmaku.type === DanmakuType.TOP ? danmaku.fontSizeType * playResY / 20 : playResY - config.bottomSpace - danmaku.fontSizeType * playResY / 20;
return {
...danmaku,
start: 0,
end: 0,
top,
left,
duration: fixTime
};
}
default:
throw new Error(`Unknown danmaku type: ${danmaku.type}`);
}
};
const event_event = (list, config)=>{
const content = [
'[Events]',
'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text',
...list.map((danmaku)=>{
const positionedDanmaku = calculateDanmakuPosition(danmaku, config);
return dialogue(positionedDanmaku, config);
})
];
return content.join('\n');
};
const info_info = ({ playResX, playResY }, { filename, title })=>{
const content = [
'[Script Info]',
`Title: ${title}`,
`Original Script: 根据 ${filename} 的弹幕信息,由 ${package_namespaceObject.TB} 生成`,
'ScriptType: v4.00+',
'Collisions: Reverse',
`PlayResX: ${playResX}`,
`PlayResY: ${playResY}`,
'Timer: 100.0000'
];
return content.join('\n');
};
const compressTypes = new Set([
'brotli',
'gzip'
]);
const baseTypes = new Set([
'base64',
'base18384'
]);
function fromUint16Array(array) {
let result = '';
for (const element of array)result += String.fromCodePoint(element);
return result;
}
function raw_raw(list, config, context, compressType = 'brotli', baseType = 'base18384') {
const raw1 = {
list,
config,
context
};
const rawText = JSON.stringify(raw1);
let compress;
compress = 'brotli' === compressType ? brotliCompressSync(rawText) : gzipSync(rawText);
return `;RawCompressType: ${compressType}\n;RawBaseType: ${baseType}\n;Raw: ${'base64' === baseType ? compress.toString('base64') : fromUint16Array(external_base16384_encode(compress))}`;
}
function deRaw(ass) {
const arr = ass.split('\n');
const lineCompressType = arr.find((line)=>line.startsWith(';RawCompressType:'));
const lineBaseType = arr.find((line)=>line.startsWith(';RawBaseType:'));
const lineRaw = arr.find((line)=>line.startsWith(';Raw:'));
if (!lineCompressType || !lineBaseType || !lineRaw) return;
{
let compressType = lineCompressType.replace(';RawCompressType: ', '').trim();
let baseType = lineBaseType.replace(';RawBaseType: ', '').trim();
if (!compressTypes.has(compressType)) compressType = 'gzip';
if (!baseTypes.has(baseType)) baseType = 'base64';
const text = lineRaw.replace(';Raw: ', '').trim();
const buffer = 'base64' === baseType ? Buffer.from(text, 'base64') : Buffer.from(decode(Buffer.from(text, 'utf8').toString('utf8')));
let decompress;
decompress = 'brotli' === compressType ? brotliDecompressSync(buffer) : gunzipSync(buffer);
try {
return JSON.parse(decompress.toString('utf8'));
} catch {
return;
}
}
}
const style = ({ fontName, fontSize, color: configColor, outlineColor, backColor, bold, outline, shadow, opacity })=>{
const fields = [
'Name',
'Fontname',
'Fontsize',
'PrimaryColour',
'SecondaryColour',
'OutlineColour',
'BackColour',
'Bold',
'Italic',
'Underline',
'StrikeOut',
'ScaleX',
'ScaleY',
'Spacing',
'Angle',
'BorderStyle',
'Outline',
'Shadow',
'Alignment',
'MarginL',
'MarginR',
'MarginV',
'Encoding'
];
const primaryColorValue = formatColor(hexColorToRGB(configColor), opacity);
const secondaryColor = getDecoratingColor(hexColorToRGB(configColor));
const outlineColorValue = formatColor(outlineColor ? hexColorToRGB(outlineColor) : secondaryColor, opacity);
const backColorValue = formatColor(backColor ? hexColorToRGB(backColor) : secondaryColor, opacity);
const colorStyle = `${primaryColorValue},${primaryColorValue},${outlineColorValue},${backColorValue}`;
const boldValue = bold ? '1' : '0';
const fontStyle = `${boldValue},0,0,0,100,100,0,0,1,${outline},${shadow},7,0,0,0,0`;
const fontDeclaration = (size, i)=>`Style: F${i},${fontName},${size},${colorStyle},${fontStyle}`;
const content = [
'[V4+ Styles]',
`Format: ${fields.join(',')}`,
...fontSize.map(fontDeclaration)
];
return content.join('\n');
};
const default_context = {
filename: 'unknown',
title: 'unknown'
};
const create_ass = (list, rawList, config, context = default_context, rawConfig)=>{
const Elist = UniPool2DanmakuLists(list);
const ErawList = UniPool2DanmakuLists(rawList);
const content = [
info_info(config, context),
style(config),
event_event(Elist, config)
];
if (config.includeRaw) content.push(raw_raw(ErawList, config, context, rawConfig?.compressType, rawConfig?.baseType));
return `${content.join('\n\n')}\n`;
};
const getConfig = (overrides = {})=>{
const defaults = {
fontSize: [
25,
25,
36
],
fontName: 'SimHei',
color: '#ffffff',
outlineColor: void 0,
backColor: void 0,
outline: 2,
shadow: 0,
bold: false,
padding: [
2,
2,
2,
2
],
playResX: 1920,
playResY: 1080,
scrollTime: 8,
fixTime: 4,
opacity: 0.6,
bottomSpace: 60,
includeRaw: true,
mergeIn: -1
};
const config = lang_assign(defaults, overrides);
config.color = formatColor(hexColorToRGB(config.color));
config.outlineColor = config.outlineColor && formatColor(hexColorToRGB(config.outlineColor));
config.backColor = config.backColor && formatColor(hexColorToRGB(config.backColor));
return config;
};
function generateASS(danmaku, options, canvasCtx) {
const config = getConfig(options.substyle);
const mergedList = danmaku.merge(config.mergeIn);
const layoutList = layoutDanmaku(mergedList, config, canvasCtx);
const content = create_ass(layoutList, danmaku, config, {
filename: options?.filename || 'unknown',
title: options?.title || 'unknown'
}, options.raw);
return content;
}
function parseAssRawField(ass, options) {
const raw = deRaw(ass);
if (raw) return DanmakuList2UniPool(raw.list, options);
return UniPool.create();
}
const file_bili_dm = /*@__PURE__*/ fileDesc("Cg1iaWxpL2RtLnByb3RvEiBiaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MSJkCgZBdmF0YXISCgoCaWQYASABKAkSCwoDdXJsGAIgASgJEkEKC2F2YXRhcl90eXBlGAMgASgOMiwuYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuQXZhdGFyVHlwZSIjCgZCdWJibGUSDAoEdGV4dBgBIAEoCRILCgN1cmwYAiABKAkixgEKCEJ1YmJsZVYyEgwKBHRleHQYASABKAkSCwoDdXJsGAIgASgJEkEKC2J1YmJsZV90eXBlGAMgASgOMiwuYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuQnViYmxlVHlwZRIVCg1leHBvc3VyZV9vbmNlGAQgASgIEkUKDWV4cG9zdXJlX3R5cGUYBSABKA4yLi5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5FeHBvc3VyZVR5cGUiWwoGQnV0dG9uEgwKBHRleHQYASABKAkSQwoGYWN0aW9uGAIgASgOMjMuYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuVG9hc3RGdW5jdGlvblR5cGUiWAoOQnV6endvcmRDb25maWcSRgoIa2V5d29yZHMYASADKAsyNC5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5CdXp6d29yZFNob3dDb25maWcieAoSQnV6endvcmRTaG93Q29uZmlnEgwKBG5hbWUYASABKAkSDgoGc2NoZW1hGAIgASgJEg4KBnNvdXJjZRgDIAEoBRIKCgJpZBgEIAEoAxITCgtidXp6d29yZF9pZBgFIAEoAxITCgtzY2hlbWFfdHlwZRgGIAEoBSJ7CghDaGVja0JveBIMCgR0ZXh0GAEgASgJEjwKBHR5cGUYAiABKA4yLi5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5DaGVja2JveFR5cGUSFQoNZGVmYXVsdF92YWx1ZRgDIAEoCBIMCgRzaG93GAQgASgIIm8KCkNoZWNrQm94VjISDAoEdGV4dBgBIAEoCRI8CgR0eXBlGAIgASgOMi4uYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuQ2hlY2tib3hUeXBlEhUKDWRlZmF1bHRfdmFsdWUYAyABKAgiggIKC0NsaWNrQnV0dG9uEhUKDXBvcnRyYWl0X3RleHQYASADKAkSFgoObGFuZHNjYXBlX3RleHQYAiADKAkSGwoTcG9ydHJhaXRfdGV4dF9mb2N1cxgDIAMoCRIcChRsYW5kc2NhcGVfdGV4dF9mb2N1cxgEIAMoCRJBCgtyZW5kZXJfdHlwZRgFIAEoDjIsLmJpbGliaWxpLmNvbW11bml0eS5zZXJ2aWNlLmRtLnYxLlJlbmRlclR5cGUSDAoEc2hvdxgGIAEoCBI4CgZidWJibGUYByABKAsyKC5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5CdWJibGUiswIKDUNsaWNrQnV0dG9uVjISFQoNcG9ydHJhaXRfdGV4dBgBIAMoCRIWCg5sYW5kc2NhcGVfdGV4dBgCIAMoCRIbChNwb3J0cmFpdF90ZXh0X2ZvY3VzGAMgAygJEhwKFGxhbmRzY2FwZV90ZXh0X2ZvY3VzGAQgAygJEkEKC3JlbmRlcl90eXBlGAUgASgOMiwuYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuUmVuZGVyVHlwZRIXCg90ZXh0X2lucHV0X3Bvc3QYBiABKAgSFQoNZXhwb3N1cmVfb25jZRgHIAEoCBJFCg1leHBvc3VyZV90eXBlGAggASgOMi4uYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuRXhwb3N1cmVUeXBlIksKB0NvbW1hbmQSQAoLY29tbWFuZF9kbXMYASADKAsyKy5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5Db21tYW5kRG0i5gEKCUNvbW1hbmREbRIKCgJpZBgBIAEoAxILCgNvaWQYAiABKAMSCwoDbWlkGAMgASgDEg8KB2NvbW1hbmQYBCABKAkSDwoHY29udGVudBgFIAEoCRIQCghwcm9ncmVzcxgGIAEoBRINCgVjdGltZRgHIAEoCRINCgVtdGltZRgIIAEoCRINCgVleHRyYRgJIAEoCRINCgVpZFN0chgKIAEoCRIMCgR0eXBlGAsgASgFEhMKC2F1dG9fY3JlYXRlGAwgASgIEhIKCmNvdW50X2Rvd24YDSABKAUSDAoEYXR0chgOIAEoBSJQCg1EYW5tYWt1QUlGbGFnEj8KCGRtX2ZsYWdzGAEgAygLMi0uYmlsaWJpbGkuY29tbXVuaXR5LnNlcnZpY2UuZG0udjEuRGFubWFrdUZsYWciiwMKC0Rhbm1ha3VFbGVtEgoKAmlkGAEgASgDEhAKCHByb2dyZXNzGAIgASgFEgwKBG1vZGUYAyABKAUSEAoIZm9udHNpemUYBCABKAUSPwoFY29sb3IYBSABKA4yMC5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5EbUNvbG9yZnVsVHlwZRIQCghtaWRfaGFzaBgGIAEoCRIPCgdjb250ZW50GAcgASgJEg0KBWN0aW1lGAggASgDEg4KBndlaWdodBgJIAEoBRIOCgZhY3Rpb24YCiABKAkSDAoEcG9vbBgLIAEoBRIOCgZpZF9zdHIYDCABKAkSDAoEYXR0chgNIAEoBRIRCglhbmltYXRpb24YFiABKAkSDQoFZXh0cmEYFyABKAkSQgoIY29sb3JmdWwYGCABKA4yMC5iaWxpYmlsaS5jb21tdW5pdHkuc2VydmljZS5kbS52MS5EbUNvbG9yZnVsVHlwZRIMCgR0eXBlGBkgASgFEgsKA29pZBgaIAEoAyIpCgtEYW5tYWt1RmxhZxIMCgRkbWlkGAEgASgDEgwKBGZsYWcYAiABKAUiSwoRRGFubWFrdUZsYWdDb25maWcSEAoIcmVjX2ZsYWcYASABKAUSEAoIcmVjX3RleHQYAiABKAkSEgoKcmVjX3N3aXRjaBgDIAEoBSKLBwoYRGFubXVEZWZhdWx0UGxheWVyQ29uZmlnEikKIXBsYXllcl9kYW5tYWt1X3VzZV9kZWZhdWx0X2NvbmZpZxgBIAEoCB