UNPKG

@bililive-tools/manager

Version:
170 lines (169 loc) 6.14 kB
import { Emitter } from "mitt"; import { ChannelId, Message, Quality } from "./common.js"; import { RecorderProvider } from "./manager.js"; import { AnyObject, PickRequired, UnknownObject } from "./utils.js"; import type { NamespacedCache } from "./cache.js"; import type { DownloaderType } from "./downloader/index.js"; import type { XmlStreamStats } from "./xml_stream_controller.js"; type FormatName = "auto" | "flv" | "hls" | "fmp4" | "flv_only" | "hls_only" | "fmp4_only"; type CodecName = "auto" | "avc" | "hevc" | "avc_only" | "hevc_only"; export interface RecorderCreateOpts<E extends AnyObject = UnknownObject> { providerId: RecorderProvider<E>["id"]; channelId: ChannelId; id?: string; remarks?: string; weight?: number; disableAutoCheck?: boolean; disableProvideCommentsWhenRecording?: boolean; quality: Quality; streamPriorities: string[]; sourcePriorities: string[]; formatPriorities?: Array<"flv" | "hls">; source?: string; segment?: string; saveGiftDanma?: boolean; saveSCDanma?: boolean; /** 保存封面 */ saveCover?: boolean; /** 转封装为 mp4 */ convert2Mp4?: boolean; /** 身份验证 */ auth?: string; /** cookie所有者uid,B站弹幕录制 */ uid?: number | string; /** 画质匹配重试次数 */ qualityRetry?: number; /** 抖音是否使用双屏直播流,开启后如果是双屏直播,那么就使用拼接的流,默认为true */ doubleScreen?: boolean; /** B站是否使用m3u8代理 */ useM3U8Proxy?: boolean; /**B站m3u8代理url */ m3u8ProxyUrl?: string; /** B站自定义Host,用于替换直播流链接中的host */ customHost?: string; /** 流格式 */ formatName?: FormatName; /** 流编码 */ codecName?: CodecName; /** 选择使用的api,虎牙支持: auto,web,mp,wup,抖音支持:web,webHTML,mobile,userHTML */ api?: "auto" | "web" | "mp" | "wup" | "webHTML" | "mobile" | "userHTML" | "balance" | "random" | string; /** 标题关键词,如果直播间标题包含这些关键词,则不会自动录制,支持两种格式: * 1. 逗号分隔的关键词:'回放,录播,重播' * 2. 正则表达式:'/pattern/flags'(如:'/回放|录播/i') */ titleKeywords?: string; /** 用于指定录制文件格式,auto时,分段使用ts,不分段使用mp4 */ videoFormat?: "auto" | "ts" | "mkv" | "flv"; /** 录制类型 */ recorderType?: "auto" | "ffmpeg" | "mesio" | "bililive"; /** 流格式优先级 */ formatriorities?: Array<"flv" | "hls">; /** 只录制音频 */ onlyAudio?: boolean; /** 监控时间段 */ handleTime?: [string | null, string | null]; /** 控制弹幕是否使用服务端时间戳 */ useServerTimestamp?: boolean; extra?: Partial<E>; /** 调试等级 */ debugLevel?: "none" | "basic" | "verbose"; } export type SerializedRecorder<E extends AnyObject> = PickRequired<RecorderCreateOpts<E>, "id"> & Pick<Recorder<E>, "id" | "channelId" | "remarks" | "disableAutoCheck" | "quality" | "streamPriorities" | "sourcePriorities" | "extra" | "segment" | "saveSCDanma" | "saveCover" | "convert2Mp4" | "saveGiftDanma" | "disableProvideCommentsWhenRecording" | "liveInfo" | "uid" | "titleKeywords">; /** 录制状态,idle: 空闲中,recording: 录制中,stopping-record: 停止录制中,check-error: 检查错误,title-blocked: 标题黑名单 */ export type RecorderState = "idle" | "recording" | "stopping-record" | "check-error" | "title-blocked"; export type Progress = { time: string | null; }; export interface RecordHandle { id: string; stream: string; source: string; recorderType?: DownloaderType; url: string; downloaderArgs?: string[]; progress?: Progress; savePath: string; stop: (this: RecordHandle, reason?: string) => Promise<void>; cut: (this: RecordHandle) => Promise<void>; } export interface DebugLog { type: string | "common" | "ffmpeg" | "error"; text: string; } export type GetSavePath = (data: { owner: string; title: string; startTime: number; liveStartTime: Date; recordStartTime: Date; extraMs?: boolean; }) => string; export interface Recorder<E extends AnyObject = UnknownObject> extends Emitter<{ RecordStart: RecordHandle; RecordSegment?: RecordHandle; videoFileCreated: { filename: string; cover?: string; rawFilename?: string; }; videoFileCompleted: { filename: string; stats?: XmlStreamStats; }; progress: Progress; RecordStop: { recordHandle: RecordHandle; reason?: string; }; Updated: (string | keyof Recorder)[]; Message: Message; DebugLog: DebugLog; }>, RecorderCreateOpts<E> { id: string; extra: Partial<E>; availableStreams: string[]; availableSources: string[]; usedStream?: string; usedSource?: string; state: RecorderState; qualityRetry: number; uid?: number | string; liveInfo?: { living: boolean; owner: string; title: string; liveStartTime: Date; avatar: string; cover: string; liveId?: string; recordStartTime: Date; area?: string; }; tempStopIntervalCheck?: boolean; /** 缓存实例(命名空间) */ cache: NamespacedCache; getChannelURL: (this: Recorder<E>) => string; checkLiveStatusAndRecord: (this: Recorder<E>, opts: { getSavePath: GetSavePath; banLiveId?: string; isManualStart?: boolean; }) => Promise<RecordHandle | null>; recordHandle?: RecordHandle; toJSON: (this: Recorder<E>) => SerializedRecorder<E>; getLiveInfo: (this: Recorder<E>) => Promise<{ owner: string; title: string; avatar: string; cover: string; channelId: ChannelId; living: boolean; liveStartTime: Date; area: string; }>; getStream: (this: Recorder<E>) => Promise<{ source: string; name: string; url: string; }>; } export {};