@skax/hls-io 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/index.cjs +7328 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +7275 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.umd.js +7290 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/index.d.ts +4993 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/types/index.ts","../src/network/fetcher.ts","../src/crypto/decrypt.ts","../src/parser/tags.ts","../src/parser/m3u8-parser.ts","../src/utils/url.ts","../src/playlist/playlist-manager.ts","../src/segment/cache.ts","../src/segment/segment-manager.ts","../src/hls-io.ts"],"sourcesContent":["/**\n * Core type definitions for HLS-IO\n *\n * All exported enums, interfaces, and constants include bilingual documentation\n * (English + 中文) with `@example` code blocks for key types.\n *\n * @module types\n */\n\n// ============ HLS Tag Types ============\n\n/**\n * Standard HLS tag names as defined in the HTTP Live Streaming specification (RFC 8216).\n *\n * 标准 HLS 标签名称,定义于 HTTP Live Streaming 规范 (RFC 8216) 中。\n *\n * @example\n * ```ts\n * import { HLSTag } from \"hls-io\";\n *\n * if (line.startsWith(HLSTag.EXTINF)) {\n * const duration = parseFloat(line.split(\":\")[1]);\n * console.log(`Segment duration: ${duration}`);\n * }\n *\n * if (line.startsWith(HLSTag.EXT_X_STREAM_INF)) {\n * console.log(\"Found variant stream in master playlist\");\n * }\n * ```\n */\nexport enum HLSTag {\n /** Playlist header */\n /** 播放列表头 */\n EXTM3U = \"#EXTM3U\",\n /** Segment duration */\n /** 分片时长 */\n EXTINF = \"#EXTINF\",\n /** Byte range for a segment */\n /** 分片的字节范围 */\n EXT_X_BYTERANGE = \"#EXT-X-BYTERANGE\",\n /** Segment discontinuity marker */\n /** 分片间断标记 */\n EXT_X_DISCONTINUITY = \"#EXT-X-DISCONTINUITY\",\n /** Segment decryption key */\n /** 分片解密密钥 */\n EXT_X_KEY = \"#EXT-X-KEY\",\n /** Media initialization section (fMP4) */\n /** 媒体初始化段(fMP4),#EXT-X-MAP 并不局限于只能出现一次。在多段内容拼接(尤其是主内容与广告拼接)的场景下,为了重置解码器并加载不同的元数据,使用多个 #EXT-X-MAP 标签是正确的标准做法 */\n EXT_X_MAP = \"#EXT-X-MAP\",\n /** Program date-time of a segment */\n /** 分片的节目日期时间 */\n EXT_X_PROGRAM_DATE_TIME = \"#EXT-X-PROGRAM-DATE-TIME\",\n /** Date range metadata for a segment */\n /** 分片的日期范围元数据 */\n EXT_X_DATERANGE = \"#EXT-X-DATERANGE\",\n /** Maximum duration of any segment */\n /** 任意分片的最大时长 */\n EXT_X_TARGETDURATION = \"#EXT-X-TARGETDURATION\",\n /** Starting media sequence number */\n /** 起始媒体序列号 */\n EXT_X_MEDIA_SEQUENCE = \"#EXT-X-MEDIA-SEQUENCE\",\n /** Discontinuity sequence number */\n /** 间断序列号 */\n EXT_X_DISCONTINUITY_SEQUENCE = \"#EXT-X-DISCONTINUITY-SEQUENCE\",\n /** End of playlist (VOD only) */\n /** 播放列表结束标记(仅 VOD) */\n EXT_X_ENDLIST = \"#EXT-X-ENDLIST\",\n /** Playlist type declaration */\n /** 播放列表类型声明 */\n EXT_X_PLAYLIST_TYPE = \"#EXT-X-PLAYLIST-TYPE\",\n /** I-frame only playlist indicator */\n /** 仅包含 I 帧的播放列表标识 */\n EXT_X_I_FRAMES_ONLY = \"#EXT-X-I-FRAMES-ONLY\",\n /** Media rendition declaration (master) */\n /** 媒体呈现声明(主播放列表) */\n EXT_X_MEDIA = \"#EXT-X-MEDIA\",\n /** Variant stream declaration (master) */\n /** 变体流声明(主播放列表) */\n EXT_X_STREAM_INF = \"#EXT-X-STREAM-INF\",\n /** I-frame variant stream (master) */\n /** I 帧变体流(主播放列表) */\n EXT_X_I_FRAME_STREAM_INF = \"#EXT-X-I-FRAME-STREAM-INF\",\n /** Session data (master) */\n /** 会话数据(主播放列表) */\n EXT_X_SESSION_DATA = \"#EXT-X-SESSION-DATA\",\n /** Session key (master) */\n /** 会话密钥(主播放列表) */\n EXT_X_SESSION_KEY = \"#EXT-X-SESSION-KEY\",\n /** Independent segments declaration */\n /** 独立分片声明 */\n EXT_X_INDEPENDENT_SEGMENTS = \"#EXT-X-INDEPENDENT-SEGMENTS\",\n /** Start offset for playback */\n /** 播放起始偏移 */\n EXT_X_START = \"#EXT-X-START\",\n /** HLS protocol version */\n /** HLS 协议版本 */\n EXT_X_VERSION = \"#EXT-X-VERSION\",\n // LL-HLS tags\n /** Low-latency server control */\n /** 低延迟服务器控制 */\n EXT_X_SERVER_CONTROL = \"#EXT-X-SERVER-CONTROL\",\n /** Low-latency partial segment */\n /** 低延迟部分分片 */\n EXT_X_PART = \"#EXT-X-PART\",\n /** Low-latency partial segment info */\n /** 低延迟部分分片信息 */\n EXT_X_PART_INF = \"#EXT-X-PART-INF\",\n /** Low-latency preload hint */\n /** 低延迟预加载提示 */\n EXT_X_PRELOAD_HINT = \"#EXT-X-PRELOAD-HINT\",\n /** Low-latency rendition report */\n /** 低延迟呈现报告 */\n EXT_X_RENDITION_REPORT = \"#EXT-X-RENDITION-REPORT\",\n /** Low-latency skip directive */\n /** 低延迟跳过指令 */\n EXT_X_SKIP = \"#EXT-X-SKIP\",\n /** Segment gap indicator */\n /** 分片间隙标识 */\n EXT_X_GAP = \"#EXT-X-GAP\",\n /** Segment bitrate declaration */\n /** 分片码率声明 */\n EXT_X_BITRATE = \"#EXT-X-BITRATE\",\n // Common tags\n /** Allow cache directive */\n /** 允许缓存指令 */\n EXT_X_ALLOW_CACHE = \"#EXT-X-ALLOW-CACHE\",\n /** Content steering (CDN switching) */\n /** 内容引导(CDN 切换) */\n EXT_X_CONTENT_STEERING = \"#EXT-X-CONTENT-STEERING\",\n}\n\n// ============ Playlist Types ============\n\n/**\n * Represents the playback mode of an HLS playlist.\n *\n * 表示 HLS 播放列表的播放模式。\n *\n * @example\n * ```ts\n * import { PlaylistType } from \"hls-io\";\n *\n * if (playlist.type === PlaylistType.LIVE) {\n * console.log(\"Live stream — will keep refreshing\");\n * } else {\n * console.log(\"VOD stream — will play to the end\");\n * }\n * ```\n */\nexport enum PlaylistType {\n /** Live streaming — no EXT-X-ENDLIST tag */\n /** 直播 — 无 EXT-X-ENDLIST 标签 */\n LIVE = \"LIVE\",\n /** VOD / Event — has EXT-X-ENDLIST tag */\n /** 点播 / 事件 — 有 EXT-X-ENDLIST 标签 */\n VOD = \"VOD\",\n}\n\n/**\n * Media segment container format.\n *\n * 媒体分片容器格式。\n *\n * @example\n * ```ts\n * import { SegmentFormat } from \"hls-io\";\n *\n * const format = uri.endsWith(\".m4s\") ? SegmentFormat.FMP4 : SegmentFormat.TS;\n * ```\n */\nexport enum SegmentFormat {\n /** MPEG Transport Stream (.ts) */\n /** MPEG 传输流 (.ts) */\n TS = \"ts\",\n /** Fragmented MP4 (.m4s, .mp4) */\n /** 分片 MP4 (.m4s, .mp4) */\n FMP4 = \"fmp4\",\n}\n\n/**\n * Encryption method used for HLS content protection.\n *\n * HLS 内容保护所使用的加密方法。\n *\n * @example\n * ```ts\n * import { EncryptionMethod } from \"hls-io\";\n *\n * if (key.method === EncryptionMethod.AES_128) {\n * const decrypted = await decryptAES128(segment.data, key);\n * }\n * ```\n */\nexport enum EncryptionMethod {\n /** No encryption */\n /** 无加密 */\n NONE = \"NONE\",\n /** AES-128 CBC encryption */\n /** AES-128 CBC 加密 */\n AES_128 = \"AES-128\",\n /** Sample-AES encryption (FairPlay etc.) */\n /** Sample-AES 加密(FairPlay 等) */\n SAMPLE_AES = \"SAMPLE-AES\",\n}\n\n/**\n * Key format identifier for encrypted HLS streams.\n *\n * 加密 HLS 流的密钥格式标识符。\n *\n * @example\n * ```ts\n * import { KeyFormat } from \"hls-io\";\n *\n * if (key.keyFormat === KeyFormat.COMMON_ENCRYPTION) {\n * console.log(\"FairPlay key delivery detected\");\n * }\n * ```\n */\nexport enum KeyFormat {\n /** Raw identity format */\n /** 原始身份格式 */\n IDENTITY = \"identity\",\n /** Apple FairPlay key delivery */\n /** Apple FairPlay 密钥分发 */\n COMMON_ENCRYPTION = \"com.apple.streamingkeydelivery\",\n}\n\n// ============ Data Structures ============\n\n/**\n * Attributes parsed from an `EXT-X-KEY` tag.\n *\n * 从 `EXT-X-KEY` 标签中解析出的属性。\n *\n * @example\n * ```ts\n * import { KeyInfo, EncryptionMethod } from \"hls-io\";\n *\n * const key: KeyInfo = {\n * method: EncryptionMethod.AES_128,\n * uri: \"https://example.com/key.bin\",\n * };\n * ```\n */\nexport interface KeyInfo {\n /** Encryption method (NONE, AES-128, SAMPLE-AES) */\n /** 加密方法 */\n method: EncryptionMethod;\n /** URI to the key file */\n /** 密钥文件的 URI */\n uri?: string;\n /** Initialization vector (16 bytes) */\n /** 初始化向量(16 字节) */\n iv?: Uint8Array;\n /** Key format identifier */\n /** 密钥格式标识符 */\n keyFormat?: KeyFormat;\n /** Key format versions string */\n /** 密钥格式版本字符串 */\n keyFormatVersions?: string;\n}\n\n/**\n * Attributes parsed from an `EXT-X-MAP` tag (fMP4 init segment).\n *\n * 从 `EXT-X-MAP` 标签(fMP4 初始化段)中解析出的属性。\n *\n * @example\n * ```ts\n * import { MapInfo } from \"hls-io\";\n *\n * const map: MapInfo = {\n * uri: \"https://example.com/init.mp4\",\n * byteRange: { length: 1024 },\n * };\n * ```\n */\nexport interface MapInfo {\n /** URI of the initialization segment */\n /** 初始化段的 URI */\n uri: string;\n /** Optional byte range within the resource */\n /** 资源内可选的字节范围 */\n byteRange?: ByteRange;\n}\n\n/**\n * A byte range specification used in `EXT-X-BYTERANGE`, `EXT-X-MAP`, and `EXT-X-PART`.\n *\n * 用于 `EXT-X-BYTERANGE`、`EXT-X-MAP` 和 `EXT-X-PART` 的字节范围规范。\n *\n * @example\n * ```ts\n * import { ByteRange } from \"hls-io\";\n *\n * // Range \"1024@2048\" means 1024 bytes starting at offset 2048\n * const range: ByteRange = { length: 1024, offset: 2048 };\n *\n * // Range \"512\" means first 512 bytes of the resource\n * const range2: ByteRange = { length: 512 };\n * ```\n */\nexport interface ByteRange {\n /** Number of bytes */\n /** 字节数 */\n length: number;\n /** Starting byte offset (0 if not specified) */\n /** 起始字节偏移(未指定则为 0) */\n offset?: number;\n}\n\n/**\n * A partial segment (`EXT-X-PART`) for Low-Latency HLS delivery.\n *\n * 低延迟 HLS 中的部分分片(`EXT-X-PART`)。\n *\n * @example\n * ```ts\n * import { PartialSegment } from \"hls-io\";\n *\n * const part: PartialSegment = {\n * duration: 0.2,\n * uri: \"https://example.com/part-1.m4s\",\n * independent: true,\n * };\n * ```\n */\nexport interface PartialSegment {\n /** Duration of this partial segment in seconds */\n /** 部分分片时长(秒) */\n duration: number;\n /** URI of the partial segment */\n /** 部分分片的 URI */\n uri: string;\n /** Byte range within the resource */\n /** 资源内的字节范围 */\n byteRange?: ByteRange;\n /** Whether this partial segment can be decoded independently */\n /** 该部分分片是否可独立解码 */\n independent?: boolean;\n /** Whether this partial segment contains a gap */\n /** 该部分分片是否包含间隙 */\n gap?: boolean;\n}\n\n/**\n * Preload hint (`EXT-X-PRELOAD-HINT`) for upcoming partial segments or maps in LL-HLS.\n *\n * 低延迟 HLS 中用于即将到来的部分分片或 MAP 的预加载提示(`EXT-X-PRELOAD-HINT`)。\n *\n * @example\n * ```ts\n * import { PreloadHint } from \"hls-io\";\n *\n * const hint: PreloadHint = {\n * type: \"PART\",\n * uri: \"https://example.com/future-part.m4s\",\n * byteRangeStart: 0,\n * byteRangeLength: 12345,\n * };\n * ```\n */\nexport interface PreloadHint {\n /** Type of the preloaded resource (PART or MAP) */\n /** 预加载资源的类型(PART 或 MAP) */\n type: \"PART\" | \"MAP\";\n /** URI of the resource to preload */\n /** 待预加载资源的 URI */\n uri: string;\n /** Starting byte position for partial byte-range requests */\n /** 部分字节范围请求的起始字节位置 */\n byteRangeStart?: number;\n /** Expected byte length of the resource */\n /** 资源的预期字节长度 */\n byteRangeLength?: number;\n}\n\n/**\n * Rendition report (`EXT-X-RENDITION-REPORT`) for LL-HLS synchronization across renditions.\n *\n * 低延迟 HLS 中用于跨呈现同步的呈现报告(`EXT-X-RENDITION-REPORT`)。\n *\n * @example\n * ```ts\n * import { RenditionReport } from \"hls-io\";\n *\n * const report: RenditionReport = {\n * uri: \"https://example.com/audio-ll.m3u8\",\n * lastMSN: 42,\n * lastPart: 3,\n * };\n * ```\n */\nexport interface RenditionReport {\n /** URI of the rendition playlist */\n /** 呈现播放列表的 URI */\n uri: string;\n /** Last media sequence number */\n /** 最后的媒体序列号 */\n lastMSN?: number;\n /** Last partial segment index */\n /** 最后的部分分片索引 */\n lastPart?: number;\n}\n\n/**\n * Server control parameters (`EXT-X-SERVER-CONTROL`) for LL-HLS delivery tuning.\n *\n * 低延迟 HLS 中用于传输调优的服务器控制参数(`EXT-X-SERVER-CONTROL`)。\n *\n * @example\n * ```ts\n * import { ServerControl } from \"hls-io\";\n *\n * const control: ServerControl = {\n * canBlockReload: true,\n * canSkipUntil: 6,\n * holdBack: 6,\n * partHoldBack: 0.3,\n * };\n * ```\n */\nexport interface ServerControl {\n /** Maximum seconds a client may skip via EXT-X-SKIP */\n /** 客户端可通过 EXT-X-SKIP 跳过的最大秒数 */\n canSkipUntil?: number;\n /** Whether the server supports skipping by datetime */\n /** 服务器是否支持按日期时间跳过 */\n canSkipDateTimes?: boolean;\n /** Whether the server supports blocking playlist reloads */\n /** 服务器是否支持阻塞式播放列表重新加载 */\n canBlockReload?: boolean;\n /** Server hold-back duration in seconds */\n /** 服务器保持时长(秒) */\n holdBack?: number;\n /** Server part hold-back duration in seconds */\n /** 服务器部分分片保持时长(秒) */\n partHoldBack?: number;\n}\n\n/**\n * Date range metadata (`EXT-X-DATERANGE`) for timed metadata events (e.g. ad markers).\n *\n * 定时元数据事件的日期范围元数据(`EXT-X-DATERANGE`),例如广告标记。\n *\n * @example\n * ```ts\n * import { DateRange } from \"hls-io\";\n *\n * const adBreak: DateRange = {\n * id: \"ad-1\",\n * class: \"com.example.ad\",\n * startDate: \"2024-01-01T00:00:00Z\",\n * duration: 30,\n * attributes: { AD_ID: \"12345\" },\n * };\n * ```\n */\nexport interface DateRange {\n /** Unique identifier for this date range */\n /** 此日期范围的唯一标识符 */\n id: string;\n /** Client-defined class identifier */\n /** 客户端定义的类标识符 */\n class?: string;\n /** ISO-8601 start date */\n /** ISO-8601 起始日期 */\n startDate: string;\n /** ISO-8601 end date */\n /** ISO-8601 结束日期 */\n endDate?: string;\n /** Duration in seconds */\n /** 持续时长(秒) */\n duration?: number;\n /** Planned duration (can exceed actual) */\n /** 计划时长(可超过实际时长) */\n plannedDuration?: number;\n /** Whether this range ends on the next EXT-X-DATERANGE tag */\n /** 是否在下一个 EXT-X-DATERANGE 标签时结束 */\n endOnNext?: boolean;\n /** Arbitrary key-value attributes */\n /** 任意键值属性 */\n attributes: Record<string, string>;\n}\n\n/**\n * A single media segment extracted from the playlist.\n *\n * This is the core data unit in HLS-IO. Each segment represents one `EXTINF` entry\n * along with all associated metadata from preceding tags (KEY, MAP, BYTERANGE, etc.).\n *\n * 从播放列表中提取的单个媒体分片。\n *\n * 这是 HLS-IO 中的核心数据单元。每个分片代表一个 `EXTINF` 条目以及来自前面标签\n *(KEY、MAP、BYTERANGE 等)的所有关联元数据。\n *\n * @example\n * ```ts\n * import { Segment } from \"hls-io\";\n *\n * // Iterate over fetched segments\n * io.on(\"segmentReady\", (segment: Segment) => {\n * console.log(`Seq #${segment.sequence}: ${segment.duration}s`);\n * if (segment.key) {\n * console.log(\"Segment is encrypted:\", segment.key.method);\n * }\n * // Append segment.data to MediaSource buffer\n * sourceBuffer.appendBuffer(segment.data!);\n * });\n * ```\n */\nexport interface Segment {\n /** Sequence number (from EXT-X-MEDIA-SEQUENCE + index) */\n /** 序列号(来自 EXT-X-MEDIA-SEQUENCE + 索引) */\n sequence: number;\n /** Duration in seconds (from EXTINF) */\n /** 时长(秒,来自 EXTINF) */\n duration: number;\n /** URI of the segment file */\n /** 分片文件的 URI */\n uri: string;\n /** Title from EXTINF */\n /** EXTINF 中的标题 */\n title?: string;\n /** Byte range if any */\n /** 字节范围(如有) */\n byteRange?: ByteRange;\n /** Key for decryption */\n /** 解密密钥 */\n key?: KeyInfo;\n /** Map for fmp4 init segment */\n /** fMP4 初始化段的 MAP */\n map?: MapInfo;\n /** Raw EXT-X-MAP init data after fetch */\n /** 获取后的 EXT-X-MAP 初始化段原始数据 */\n mapData?: ArrayBuffer;\n /** Program date time */\n /** 节目日期时间 */\n programDateTime?: Date;\n /** Whether this segment has EXT-X-DISCONTINUITY before it */\n /** 此分片之前是否有 EXT-X-DISCONTINUITY */\n discontinuity: boolean;\n /** Whether this segment has EXT-X-GAP */\n /** 此分片是否有 EXT-X-GAP */\n gap: boolean;\n /** Date range associated with this segment */\n /** 与此分片关联的日期范围 */\n dateRange?: DateRange;\n /** Bitrate from EXT-X-BITRATE */\n /** 来自 EXT-X-BITRATE 的码率 */\n bitrate?: number;\n /** Raw segment data after fetch */\n /** 获取后的原始分片数据 */\n data?: ArrayBuffer;\n}\n\n/**\n * A parsed media (child) playlist.\n *\n * This is the result of parsing a non-master `.m3u8` file. It contains the actual\n * segment list and all playlist-level metadata.\n *\n * 解析后的媒体(子)播放列表。\n *\n * 这是解析非主 `.m3u8` 文件的结果。它包含实际的分片列表和所有播放列表级别的元数据。\n *\n * @example\n * ```ts\n * import { MediaPlaylist, PlaylistType } from \"hls-io\";\n *\n * io.on(\"manifestParsed\", (playlist: MediaPlaylist) => {\n * console.log(`Type: ${playlist.type}`);\n * console.log(`Target duration: ${playlist.targetDuration}s`);\n * console.log(`Segments: ${playlist.segments.length}`);\n * console.log(`Total duration: ${playlist.duration}s`);\n *\n * if (playlist.type === PlaylistType.VOD) {\n * console.log(\"This is a VOD playlist — will not refresh.\");\n * }\n *\n * // LL-HLS features\n * if (playlist.partialSegments) {\n * console.log(`LL-HLS parts: ${playlist.partialSegments.length}`);\n * }\n * });\n * ```\n */\nexport interface MediaPlaylist {\n /** Playlist type (LIVE or VOD) */\n /** 播放列表类型(LIVE 或 VOD) */\n type: PlaylistType;\n /** HLS protocol version */\n /** HLS 协议版本 */\n version: number;\n /** Maximum segment duration */\n /** 最大分片时长 */\n targetDuration: number;\n /** Starting media sequence number */\n /** 起始媒体序列号 */\n mediaSequence: number;\n /** Discontinuity sequence number */\n /** 间断序列号 */\n discontinuitySequence: number;\n /** Parsed segments */\n /** 解析后的分片列表 */\n segments: Segment[];\n /** LL-HLS partial segments */\n /** 低延迟 HLS 部分分片 */\n partialSegments?: PartialSegment[];\n /** LL-HLS preload hint */\n /** 低延迟 HLS 预加载提示 */\n preloadHint?: PreloadHint;\n /** LL-HLS rendition reports */\n /** 低延迟 HLS 呈现报告 */\n renditionReports?: RenditionReport[];\n /** LL-HLS server control */\n /** 低延迟 HLS 服务器控制 */\n serverControl?: ServerControl;\n /** Whether playlist allows cache */\n /** 播放列表是否允许缓存 */\n allowCache?: boolean;\n /** Whether playlist has independent segments */\n /** 播放列表是否包含独立分片 */\n independentSegments?: boolean;\n /** Start offset */\n /** 起始偏移 */\n startOffset?: number;\n /** If true, this is an I-frame only playlist */\n /** 如果为 true,则为仅 I 帧播放列表 */\n iFramesOnly?: boolean;\n /** Part target duration for LL-HLS */\n /** 低延迟 HLS 的部分分片目标时长 */\n partTargetDuration?: number;\n /** Total duration of all segments */\n /** 所有分片的总时长 */\n duration: number;\n /** Last segment's end time relative to start */\n /** 最后一个分片相对于起始的结束时间 */\n endTime: number;\n /** Raw playlist string for debugging */\n /** 原始播放列表字符串(用于调试) */\n raw: string;\n}\n\n/**\n * A variant stream entry parsed from an `EXT-X-STREAM-INF` tag in a master playlist.\n *\n * Represents one quality level / bitrate rendition available in the stream.\n *\n * 从主播放列表的 `EXT-X-STREAM-INF` 标签中解析出的变体流条目。\n *\n * 表示流中可用的一个质量级别 / 码率呈现。\n *\n * @example\n * ```ts\n * import { VariantStream } from \"hls-io\";\n *\n * const variant: VariantStream = {\n * uri: \"https://example.com/1080p.m3u8\",\n * bandwidth: 5000000,\n * resolution: { width: 1920, height: 1080 },\n * codecs: \"avc1.640028,mp4a.40.2\",\n * frameRate: 30,\n * score: 0,\n * attributes: {},\n * };\n * ```\n */\nexport interface VariantStream {\n /** URI of the variant's media playlist */\n /** 变体媒体播放列表的 URI */\n uri: string;\n /** Peak bandwidth in bits per second */\n /** 峰值带宽(比特/秒) */\n bandwidth: number;\n /** Average bandwidth in bits per second */\n /** 平均带宽(比特/秒) */\n averageBandwidth?: number;\n /** Codec string (e.g. \"avc1.640028,mp4a.40.2\") */\n /** 编解码器字符串 */\n codecs?: string;\n /** Video resolution */\n /** 视频分辨率 */\n resolution?: { width: number; height: number };\n /** Frame rate in frames per second */\n /** 帧率(帧/秒) */\n frameRate?: number;\n /** HDCP level */\n /** HDCP 级别 */\n hdcpLevel?: string;\n /** Associated audio group ID */\n /** 关联的音频组 ID */\n audio?: string;\n /** Associated video group ID */\n /** 关联的视频组 ID */\n video?: string;\n /** Associated subtitles group ID */\n /** 关联的字幕组 ID */\n subtitles?: string;\n /** Associated closed-captions group ID */\n /** 关联的隐藏字幕组 ID */\n closedCaptions?: string;\n /** Quality score assigned by ABR logic */\n /** ABR 逻辑分配的质量分数 */\n score?: number;\n /** Raw attributes from the tag */\n /** 标签中的原始属性 */\n attributes: Record<string, string>;\n}\n\n/**\n * A media rendition entry parsed from an `EXT-X-MEDIA` tag in a master playlist.\n *\n * Represents an alternate audio track, subtitle track, or closed-captions stream.\n *\n * 从主播放列表的 `EXT-X-MEDIA` 标签中解析出的媒体呈现条目。\n *\n * 表示备选音频轨道、字幕轨道或隐藏字幕流。\n *\n * @example\n * ```ts\n * import { MediaRendition } from \"hls-io\";\n *\n * const audioTrack: MediaRendition = {\n * type: \"AUDIO\",\n * groupId: \"audio-group\",\n * language: \"zh\",\n * name: \"Chinese\",\n * default: true,\n * autoselect: true,\n * forced: false,\n * uri: \"https://example.com/audio-zh.m3u8\",\n * };\n * ```\n */\nexport interface MediaRendition {\n /** Rendition type */\n /** 呈现类型 */\n type: \"AUDIO\" | \"VIDEO\" | \"SUBTITLES\" | \"CLOSED-CAPTIONS\";\n /** URI of the rendition playlist (may be absent for in-band tracks) */\n /** 呈现播放列表的 URI(带内轨道可能缺失) */\n uri?: string;\n /** Group identifier for grouping renditions */\n /** 用于分组呈现的组标识符 */\n groupId: string;\n /** Language code (ISO-639) */\n /** 语言代码(ISO-639) */\n language?: string;\n /** Associated language */\n /** 关联语言 */\n assocLanguage?: string;\n /** Human-readable name */\n /** 人类可读的名称 */\n name: string;\n /** Whether this is the default rendition */\n /** 是否为默认呈现 */\n default: boolean;\n /** Whether this rendition should be auto-selected */\n /** 此呈现是否应自动选择 */\n autoselect: boolean;\n /** Whether this rendition is forced (e.g. forced subtitles) */\n /** 此呈现是否为强制呈现(如强制字幕) */\n forced: boolean;\n /** In-stream identifier for closed-captions */\n /** 隐藏字幕的流内标识符 */\n instreamId?: string;\n /** Rendition characteristics string */\n /** 呈现特征字符串 */\n characteristics?: string;\n /** Audio channel count */\n /** 音频声道数 */\n channels?: string;\n}\n\n/**\n * A parsed master (multivariant) playlist.\n *\n * Contains the list of available quality variants and media renditions.\n * This is the top-level playlist that references child media playlists.\n *\n * 解析后的主(多变体)播放列表。\n *\n * 包含可用的质量变体和媒体呈现列表。这是引用子媒体播放列表的顶级播放列表。\n *\n * @example\n * ```ts\n * import { MasterPlaylist, HlsIOConfig, VariantStream } from \"hls-io\";\n *\n * io.on(\"manifestParsed\", (playlist: MasterPlaylist) => {\n * console.log(`Variants: ${playlist.variants.length}`);\n *\n * // Select the highest bandwidth variant\n * const bestVariant: VariantStream = playlist.variants\n * .sort((a, b) => b.bandwidth - a.bandwidth)[0];\n * console.log(`Best: ${bestVariant.resolution?.width}x${bestVariant.resolution?.height}`);\n *\n * // List available audio tracks\n * const audioTracks = playlist.mediaRenditions?.filter(r => r.type === \"AUDIO\") ?? [];\n * audioTracks.forEach(track => console.log(`Audio: ${track.name} (${track.language})`));\n * });\n * ```\n */\nexport interface MasterPlaylist {\n /** HLS protocol version */\n /** HLS 协议版本 */\n version: number;\n /** Available variant streams */\n /** 可用的变体流列表 */\n variants: VariantStream[];\n /** Available media renditions (audio, subtitles, etc.) */\n /** 可用的媒体呈现(音频、字幕等) */\n mediaRenditions?: MediaRendition[];\n /** Session data key-value pairs */\n /** 会话数据键值对 */\n sessionData?: Record<string, string>;\n /** Session keys for decryption */\n /** 会话解密密钥 */\n sessionKeys?: KeyInfo[];\n /** Whether the playlist has independent segments */\n /** 播放列表是否包含独立分片 */\n independentSegments?: boolean;\n /** Raw playlist string for debugging */\n /** 原始播放列表字符串(用于调试) */\n raw: string;\n}\n\n// ============ Configuration ============\n\n/**\n * Configuration options for creating an HlsIO instance.\n *\n * This is the primary configuration object passed to the HlsIO constructor.\n * All properties are optional except `url`.\n *\n * 用于创建 HlsIO 实例的配置选项。\n *\n * 这是传递给 HlsIO 构造函数的主要配置对象。除 `url` 外,所有属性均为可选。\n *\n * @example\n * ```ts\n * import { HlsIO, HlsIOConfig, PlaylistType } from \"hls-io\";\n *\n * // Minimal config — auto-detect playlist type\n * const config: HlsIOConfig = {\n * url: \"https://example.com/master.m3u8\",\n * };\n *\n * // Full config for a live stream\n * const liveConfig: HlsIOConfig = {\n * url: \"https://example.com/live.m3u8\",\n * playlistType: PlaylistType.LIVE,\n * bufferAhead: 5,\n * heartbeatInterval: 10000,\n * lowLatencyMode: true,\n * debug: true,\n * };\n *\n * // VOD config with large cache\n * const vodConfig: HlsIOConfig = {\n * url: \"https://example.com/vod.m3u8\",\n * playlistType: PlaylistType.VOD,\n * maxCacheSize: 100,\n * cachePolicy: \"lru\",\n * onLastSegment: () => console.log(\"Playback complete!\"),\n * };\n *\n * const io = new HlsIO(config);\n * ```\n */\nexport interface HlsIOConfig {\n /** URL of the M3U8 playlist */\n /** M3U8 播放列表的 URL */\n url?: string;\n /** Playlist type: auto-detect (default), live, or vod */\n /** 播放列表类型:自动检测(默认)、直播或点播 */\n playlistType?: PlaylistType;\n /** Minimum number of segments to keep buffered ahead */\n /** 前方保持缓冲的最小分片数 */\n bufferAhead?: number;\n /** Maximum number of segments to cache (VOD mode) */\n /** 最大缓存分片数(VOD 模式) */\n maxCacheSize?: number;\n /** Heartbeat interval in ms (0 = disabled) */\n /** 心跳间隔(毫秒,0 = 禁用) */\n heartbeatInterval?: number;\n /** Playlist refresh interval in ms (0 = auto-calculate) */\n /** 播放列表刷新间隔(毫秒,0 = 自动计算) */\n playlistRefreshInterval?: number;\n /** Custom fetch options (CORS mode, credentials, headers) */\n /** 自定义 fetch 选项(CORS 模式、凭证、请求头) */\n fetchOptions?: RequestInit;\n /** Whether to automatically resolve relative URIs */\n /** 是否自动解析相对 URI */\n resolveRelativeUris?: boolean;\n /** Base URL for resolving relative URIs */\n /** 用于解析相对 URI 的基础 URL */\n baseUrl?: string;\n /** Enable debug logging */\n /** 启用调试日志 */\n debug?: boolean;\n /** Enable LL-HLS low-latency mode */\n /** 启用低延迟 HLS 模式 */\n lowLatencyMode?: boolean;\n /** LL-HLS part target duration (used with _HLS_part trick) */\n /** 低延迟 HLS 部分分片目标时长(与 _HLS_part 技巧配合使用) */\n partTargetDuration?: number;\n /** For VOD: callback when last segment is fetched */\n /** VOD 模式:当最后一个分片获取完成时的回调 */\n onLastSegment?: () => void;\n /** Segment cache eviction policy */\n /** 分片缓存淘汰策略 */\n cachePolicy?: \"lru\" | \"fifo\";\n /**\n * Whether to follow HTTP redirects and use the final redirected URL\n * for subsequent playlist requests.\n *\n * When `true`, after a redirect the library updates `config.url` to the\n * final redirected address. When `false` (default), the library always\n * requests the original URL, even after a redirect.\n *\n * @default false\n */\n /**\n * 是否跟随 HTTP 重定向,并在后续请求中使用最终的重定向地址。\n *\n * 为 `true` 时,重定向后库会将 `config.url` 更新为最终的重定向地址。\n * 为 `false`(默认)时,库始终请求原始地址,即使发生重定向也不改变。\n *\n * @default true\n */\n followRedirectUrl?: boolean;\n /** Request timeout in ms for M3U8 playlist (0 = none, default: 30000). Also used as fallback for segmentTimeout. */\n /** M3U8 播放列表请求超时(毫秒,0 = 无,默认 30000)。同时作为 segmentTimeout 的 fallback。 */\n /** 请求超时时间(毫秒,0 = 无超时,默认: 30000) */\n timeout?: number;\n /** Segment/MAP/KEY fetch timeout in ms (0 = none, default: use timeout). */\n /** 分片/MAP/KEY 获取超时(毫秒,0 = 无,默认使用 timeout)。 */\n segmentTimeout?: number;\n /** Max retry attempts for segment/map fetch (default: 5) */\n /** 分片/重试请求最大次数 (默认: 5) */\n segmentRetryCount?: number;\n /** Base delay between segment/map retries in ms (default: 500) */\n /** 分片/重试之间的基础延迟,单位毫秒 (默认: 500) */\n segmentRetryDelay?: number;\n /**\n * Segment/map retry backoff multiplier (default: 2)\n * 分片/重试延迟成倍增加系数 (默认: 2)\n * 每次重试后,延迟将乘以此系数,以实现指数级回退。\n * 例如,第一次重试后延迟为 500ms,第二次重试后延迟为 1000ms,第三次重试后延迟为 2000ms,依此类推。\n * 设置为 1 则表示没有回退,每次重试之间的延迟保持不变。\n * 设置为 0 则表示禁用重试回退,每次重试立即进行,没有任何延迟。\n * 注意:过高的回退倍数可能会导致重试延迟过长,影响用户体验;过低的回退倍数可能会导致频繁重试,增加服务器负载。\n * 根据实际网络状况和服务器响应时间调整此参数,以找到最佳的重试策略。\n *\n * MAPs (fMP4 init segments) will use the same retry configuration as media segments.\n * 如果 MAP(fMP4 初始化段)使用与媒体分片相同的重试配置,则它们将遵循相同的重试次数、延迟和回退倍数设置。\n * @default 2\n */\n segmentRetryBackoff?: number;\n}\n\n/**\n * Default configuration values applied when the user omits optional properties.\n *\n * 当用户省略可选属性时应用的默认配置值。\n *\n * @example\n * ```ts\n * import { DEFAULT_CONFIG } from \"hls-io\";\n *\n * // Merge with user config\n * const config = { ...DEFAULT_CONFIG, url: \"https://example.com/stream.m3u8\" };\n * ```\n */\nexport const DEFAULT_CONFIG: Required<HlsIOConfig> = {\n url: \"\",\n playlistType: PlaylistType.LIVE,\n bufferAhead: 5,\n maxCacheSize: 50,\n heartbeatInterval: 30000,\n playlistRefreshInterval: 0,\n fetchOptions: {},\n resolveRelativeUris: true,\n baseUrl: \"\",\n debug: false,\n lowLatencyMode: false,\n partTargetDuration: 0,\n onLastSegment: () => {},\n cachePolicy: \"lru\",\n followRedirectUrl: true,\n timeout: 30000,\n segmentRetryCount: 5,\n segmentTimeout: 0,\n segmentRetryDelay: 500,\n segmentRetryBackoff: 2,\n};\n\n// ============ Events ============\n\n/**\n * Event names emitted by the HlsIO instance.\n *\n * Use these enum values to subscribe to specific lifecycle and data events via `io.on()`.\n *\n * HlsIO 实例发出的事件名称。\n *\n * 通过这些枚举值,可以通过 `io.on()` 订阅特定的生命周期和数据事件。\n *\n * @example\n * ```ts\n * import { HlsIO, HlsIOEvents } from \"hls-io\";\n *\n * const io = new HlsIO({ url: \"https://example.com/live.m3u8\" });\n *\n * io.on(HlsIOEvents.SEGMENT_READY, (segment) => {\n * // Feed segment into MediaSource\n * });\n *\n * io.on(HlsIOEvents.ERROR, (error) => {\n * console.error(`Error [${error.type}]: ${error.message}`);\n * });\n *\n * io.on(HlsIOEvents.PLAYLIST_TYPE_DETECTED, (type) => {\n * console.log(`Detected: ${type}`);\n * });\n * ```\n */\nexport enum HlsIOEvents {\n /** Playlist loaded/refreshed */\n /** 播放列表已加载/已刷新 */\n PLAYLIST_LOADED = \"playlistLoaded\",\n /** New segments available (live mode) */\n /** 新分片可用(直播模式) */\n SEGMENTS_UPDATED = \"segmentsUpdated\",\n /** A segment has been fetched and is ready */\n /** 分片已获取并准备就绪 */\n SEGMENT_READY = \"segmentReady\",\n /** Last segment of VOD fetched */\n /** VOD 的最后一个分片已获取 */\n LAST_SEGMENT = \"lastSegment\",\n /** An error occurred */\n /** 发生错误 */\n ERROR = \"error\",\n /** Heartbeat sent */\n /** 心跳已发送 */\n HEARTBEAT = \"heartbeat\",\n /** Playlist type determined */\n /** 播放列表类型已确定 */\n PLAYLIST_TYPE_DETECTED = \"playlistTypeDetected\",\n /** Segment discontinuity detected */\n /** 检测到分片间断 */\n DISCONTINUITY = \"discontinuity\",\n /** Playlist ended (VOD) */\n /** 播放列表已结束(VOD) */\n ENDED = \"ended\",\n /** Manifest / playlist parsing complete */\n /** 清单/播放列表解析完成 */\n MANIFEST_PARSED = \"manifestParsed\",\n /** Decryption started */\n /** 解密开始 */\n DECRYPTING = \"decrypting\",\n /** Decryption complete */\n /** 解密完成 */\n DECRYPTED = \"decrypted\",\n /** LL-HLS partial segment */\n /** 低延迟 HLS 部分分片 */\n PART_SEGMENT = \"partSegment\",\n /** EXT-X-MAP init segment loaded */\n /** EXT-X-MAP 初始化段加载完成 */\n MAP_LOADED = \"mapLoaded\",\n /** Buffer stall warning */\n /** 缓冲区停滞警告 */\n BUFFER_STALL = \"bufferStall\",\n /**\n * Fired whenever the cumulative duration of all observed segments changes.\n *\n * 当所有已观测分片的累计时长发生变化时触发。\n *\n * @example\n * ```ts\n * io.on(HlsIOEvents.DURATION_UPDATED, (duration) => {\n * console.log(`Total duration: ${duration.toFixed(2)}s`);\n * });\n * ```\n */\n DURATION_UPDATED = \"durationUpdated\",\n /**\n * Fired whenever the accumulated buffered media time changes (i.e. a segment\n * has been downloaded and counted towards the buffer).\n *\n * 当已缓冲的媒体时长累计值发生变化时触发(即有分片已下载并计入缓冲)。\n *\n * @example\n * ```ts\n * io.on(HlsIOEvents.BUFFERED_TIME_UPDATED, (bufferedTime) => {\n * console.log(`Buffered: ${bufferedTime.toFixed(2)}s`);\n * });\n * ```\n */\n BUFFERED_TIME_UPDATED = \"bufferedTimeUpdated\",\n /** Request timed out */\n /** 请求超时 */\n TIMEOUT = \"timeout\",\n}\n\n/**\n * Type-safe event-to-callback mapping for HlsIO.\n *\n * Maps each event from {@link HlsIOEvents} to its corresponding callback signature.\n * This is used internally by the typed event emitter to provide autocompletion and\n * type checking when subscribing to events.\n *\n * HlsIO 的类型安全事件到回调映射。\n *\n * 将 {@link HlsIOEvents} 中的每个事件映射到其对应的回调签名。由类型化事件发射器在内部使用,\n * 以便在订阅事件时提供自动补全和类型检查。\n *\n * @example\n * ```ts\n * import { HlsIOEventMap, HlsIOEvents, HlsIO } from \"hls-io\";\n *\n * // Type-safe event listeners\n * const io = new HlsIO({ url: \"https://example.com/live.m3u8\" });\n *\n * // TypeScript knows the callback parameter types:\n * io.on(HlsIOEvents.ERROR, (error) => {\n * // error is typed as HlsIOError\n * console.log(error.type);\n * });\n *\n * io.on(HlsIOEvents.SEGMENT_READY, (segment) => {\n * // segment is typed as Segment\n * console.log(segment.sequence, segment.duration);\n * });\n *\n * io.on(HlsIOEvents.DURATION_UPDATED, (duration) => {\n * // duration is typed as number (seconds)\n * console.log(`Total duration: ${duration.toFixed(2)}s`);\n * });\n *\n * io.on(HlsIOEvents.BUFFERED_TIME_UPDATED, (bufferedTime) => {\n * // bufferedTime is typed as number (seconds)\n * console.log(`Buffered: ${bufferedTime.toFixed(2)}s`);\n * });\n * ```\n */\nexport interface HlsIOEventMap {\n [HlsIOEvents.PLAYLIST_LOADED]: (playlist: MediaPlaylist | MasterPlaylist) => void;\n [HlsIOEvents.SEGMENTS_UPDATED]: (segments: Segment[]) => void;\n [HlsIOEvents.SEGMENT_READY]: (segment: Segment) => void;\n [HlsIOEvents.LAST_SEGMENT]: (segment: Segment) => void;\n [HlsIOEvents.ERROR]: (error: HlsIOError) => void;\n [HlsIOEvents.PLAYLIST_TYPE_DETECTED]: (type: PlaylistType) => void;\n [HlsIOEvents.DISCONTINUITY]: (segment: Segment) => void;\n [HlsIOEvents.ENDED]: () => void;\n [HlsIOEvents.MANIFEST_PARSED]: (playlist: MediaPlaylist | MasterPlaylist) => void;\n [HlsIOEvents.DECRYPTING]: (segment: Segment) => void;\n [HlsIOEvents.DECRYPTED]: (segment: Segment) => void;\n [HlsIOEvents.PART_SEGMENT]: (part: PartialSegment) => void;\n [HlsIOEvents.MAP_LOADED]: (info: { mapUri: string; data: ArrayBuffer }) => void;\n [HlsIOEvents.BUFFER_STALL]: (info: { buffered: number; needed: number }) => void;\n [HlsIOEvents.DURATION_UPDATED]: (duration: number) => void;\n [HlsIOEvents.BUFFERED_TIME_UPDATED]: (bufferedTime: number) => void;\n [HlsIOEvents.TIMEOUT]: (error: HlsIOError) => void;\n}\n\n/**\n * Structured error object emitted on the `error` event.\n *\n * Provides categorized error information to simplify error-handling logic.\n *\n * 在 `error` 事件上发出的结构化错误对象。\n *\n * 提供分类的错误信息,以简化错误处理逻辑。\n *\n * @example\n * ```ts\n * import { HlsIOError } from \"hls-io\";\n *\n * io.on(\"error\", (error: HlsIOError) => {\n * switch (error.type) {\n * case \"NETWORK\":\n * console.error(\"Network error:\", error.originalError?.message);\n * break;\n * case \"PARSE\":\n * console.error(\"Parse error in:\", error.playlistUrl);\n * break;\n * case \"DECRYPT\":\n * console.error(\"Decrypt error for:\", error.segmentUri);\n * break;\n * }\n *\n * });\n * ```\n */\nexport interface HlsIOError {\n /** Error category */\n /** 错误类别 */\n type: \"NETWORK\" | \"PARSE\" | \"DECRYPT\" | \"CONFIG\" | \"TIMEOUT\" | \"UNKNOWN\";\n /**\n * Optional sub-type for more specific error identification (e.g. \"M3U8\", \"TS\", \"FMP4\", \"KEY\", \"MAP\", \"PART\").\n *\n * 用于更具体错误识别的可选子类型(例如 \"M3U8\"、\"TS\"、\"FMP4\"、\"KEY\"、\"MAP\"、\"PART\")。\n */\n subType?: \"M3U8\" | \"SEGMENT\" | \"KEY\" | \"MAP\" | \"PART\";\n /** Human-readable error message */\n /** 人类可读的错误信息 */\n message: string;\n /** The original thrown Error object */\n /** 原始抛出的 Error 对象 */\n originalError?: Error;\n /** URI of the segment that caused the error */\n /** 导致错误的分片 URI */\n segmentUri?: string;\n /** URL of the playlist that caused the error */\n /** 导致错误的播放列表 URL */\n playlistUrl?: string;\n /** Whether this error is eligible for automatic retry */\n}\n\n// ============ Network ============\n\n/**\n * Result of a successful fetch operation.\n *\n * Wraps the raw binary data along with HTTP response metadata.\n *\n * 成功获取操作的结果。\n *\n * 包装原始二进制数据以及 HTTP 响应元数据。\n *\n * @example\n * ```ts\n * import { FetchResult } from \"hls-io\";\n *\n * const result: FetchResult = {\n * data: new ArrayBuffer(1024),\n * url: \"https://example.com/segment-1.ts\",\n * contentType: \"video/mp2t\",\n * contentLength: 500000,\n * };\n * ```\n */\nexport interface FetchResult {\n /** Response body as ArrayBuffer */\n /** 响应体(ArrayBuffer) */\n data: ArrayBuffer;\n /** Final URL after redirects */\n /** 重定向后的最终 URL */\n url: string;\n /** Content-Type header value */\n /** Content-Type 响应头值 */\n contentType?: string;\n /** Content-Length header value */\n /** Content-Length 响应头值 */\n contentLength?: number;\n}\n\n// ============ Cache ============\n\n/**\n * A single entry in the segment data cache.\n *\n * Stores a fetched segment along with its metadata for cache management.\n *\n * 分片数据缓存中的单个条目。\n *\n * 存储已获取的分片及其元数据,用于缓存管理。\n *\n * @example\n * ```ts\n * import { CacheEntry } from \"hls-io\";\n *\n * const entry: CacheEntry = {\n * segment: mySegment,\n * data: new ArrayBuffer(65536),\n * timestamp: Date.now(),\n * key: \"seq_42\",\n * };\n * ```\n */\nexport interface CacheEntry {\n /** The segment metadata */\n /** 分片元数据 */\n segment: Segment;\n /** Raw binary segment data */\n /** 原始二进制分片数据 */\n data: ArrayBuffer;\n /** Timestamp when this entry was cached (ms) */\n /** 此条目缓存时的时间戳(毫秒) */\n timestamp: number;\n /** Cache key (e.g. sequence number string) */\n /** 缓存键(例如序列号字符串) */\n key: string;\n}\n\n/**\n * Statistics about the segment cache.\n *\n * Useful for monitoring cache efficiency and detecting memory pressure.\n *\n * 分片缓存的统计信息。\n *\n * 用于监控缓存效率并检测内存压力。\n *\n * @example\n * ```ts\n * import { CacheStats } from \"hls-io\";\n *\n * const stats: CacheStats = {\n * size: 42,\n * hits: 150,\n * misses: 5,\n * evictions: 3,\n * };\n * console.log(`Hit rate: ${(stats.hits / (stats.hits + stats.misses) * 100).toFixed(1)}%`);\n * ```\n */\nexport interface CacheStats {\n /** Current number of cached entries */\n /** 当前缓存条目数 */\n size: number;\n /** Number of successful cache lookups */\n /** 成功缓存查找的次数 */\n hits: number;\n /** Number of failed cache lookups */\n /** 失败缓存查找的次数 */\n misses: number;\n /** Number of evicted entries */\n /** 被淘汰的条目数 */\n evictions: number;\n}\n","/**\n * Network Fetcher\n * 网络抓取器\n *\n * A thin HTTP client wrapping the Fetch API with timeout, abort, CORS config,\n * and heartbeat support. Does NOT contain retry logic — retry is handled by\n * each caller (PlaylistManager, SegmentManager) with their own strategies.\n *\n * 封装 Fetch API 的轻量 HTTP 客户端,提供超时、中止、CORS 配置和心跳支持。\n * 不包含重试逻辑 — 重试由各调用方(PlaylistManager、SegmentManager)\n * 根据各自策略自行处理。\n *\n * @module fetcher\n */\n\nimport { FetchResult } from \"../types\";\nimport { Logger } from \"@skax/logger\";\n\n/**\n * Fetcher — a single-request HTTP client for HLS resources.\n *\n * Each call to `fetch` or `fetchSegment` performs exactly one network request.\n * Callers are responsible for implementing retry/backoff on top of this.\n *\n * Fetcher — 用于 HLS 资源的单次请求 HTTP 客户端。\n *\n * 每次调用 `fetch` 或 `fetchSegment` 只执行一次网络请求。\n * 调用方负责在此基础上实现重试/退避。\n */\nexport class Fetcher {\n private _logger: Logger;\n private _fetchOptions: RequestInit;\n private _timeout: number;\n private _heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private _abortControllers: Map<string, AbortController> = new Map();\n\n /**\n * @param fetchOptions - Base RequestInit applied to every request.\n * @param debug - Enable DEBUG-level logging.\n * @param timeout - Request timeout in ms (0 = no timeout, default 30000).\n */\n constructor(fetchOptions: RequestInit = {}, debug = false, timeout = 30000) {\n this._logger = new Logger(\"HLS-IO:Fetcher\", debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n this._fetchOptions = {\n mode: \"cors\",\n credentials: \"omit\",\n ...fetchOptions,\n };\n this._timeout = timeout;\n }\n\n // ==================== Heartbeat ====================\n\n startHeartbeat(url: string, intervalMs: number, callback: (timestamp: number) => void): void {\n this.stopHeartbeat();\n this._logger.info(`Starting heartbeat every ${intervalMs}ms to ${url}`);\n\n this._heartbeatTimer = setInterval(async () => {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(url, {\n method: \"HEAD\",\n signal: controller.signal,\n ...this._fetchOptions,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n callback(Date.now());\n } else {\n this._logger.warn(`Heartbeat failed with status ${response.status}`);\n }\n } catch (error) {\n this._logger.warn(`Heartbeat error: ${(error as Error).message}`);\n // Do NOT call callback on failure — only successful heartbeats are reported\n // 失败时不调用回调 — 仅报告成功的心跳\n }\n }, intervalMs);\n }\n\n stopHeartbeat(): void {\n if (this._heartbeatTimer) {\n clearInterval(this._heartbeatTimer);\n this._heartbeatTimer = null;\n this._logger.info(\"Heartbeat stopped\");\n }\n }\n\n isHeartbeatActive(): boolean {\n return this._heartbeatTimer !== null;\n }\n\n // ==================== Core Fetch ====================\n\n /**\n * Perform a single HTTP GET request. No retry.\n * 执行单次 HTTP GET 请求。不重试。\n *\n * @param url - The URL to fetch.\n * @param options - Optional per-request overrides.\n * @returns FetchResult on success.\n * @throws Error on network failure or non-2xx response.\n */\n async fetch(url: string, options?: RequestInit & { timeout?: number }): Promise<FetchResult> {\n const fetchOptionsHeaders: Record<string, string> = {};\n if (this._fetchOptions.headers instanceof Headers) {\n this._fetchOptions.headers.forEach((value, key) => {\n fetchOptionsHeaders[key] = value;\n });\n } else if (this._fetchOptions.headers) {\n const headers = this._fetchOptions.headers as Record<string, string>;\n for (const key in headers) {\n if (Object.prototype.hasOwnProperty.call(headers, key)) {\n fetchOptionsHeaders[key] = headers[key];\n }\n }\n }\n\n const optionsHeaders: Record<string, string> = {};\n if (options?.headers instanceof Headers) {\n options.headers.forEach((value, key) => {\n optionsHeaders[key] = value;\n });\n } else if (options?.headers) {\n const headers = options.headers as Record<string, string>;\n for (const key in headers) {\n if (Object.prototype.hasOwnProperty.call(headers, key)) {\n optionsHeaders[key] = headers[key];\n }\n }\n }\n\n const mergedOptions = {\n ...this._fetchOptions,\n ...options,\n headers: {\n ...fetchOptionsHeaders,\n ...optionsHeaders,\n },\n };\n console.warn(\"Fetch options:\", mergedOptions);\n const controller = new AbortController();\n this._abortControllers.set(url, controller);\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n const effectiveTimeout = options?.timeout ?? this._timeout;\n if (effectiveTimeout > 0) {\n timeoutId = setTimeout(() => controller.abort(), effectiveTimeout);\n }\n\n try {\n const response = await fetch(url, {\n ...mergedOptions,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const err = new Error(`HTTP ${response.status}: ${response.statusText}`) as Error & { status: number };\n err.status = response.status;\n throw err;\n }\n\n const data = await response.arrayBuffer();\n\n return {\n data,\n url: response.url || url,\n contentType: response.headers.get(\"content-type\") || undefined,\n contentLength: parseInt(response.headers.get(\"content-length\") || \"0\", 10) || undefined,\n };\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n this._abortControllers.delete(url);\n }\n }\n\n /**\n * Fetch a media segment with optional byte-range header. No retry.\n * 获取媒体分片,可选字节范围头。不重试。\n *\n * @param url - The segment URL.\n * @param byteRange - Optional byte range.\n * @returns FetchResult on success.\n * @throws Error on failure.\n */\n async fetchSegment(url: string, byteRange?: { length: number; offset?: number }, options?: RequestInit): Promise<FetchResult> {\n const headers: Record<string, string> = {\n ...((options?.headers as Record<string, string>) || {}),\n };\n\n if (byteRange) {\n const start = byteRange.offset ?? 0;\n const end = start + byteRange.length - 1;\n headers[\"Range\"] = `bytes=${start}-${end}`;\n }\n\n return this.fetch(url, { ...options, headers });\n }\n\n // ==================== Abort ====================\n\n cancelRequest(url: string): void {\n const controller = this._abortControllers.get(url);\n if (controller) {\n controller.abort();\n this._abortControllers.delete(url);\n this._logger.debug(`Cancelled request to ${url}`);\n }\n }\n\n cancelAll(): void {\n for (const [url, controller] of this._abortControllers) {\n controller.abort();\n this._logger.debug(`Cancelled request to ${url}`);\n }\n this._abortControllers.clear();\n }\n\n // ==================== Config ====================\n\n updateFetchOptions(options: RequestInit): void {\n this._fetchOptions = { ...this._fetchOptions, ...options };\n }\n}\n\n/**\n * Create a Fetcher pre-configured for CORS requests.\n * 创建一个预配置了 CORS 的 Fetcher。\n */\nexport function createCorsFetcher(corsMode: RequestMode = \"cors\", credentials: RequestCredentials = \"omit\", customHeaders: Record<string, string> = {}): Fetcher {\n return new Fetcher({\n mode: corsMode,\n credentials,\n headers: customHeaders,\n cache: \"no-cache\",\n });\n}\n","/**\n * Decryption module for HLS-IO — HLS-IO 解密模块\n *\n * Provides AES-128-CBC and SAMPLE-AES decryption entry points.\n * 提供 AES-128-CBC 和 SAMPLE-AES 解密的入口点。\n *\n * Currently provides the structure and interfaces for decryption.\n * Actual implementation depends on the Web Crypto API (SubtleCrypto).\n * 当前提供解密的结构和接口,实际实现依赖 Web Crypto API (SubtleCrypto)。\n *\n * Decryption Strategy — 解密策略:\n * 1. **AES-128-CBC**: standard HLS encryption\n * - Key from EXT-X-KEY URI\n * - IV from EXT-X-KEY IV attribute, or use media sequence as IV\n * 标准 HLS 加密:\n * - 密钥来自 EXT-X-KEY URI\n * - IV 来自 EXT-X-KEY IV 属性,或使用媒体序列号作为 IV\n * 2. **SAMPLE-AES**: selective encryption (part of the sample is encrypted)\n * - Used primarily with fMP4\n * - Requires parsing of the container format\n * 选择性加密(样本的一部分被加密):\n * - 主要用于 fMP4\n * - 需要解析容器格式\n * 3. **Common Encryption (CENC)**: for fMP4 with DRM\n * - Uses 'cenc' or 'cbcs' scheme\n * - Requires EME (Encrypted Media Extensions) API\n * 用于带 DRM 的 fMP4:\n * - 使用 'cenc' 或 'cbcs' 方案\n * - 需要 EME (Encrypted Media Extensions) API\n *\n * @module decrypt\n * 解密模块\n */\n\nimport { Segment, KeyInfo, EncryptionMethod, KeyFormat } from \"../types\";\nimport { Logger } from \"@skax/logger\";\n\n/**\n * Result of a decryption operation.\n * 解密操作的结果。\n *\n * @interface DecryptionResult\n * 解密结果\n */\nexport interface DecryptionResult {\n /**\n * Decrypted data as an ArrayBuffer.\n * 解密后的数据,类型为 ArrayBuffer。\n */\n data: ArrayBuffer;\n\n /**\n * The encryption method that was applied to the original data.\n * 应用于原始数据的加密方法。\n */\n method: EncryptionMethod;\n}\n\n/**\n * Configuration options for the Decryptor.\n * 解密器的配置选项。\n *\n * @interface DecryptorConfig\n * 解密器配置\n */\nexport interface DecryptorConfig {\n /**\n * Whether to enable Web Crypto API based decryption (default: true).\n * Disable if the runtime lacks SubtleCrypto and you are using a JS\n * polyfill or external key provider instead.\n * 是否启用基于 Web Crypto API 的解密(默认:true)。\n * 如果运行时缺少 SubtleCrypto 且使用 JS polyfill 或外部密钥提供者替代,请禁用。\n */\n useWebCrypto?: boolean;\n\n /**\n * External key provider for DRM / Common Encryption scenarios.\n * When provided, this function is called instead of fetching the key\n * from the EXT-X-KEY URI.\n * 用于 DRM / Common Encryption 场景的外部密钥提供者。\n * 提供后,将调用此函数而不是从 EXT-X-KEY URI 获取密钥。\n *\n * @param keyInfo - The key metadata from the playlist.\n * 播放列表中的密钥元数据。\n * @param segment - The segment being decrypted.\n * 正在解密的媒体分片。\n * @returns A Promise resolving to the raw AES key bytes (16 bytes for AES-128).\n * 返回解析为原始 AES 密钥字节的 Promise(AES-128 为 16 字节)。\n */\n keyProvider?: (keyInfo: KeyInfo, segment: Segment) => Promise<ArrayBuffer>;\n}\n\n/**\n * Decryptor class — entry point for segment decryption.\n * 解密器类 — 媒体分片解密的入口点。\n *\n * Supports:\n * - **AES-128-CBC** via Web Crypto API\n * 通过 Web Crypto API 实现\n * - **Custom key provider** for DRM scenarios\n * 用于 DRM 场景的自定义密钥提供者\n * - **SAMPLE-AES** placeholder (requires container parsing)\n * SAMPLE-AES 占位(需要容器解析)\n *\n * @example\n * ```typescript\n * // Create a decryptor with default settings (Web Crypto API)\n * const decryptor = new Decryptor({ useWebCrypto: true });\n *\n * // Check if a segment needs decryption\n * if (decryptor.needsDecryption(segment)) {\n * const result = await decryptor.decrypt(segment, encryptedData);\n * // result.data is now a plaintext ArrayBuffer\n * appendBuffer(result.data);\n * }\n *\n * // Create a decryptor with a custom key provider (e.g. for DRM)\n * const drmDecryptor = new Decryptor({\n * keyProvider: async (keyInfo, seg) => {\n * const license = await fetchLicense(keyInfo.uri!);\n * return license.key;\n * }\n * });\n * ```\n */\nexport class Decryptor {\n /**\n * Logger instance.\n * 日志实例。\n */\n private _logger: Logger;\n\n /**\n * Active configuration (useWebCrypto, keyProvider).\n * 当前配置。\n */\n private _config: DecryptorConfig;\n\n /**\n * In-memory cache for fetched keys, keyed by URI.\n * 内存缓存,用于存储已获取的密钥,键为 URI。\n */\n private _keyCache: Map<string, ArrayBuffer> = new Map();\n\n /**\n * Map to track pending key fetches to prevent duplicate requests for the same key URI.\n * 用于跟踪正在进行的密钥获取的 Map,以防止对同一密钥 URI 发起重复请求。\n */\n private _pendingKeyFetches: Map<string, Promise<ArrayBuffer>> = new Map();\n\n /**\n * Maximum number of keys to cache in memory. When the cache exceeds this size, the oldest entry is evicted.\n * 内存中缓存的最大密钥数量。当缓存超过此大小时,最旧的条目将被淘汰。\n */\n private readonly _MAX_KEY_CACHE_SIZE = 50;\n\n /**\n * Construct a new Decryptor instance.\n * 构造一个新的 Decryptor 实例。\n *\n * @param config - Decryption configuration (useWebCrypto, keyProvider).\n * 解密配置(useWebCrypto、keyProvider)。\n *\n * @example\n * ```typescript\n * // Default: Web Crypto API enabled, no external key provider\n * const decryptor = new Decryptor();\n *\n * // With custom key provider\n * const decryptor = new Decryptor({\n * keyProvider: async (keyInfo, segment) => {\n * // custom key retrieval logic\n * // 自定义密钥获取逻辑\n * const resp = await fetch(\"/drn/license?kid=\" + keyInfo.uri);\n * return resp.arrayBuffer();\n * }\n * });\n * ```\n */\n constructor(config: DecryptorConfig = {}) {\n this._logger = new Logger(\"HLS-IO:Decryptor\", Logger.LEVEL.WARN);\n this._config = { useWebCrypto: true, ...config };\n }\n\n /**\n * Check whether a segment requires decryption.\n * 检查媒体分片是否需要解密。\n *\n * A segment needs decryption when it has a key whose method is defined\n * and is not `NONE`.\n * 当分片具有已定义且方法不是 `NONE` 的密钥时,需要解密。\n *\n * @param segment - The segment to inspect.\n * 要检查的媒体分片。\n * @returns `true` if the segment is encrypted, `false` otherwise.\n * 如果分片已加密则返回 `true`,否则返回 `false`。\n *\n * @example\n * ```typescript\n * for (const segment of playlist.segments) {\n * if (decryptor.needsDecryption(segment)) {\n * console.log(\"Encrypted:\", segment.uri);\n * }\n * }\n * ```\n */\n needsDecryption(segment: Segment): boolean {\n // RFC 8216 §6.3.6: MUST ignore EXT-X-KEY with unsupported KEYFORMAT.\n // RFC 8216 §6.3.6: 必须忽略 KEYFORMAT 不支持的 EXT-X-KEY。\n if (segment.key?.keyFormat && segment.key.keyFormat !== KeyFormat.IDENTITY) {\n return false;\n }\n return !!(segment.key && segment.key.method !== EncryptionMethod.NONE && segment.key.method !== undefined);\n }\n\n /**\n * Decrypt segment data using the appropriate method.\n * 使用合适的方法解密媒体分片数据。\n *\n * Routes to the correct decryptor based on `segment.key.method`:\n * - `NONE` → pass-through (no decryption)\n * - `AES-128` → AES-128-CBC decryption via Web Crypto API\n * - `SAMPLE-AES` → placeholder (raw data returned as-is)\n * 根据 `segment.key.method` 路由到正确的解密器。\n *\n * @param segment - The segment metadata including key info.\n * 包含密钥信息的媒体分片元数据。\n * @param data - The encrypted binary data (optional if segment.data is set).\n * 加密的二进制数据(如果 segment.data 已设置则可选)。\n * @returns A `DecryptionResult` containing the plaintext data and method used.\n * 包含明文数据和所用方法的 `DecryptionResult`。\n *\n * @example\n * ```typescript\n * const encryptedData = await fetcher.fetchSegment(segment.uri);\n * const { data: plaintext } = await decryptor.decrypt(segment, encryptedData.data);\n * sourceBuffer.appendBuffer(plaintext);\n * ```\n */\n async decrypt(segment: Segment, data: ArrayBuffer): Promise<DecryptionResult> {\n if (!segment.data && !data) {\n throw new Error(\"No data to decrypt\");\n }\n\n const rawData = data || segment.data!;\n const keyInfo = segment.key;\n\n if (!keyInfo || keyInfo.method === EncryptionMethod.NONE) {\n return { data: rawData, method: EncryptionMethod.NONE };\n }\n\n this._logger.debug(`Decrypting segment ${segment.uri} with method ${keyInfo.method}`);\n\n switch (keyInfo.method) {\n case EncryptionMethod.AES_128:\n return this._decryptAES128(segment, rawData, keyInfo);\n\n case EncryptionMethod.SAMPLE_AES:\n return this._decryptSampleAES(segment, rawData, keyInfo);\n\n default:\n this._logger.warn(`Unknown encryption method, returning raw data`);\n return { data: rawData, method: EncryptionMethod.NONE };\n }\n }\n\n /**\n * Prefetch the decryption key for a segment.\n * 预取片段的解密密钥。\n *\n * @param segment - The segment metadata.\n */\n async prefetchKey(segment: Segment): Promise<void> {\n const keyInfo = segment.key;\n if (!keyInfo || keyInfo.method === EncryptionMethod.NONE || keyInfo.method !== EncryptionMethod.AES_128) {\n return;\n }\n\n if (this._config.keyProvider || !keyInfo.uri) {\n return; // External provider or missing URI\n }\n\n if (this._keyCache.has(keyInfo.uri)) {\n return; // Already cached\n }\n\n if (this._pendingKeyFetches.has(keyInfo.uri)) {\n await this._pendingKeyFetches.get(keyInfo.uri);\n return;\n }\n\n this._logger.debug(`Prefetching decryption key from ${keyInfo.uri}`);\n const fetchPromise = fetch(keyInfo.uri)\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.arrayBuffer();\n })\n .then((key) => {\n if (this._keyCache.size >= this._MAX_KEY_CACHE_SIZE) {\n const oldestKey = this._keyCache.keys().next().value;\n if (oldestKey) this._keyCache.delete(oldestKey);\n }\n this._keyCache.set(keyInfo.uri!, key);\n this._pendingKeyFetches.delete(keyInfo.uri!);\n return key;\n })\n .catch((err) => {\n this._pendingKeyFetches.delete(keyInfo.uri!);\n throw new Error(`Prefetching key failed for ${keyInfo.uri}: ${(err as Error).message}`);\n });\n\n this._pendingKeyFetches.set(keyInfo.uri, fetchPromise);\n await fetchPromise;\n }\n\n /**\n * AES-128-CBC decryption.\n * AES-128-CBC 解密。\n *\n * AES-128-CBC Decryption Flow — 解密流程:\n *\n * **Step 1 — Obtain the 16-byte key (128 bits):**\n * - If a `keyProvider` is configured, call it with `(keyInfo, segment)`.\n * - Otherwise, fetch the key from `keyInfo.uri` (the EXT-X-KEY URI).\n * - Validate that the key is exactly 16 bytes.\n * **第 1 步 — 获取 16 字节密钥(128 位):**\n * - 如果配置了 `keyProvider`,用 `(keyInfo, segment)` 调用它。\n * - 否则,从 `keyInfo.uri`(EXT-X-KEY URI)获取密钥。\n * - 验证密钥长度恰好为 16 字节。\n *\n * **Step 2 — Determine the 16-byte Initialization Vector (IV):**\n * - If `keyInfo.iv` is present and is exactly 16 bytes, use it directly\n * (the IV from the EXT-X-KEY `IV` attribute).\n * - Otherwise, derive the IV from the media sequence number:\n * populate a 16-byte buffer with the segment's `sequence` as a\n * big-endian 128-bit integer (padded into the low 8 bytes).\n * **第 2 步 — 确定 16 字节初始化向量(IV):**\n * - 如果 `keyInfo.iv` 存在且恰好 16 字节,直接使用\n * (来自 EXT-X-KEY `IV` 属性的 IV)。\n * - 否则,从媒体序列号派生 IV:\n * 将分片的 `sequence` 以大端序 128 位整数填充到 16 字节缓冲区中\n * (填充到低 8 字节)。\n *\n * **Step 3 — Decrypt via Web Crypto API:**\n * - Import the raw key with `crypto.subtle.importKey(\"raw\", key, \"AES-CBC\")`.\n * - Call `crypto.subtle.decrypt({ name: \"AES-CBC\", iv }, cryptoKey, data)`.\n * - Return the decrypted ArrayBuffer.\n * **第 3 步 — 通过 Web Crypto API 解密:**\n * - 使用 `crypto.subtle.importKey(\"raw\", key, \"AES-CBC\")` 导入原始密钥。\n * - 调用 `crypto.subtle.decrypt({ name: \"AES-CBC\", iv }, cryptoKey, data)`。\n * - 返回解密后的 ArrayBuffer。\n *\n * **Fallback:** If `useWebCrypto` is false or `crypto.subtle` is\n * unavailable, an error is thrown. The caller should use a JS AES\n * library polyfill or a `keyProvider` that handles decryption itself.\n * **回退:** 如果 `useWebCrypto` 为 false 或 `crypto.subtle` 不可用,\n * 将抛出错误。调用方应使用 JS AES 库 polyfill,或使用自行处理解密的\n * `keyProvider`。\n *\n * @param segment - The segment being decrypted (for sequence-derived IV).\n * 正在解密的媒体分片(用于从序列号派生 IV)。\n * @param data - The ciphertext to decrypt.\n * 待解密的密文。\n * @param keyInfo - Key metadata from the playlist.\n * 播放列表中的密钥元数据。\n * @returns A `DecryptionResult` with the plaintext data.\n * 包含明文数据的 `DecryptionResult`。\n */\n private async _decryptAES128(segment: Segment, data: ArrayBuffer, keyInfo: KeyInfo): Promise<DecryptionResult> {\n try {\n let key: ArrayBuffer;\n\n // Step 1: Obtain the 16-byte AES key.\n // 第 1 步:获取 16 字节 AES 密钥。\n if (this._config.keyProvider) {\n // Use the external key provider (e.g. DRM license server).\n // 使用外部密钥提供者(如 DRM 许可证服务器)。\n key = await this._config.keyProvider(keyInfo, segment);\n } else if (keyInfo.uri) {\n // Fetch the key from the cache or from the network.\n // 根据缓存或 EXT-X-KEY URI 获取密钥。\n if (this._keyCache.has(keyInfo.uri)) {\n key = this._keyCache.get(keyInfo.uri)!;\n } else if (this._pendingKeyFetches.has(keyInfo.uri)) {\n key = await this._pendingKeyFetches.get(keyInfo.uri)!;\n } else {\n this._logger.debug(`Fetching decryption key from ${keyInfo.uri}`);\n const fetchPromise = fetch(keyInfo.uri)\n .then((res) => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.arrayBuffer();\n })\n .then((k) => {\n if (this._keyCache.size >= this._MAX_KEY_CACHE_SIZE) {\n const oldestKey = this._keyCache.keys().next().value;\n if (oldestKey) this._keyCache.delete(oldestKey);\n }\n this._keyCache.set(keyInfo.uri!, k);\n this._pendingKeyFetches.delete(keyInfo.uri!);\n return k;\n })\n .catch((err) => {\n this._pendingKeyFetches.delete(keyInfo.uri!);\n throw err;\n });\n this._pendingKeyFetches.set(keyInfo.uri, fetchPromise);\n key = await fetchPromise;\n }\n } else {\n throw new Error(\"No key URI or key provider available for AES-128 decryption\");\n }\n\n // Validate key length: AES-128 requires exactly 16 bytes (128 bits).\n // 验证密钥长度:AES-128 要求恰好 16 字节(128 位)。\n if (key.byteLength !== 16) {\n throw new Error(`Invalid AES-128 key length: ${key.byteLength} bytes (expected 16)`);\n }\n\n // Step 2: Determine the 16-byte IV.\n // 第 2 步:确定 16 字节 IV。\n // In HLS, the IV can come from two sources:\n // 在 HLS 中,IV 可来自两种来源:\n let iv: Uint8Array;\n if (keyInfo.iv && keyInfo.iv.length === 16) {\n // a) Explicit IV from the EXT-X-KEY IV attribute (hex-encoded).\n // 来自 EXT-X-KEY IV 属性的显式 IV(十六进制编码)。\n iv = keyInfo.iv;\n } else {\n // b) Implicit IV derived from the media sequence number.\n // The sequence is placed in the low 8 bytes of a 16-byte buffer\n // in big-endian order, and the high 8 bytes are zeroed.\n // 从媒体序列号派生的隐式 IV。\n // 序列号以大端序放入 16 字节缓冲区的低 8 字节,高 8 字节为零。\n iv = new Uint8Array(16);\n const seq = segment.sequence;\n for (let i = 0; i < 8; i++) {\n iv[15 - i] = (seq >> (i * 8)) & 0xff;\n }\n }\n\n this._logger.debug(\n `Using IV: [${Array.from(iv)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\" \")}]`,\n );\n\n // Step 3: Decrypt via Web Crypto API.\n // 第 3 步:通过 Web Crypto API 解密。\n // These browsers have unprefixed and conforming webcrypto api implementations:\n // Chrome 43+, Chrome for Android 44+,\n // Opera 24+,\n // Firefox 34+,\n // Edge 12+.\n // Safari 11+.\n if (this._config.useWebCrypto && typeof crypto !== \"undefined\" && crypto.subtle) {\n // Import the raw key as a CryptoKey object for AES-CBC.\n // 将原始密钥导入为 AES-CBC 的 CryptoKey 对象。\n const cryptoKey = await crypto.subtle.importKey(\"raw\", key, { name: \"AES-CBC\" }, false, [\"decrypt\"]);\n\n // Perform the AES-CBC decryption. The IV is cast to BufferSource\n // (Uint8Array satisfies this interface).\n // 执行 AES-CBC 解密。IV 被转换为 BufferSource\n // (Uint8Array 满足此接口)。\n const decrypted = await crypto.subtle.decrypt({ name: \"AES-CBC\", iv: iv as BufferSource }, cryptoKey, data);\n\n this._logger.debug(\"AES-128-CBC decryption successful\");\n return { data: decrypted, method: EncryptionMethod.AES_128 };\n }\n\n // Fallback: Web Crypto API not available.\n // In a browser without SubtleCrypto, the caller should use a JS AES library.\n // 回退:Web Crypto API 不可用。\n // 在没有 SubtleCrypto 的浏览器中,调用方应使用 JS AES 库。\n throw new Error(\"Web Crypto API not available. Use a polyfill or provide a keyProvider.\");\n } catch (error) {\n this._logger.error(`AES-128 decryption failed: ${(error as Error).message}`);\n throw error;\n }\n }\n\n /**\n * SAMPLE-AES decryption (placeholder).\n * SAMPLE-AES 解密(占位实现)。\n *\n * **Why this is a placeholder:**\n * SAMPLE-AES selectively encrypts only portions of each media sample.\n * Decrypting it requires:\n * 1. Parsing the container format (MPEG-TS or fMP4) to locate sample\n * boundaries.\n * 2. Identifying which bytes within each sample are encrypted\n * (governed by the encoding — typically 10% of audio data, or\n * specific NAL units in video).\n * 3. Decrypting only those bytes with AES-128-CBC.\n * **为什么这是占位实现:**\n * SAMPLE-AES 仅选择性地加密每个媒体样本的部分内容。解密它需要:\n * 1. 解析容器格式(MPEG-TS 或 fMP4)以定位样本边界。\n * 2. 识别每个样本中哪些字节被加密(由编码决定 — 通常是 10% 的音频数据,\n * 或视频中的特定 NAL 单元)。\n * 3. 仅对这些字节执行 AES-128-CBC 解密。\n *\n * Currently this method returns the data *as-is*, which is sufficient\n * in many browsers that handle SAMPLE-AES at the MediaSource level.\n * 当前此方法按原样返回数据,这在许多在 MediaSource 层面处理 SAMPLE-AES\n * 的浏览器中已经足够。\n *\n * @param _segment - The segment (unused, reserved for future use).\n * 媒体分片(未使用,预留给未来扩展)。\n * @param data - The encrypted segment data.\n * 加密的分片数据。\n * @param keyInfo - Key metadata from the playlist.\n * 播放列表中的密钥元数据。\n * @returns The raw data wrapped in a `DecryptionResult`.\n * 包装在 `DecryptionResult` 中的原始数据。\n */\n private async _decryptSampleAES(_segment: Segment, data: ArrayBuffer, keyInfo: KeyInfo): Promise<DecryptionResult> {\n this._logger.warn(\"SAMPLE-AES decryption is not fully implemented. \" + \"It requires container-level parsing of TS or fMP4. \" + \"Returning raw data as-is.\");\n // TODO: Implement sample-AES decryption\n // 1. Parse the container format (TS or fMP4)\n // 解析容器格式(TS 或 fMP4)\n // 2. Identify encrypted samples\n // 识别加密的样本\n // 3. Decrypt only the encrypted portions\n // 仅解密加密部分\n return { data, method: keyInfo.method };\n }\n\n /**\n * Enable or disable debug-level logging.\n * 启用或禁用 DEBUG 级别的日志记录。\n *\n * @param debug - When `true`, verbose decryption details are logged.\n * 为 `true` 时,记录详细的解密信息。\n */\n setDebug(debug: boolean): void {\n this._logger.setLevel(debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n }\n\n /**\n * Update the decryptor configuration at runtime.\n * 在运行时更新解密器配置。\n *\n * Useful for toggling `useWebCrypto` or swapping the `keyProvider`\n * without recreating the Decryptor instance.\n * 适用于切换 `useWebCrypto` 或更换 `keyProvider`,无需重建 Decryptor 实例。\n *\n * @param config - Partial configuration to merge.\n * 要合并的部分配置。\n */\n updateConfig(config: Partial<DecryptorConfig>): void {\n this._config = { ...this._config, ...config };\n }\n}\n\n/**\n * Create a default Decryptor with an optional key provider.\n * 创建一个带有可选密钥提供者的默认 Decryptor。\n *\n * This is a convenience factory that creates a Decryptor with Web Crypto\n * enabled and the given key provider (if any).\n * 这是一个便捷工厂函数,创建一个启用 Web Crypto 并附带给定密钥提供者\n * (如有)的 Decryptor。\n *\n * @param keyProvider - Optional external key provider for DRM scenarios.\n * 可选的外部密钥提供者,用于 DRM 场景。\n * @returns A new Decryptor instance.\n * 一个新的 Decryptor 实例。\n *\n * @example\n * ```typescript\n * // Simple decryptor (fetches keys from EXT-X-KEY URI automatically)\n * const decryptor = createDecryptor();\n *\n * // Decryptor with a custom DRM license provider\n * const drmDecryptor = createDecryptor(async (keyInfo, segment) => {\n * const licenseUrl = `https://drm.example.com/license?kid=${keyInfo.uri}`;\n * const resp = await fetch(licenseUrl, { headers: { \"Authorization\": \"Bearer ...\" } });\n * const json = await resp.json();\n * return new Uint8Array(json.key).buffer;\n * });\n * ```\n */\nexport function createDecryptor(keyProvider?: (keyInfo: KeyInfo, segment: Segment) => Promise<ArrayBuffer>): Decryptor {\n return new Decryptor({ keyProvider });\n}\n","/**\n * HLS Tag constants, attribute parsers, and helpers.\n * Provides comprehensive parsing for all standard and LL-HLS tags.\n *\n * HLS 标签常量、属性解析器和辅助函数。\n * 为所有标准标签和 LL-HLS 标签提供全面的解析支持。\n */\n\nimport { EncryptionMethod, KeyFormat } from \"../types\";\n\n// Tag Matching\n// 标签匹配\n// ============\n\n/**\n * Check if a line is a comment (starts with `#` but is not an HLS tag).\n *\n * 检查某行是否为注释(以 `#` 开头但不是 HLS 标签)。\n *\n * @param line - The raw line from the playlist text\n * @param line - 播放列表文本中的原始行\n * @returns `true` if the line is a non-tag comment; `false` otherwise\n * @returns 如果是非标签注释返回 `true`,否则返回 `false`\n *\n * @example\n * ```ts\n * isComment(\"# This is a comment\"); // => true\n * isComment(\"#EXTINF:10.0,Title\"); // => false (it's a tag)\n * isComment(\"https://example.com/ts\"); // => false (it's a URI)\n * ```\n */\nexport function isComment(line: string): boolean {\n return line.startsWith(\"#\") && !line.startsWith(\"#EXT\");\n}\n\n/**\n * Check if a line is a URI (not a comment and not a tag).\n *\n * 检查某行是否为 URI(不是注释也不是标签)。\n *\n * @param line - The raw line from the playlist text\n * @param line - 播放列表文本中的原始行\n * @returns `true` if the line is a non-empty, non-comment, non-tag URI\n * @returns 如果该行是非空、非注释、非标签的 URI 则返回 `true`\n *\n * @example\n * ```ts\n * isUri(\"https://example.com/segment.ts\"); // => true\n * isUri(\"#EXTINF:10.0,Title\"); // => false (it's a tag)\n * isUri(\"\"); // => false (empty)\n * ```\n */\nexport function isUri(line: string): boolean {\n return line.trim().length > 0 && !line.startsWith(\"#\");\n}\n\n/**\n * Extract the tag name from a tag line (everything before the first colon).\n *\n * 从标签行中提取标签名称(第一个冒号之前的所有内容)。\n *\n * @param line - A full tag line, e.g. `\"#EXTINF:10.0,Title\"`\n * @param line - 完整的标签行,例如 `\"#EXTINF:10.0,Title\"`\n * @returns The tag name without its colon-suffix attributes\n * @returns 不含冒号及后续属性的标签名称\n *\n * @example\n * ```ts\n * getTagName(\"#EXTINF:10.0,Title\"); // => \"#EXTINF\"\n * getTagName(\"#EXT-X-VERSION:3\"); // => \"#EXT-X-VERSION\"\n * getTagName(\"#EXTM3U\"); // => \"#EXTM3U\" (no colon)\n * ```\n */\nexport function getTagName(line: string): string {\n const colon = line.indexOf(\":\");\n if (colon === -1) return line;\n return line.substring(0, colon);\n}\n\n/**\n * Extract the attribute string from a tag line (everything after the first colon).\n *\n * 从标签行中提取属性字符串(第一个冒号之后的所有内容)。\n *\n * @param line - A full tag line, e.g. `\"#EXT-X-KEY:METHOD=AES-128,URI=\\\"key.bin\\\"\"`\n * @param line - 完整的标签行\n * @returns The attribute portion of the tag, or an empty string if there is no colon\n * @returns 标签的属性部分,如果没有冒号则返回空字符串\n *\n * @example\n * ```ts\n * getTagAttributes(\"#EXTINF:10.0,Title\"); // => \"10.0,Title\"\n * getTagAttributes(\"#EXT-X-KEY:METHOD=AES-128\"); // => \"METHOD=AES-128\"\n * getTagAttributes(\"#EXTM3U\"); // => \"\"\n * ```\n */\nexport function getTagAttributes(line: string): string {\n const colon = line.indexOf(\":\");\n if (colon === -1) return \"\";\n return line.substring(colon + 1);\n}\n\n// Attribute Parsing\n// 属性解析\n// ===================\n\n/**\n * Parse an HLS attribute list string into a key-value dictionary.\n *\n * This function implements a **single-pass state machine** to tokenize the\n * comma-separated `AttributeName=AttributeValue` pairs defined by the HLS spec\n * (RFC 8216 §4.2). It correctly handles:\n * - **Quoted values** — double-quoted strings that may contain commas/spaces\n * - **Unquoted values** — plain tokens terminated by comma or space\n * - **Leading/trailing whitespace** around `=` and between pairs\n * - **Hex sequences** and **resolution values** (stored as strings; callers\n * should convert with {@link parseHexBytes} / {@link parseResolution})\n *\n * 将 HLS 属性列表字符串解析为键值字典。\n *\n * 此函数实现了一个**单遍扫描状态机**,用于将 HLS 规范(RFC 8216 §4.2)中定义的逗号分隔的\n * `AttributeName=AttributeValue` 对进行分词。它能正确处理:\n * - **带引号的值** — 可能包含逗号或空格的双引号字符串\n * - **不带引号的值** — 由逗号或空格终止的普通标记\n * - **等号周围及键值对之间的空白字符**\n * - **十六进制序列**和**分辨率值**(以字符串形式存储;调用者应使用\n * {@link parseHexBytes} / {@link parseResolution} 进行转换)\n *\n * @param attrStr - The raw attribute list string from a tag\n * @param attrStr - 标签中的原始属性列表字符串\n * @returns A dictionary mapping lowercased attribute names to their string values\n * @returns 属性名(小写)到字符串值的映射字典\n *\n * @example\n * ```ts\n * // Basic usage\n * // 基本用法\n * parseAttributes('METHOD=AES-128,URI=\"https://key.bin\",IV=0x1234');\n * // => { METHOD: \"AES-128\", URI: \"https://key.bin\", IV: \"0x1234\" }\n *\n * // Empty / edge cases\n * // 空值和边界情况\n * parseAttributes(\"\"); // => {}\n * parseAttributes(\"FOO\"); // => {} (no '=', treated as malformed)\n *\n * // Quoted value containing commas\n * // 包含逗号的带引号值\n * parseAttributes('CODECS=\"avc1.4d401e,mp4a.40.2\"');\n * // => { CODECS: \"avc1.4d401e,mp4a.40.2\" }\n * ```\n */\nexport function parseAttributes(attrStr: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n if (!attrStr) return attrs;\n\n let i = 0;\n const len = attrStr.length;\n\n // Single-pass state machine:\n // 单遍扫描状态机:\n // [skip ws/comma] → [read name] → [skip ws] → [expect '='] → [skip ws] → [read value] → loop\n while (i < len) {\n // --- State: Skip whitespace and commas between pairs ---\n // --- 状态:跳过键值对之间的空白和逗号 ---\n while (i < len && (attrStr[i] === \" \" || attrStr[i] === \"\\t\" || attrStr[i] === \",\")) {\n i++;\n }\n if (i >= len) break;\n\n // --- State: Read the attribute name ---\n // --- 状态:读取属性名 ---\n // An attribute name runs until '=', space, comma, or end-of-string.\n // 属性名持续到遇到 '='、空格、逗号或字符串末尾为止。\n const nameStart = i;\n while (i < len && attrStr[i] !== \"=\" && attrStr[i] !== \" \" && attrStr[i] !== \",\") {\n i++;\n }\n const name = attrStr.substring(nameStart, i);\n\n // --- State: Skip whitespace before '=' ---\n // --- 状态:跳过等号前的空白 ---\n while (i < len && attrStr[i] === \" \") i++;\n if (i >= len || attrStr[i] !== \"=\") {\n // Malformed entry — no '=' found for this name. Skip and continue.\n // 格式错误 — 该名称后没有 '='。跳过并继续。\n continue;\n }\n // consume '='\n // 消耗 '='\n i++;\n\n // --- State: Skip whitespace after '=' ---\n // --- 状态:跳过等号后的空白 ---\n while (i < len && attrStr[i] === \" \") i++;\n\n // --- State: Read the value (quoted or unquoted) ---\n // --- 状态:读取值(带引号或不带引号) ---\n let value: string;\n if (i < len && attrStr[i] === '\"') {\n // Quoted string: consume opening quote, read until closing quote.\n // 带引号字符串:消耗左引号,读取直到右引号。\n // skip opening quote\n // 跳过左引号\n i++;\n const valStart = i;\n while (i < len && attrStr[i] !== '\"') {\n i++;\n }\n value = attrStr.substring(valStart, i);\n // skip closing quote\n // 跳过右引号\n i++;\n } else {\n // Unquoted value: read until next comma or space.\n // 不带引号的值:读取直到下一个逗号或空格。\n const valStart = i;\n while (i < len && attrStr[i] !== \",\" && attrStr[i] !== \" \") {\n i++;\n }\n value = attrStr.substring(valStart, i);\n }\n\n // Store the parsed pair.\n // 存储解析后的键值对。\n attrs[name] = value;\n }\n\n return attrs;\n}\n\n/**\n * Parse a resolution string like `\"1920x1080\"` into numeric width and height.\n *\n * 将类似 `\"1920x1080\"` 的分辨率字符串解析为数字宽度和高度。\n *\n * @param res - A resolution string in the form `\"<width>x<height>\"`\n * @param res - 形如 `\"<宽>x<高>\"` 的分辨率字符串\n * @returns An object with `width` and `height` properties, or `null` if parsing fails\n * @returns 包含 `width` 和 `height` 属性的对象,解析失败则返回 `null`\n *\n * @example\n * ```ts\n * parseResolution(\"1920x1080\"); // => { width: 1920, height: 1080 }\n * parseResolution(\"1280x720\"); // => { width: 1280, height: 720 }\n * parseResolution(\"invalid\"); // => null\n * parseResolution(\"\"); // => null\n * ```\n */\nexport function parseResolution(res: string): { width: number; height: number } | null {\n if (!res) return null;\n const parts = res.split(\"x\");\n if (parts.length !== 2) return null;\n const w = parseInt(parts[0], 10);\n const h = parseInt(parts[1], 10);\n if (isNaN(w) || isNaN(h)) return null;\n return { width: w, height: h };\n}\n\n/**\n * Parse a hex string like `\"0x1234abcd\"` or `\"0XABCDEF\"` into a `Uint8Array`.\n *\n * Each pair of hex digits after the `0x` / `0X` prefix becomes one byte.\n * The input length (excluding prefix) must be **even** and contain only valid\n * hex characters (`[0-9a-fA-F]`).\n *\n * 将类似 `\"0x1234abcd\"` 或 `\"0XABCDEF\"` 的十六进制字符串解析为 `Uint8Array`。\n *\n * `0x` / `0X` 前缀后的每对十六进制数字对应一个字节。\n * 输入长度(不含前缀)必须为**偶数**且仅包含有效的十六进制字符(`[0-9a-fA-F]`)。\n *\n * @param hex - A hex string, e.g. `\"0xaabbccdd\"`\n * @param hex - 十六进制字符串,例如 `\"0xaabbccdd\"`\n * @returns A `Uint8Array` of the decoded bytes, or `null` on parse failure\n * @returns 解码后的字节数组,解析失败则返回 `null`\n *\n * @example\n * ```ts\n * parseHexBytes(\"0xABCD\"); // => Uint8Array [ 171, 205 ]\n * parseHexBytes(\"0X00FF\"); // => Uint8Array [ 0, 255 ]\n * // odd hex length\n * // 奇数长度\n * parseHexBytes(\"0x123\"); // => null\n * // invalid hex chars\n * // 无效十六进制字符\n * parseHexBytes(\"0xGG\"); // => null\n * // no prefix\n * // 无前缀\n * parseHexBytes(\"\"); // => null\n * ```\n */\nexport function parseHexBytes(hex: string): Uint8Array | null {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) return null;\n const hexStr = hex.substring(2);\n if (hexStr.length % 2 !== 0) return null;\n if (!/^[0-9a-fA-F]+$/.test(hexStr)) return null;\n const len = hexStr.length / 2;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = parseInt(hexStr.substring(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Parse an `#EXTINF` line into duration and optional title.\n *\n * The format is `#EXTINF:<duration>,<title>` where the title is optional.\n * If no comma is present the entire attribute string is treated as the duration.\n *\n * 将 `#EXTINF` 行解析为时长和可选的标题。\n *\n * 格式为 `#EXTINF:<时长>,<标题>`,其中标题是可选的。\n * 如果没有逗号,整个属性字符串将被视为时长。\n *\n * @param line - A complete `#EXTINF` tag line, e.g. `\"#EXTINF:10.0,Title\"`\n * @param line - 完整的 `#EXTINF` 标签行\n * @returns An object with `duration` (in seconds) and optional `title`, or `null` if the duration is not a valid number\n * @returns 包含 `duration`(秒)和可选 `title` 的对象,如果时长不是有效数字则返回 `null`\n *\n * @example\n * ```ts\n * parseExtInf(\"#EXTINF:10.0,My Title\"); // => { duration: 10.0, title: \"My Title\" }\n * parseExtInf(\"#EXTINF:5.5,\"); // => { duration: 5.5, title: undefined }\n * parseExtInf(\"#EXTINF:3.0\"); // => { duration: 3.0 }\n * parseExtInf(\"#EXTINF:abc\"); // => null (NaN duration)\n * ```\n */\nexport function parseExtInf(line: string): { duration: number; title?: string } | null {\n const attrs = getTagAttributes(line);\n const comma = attrs.indexOf(\",\");\n if (comma === -1) {\n // No title portion — the entire value is the duration.\n // 无标题部分 — 整个值即为时长。\n const duration = parseFloat(attrs);\n return isNaN(duration) ? null : { duration };\n }\n // Split on the first comma: left = duration, right = title.\n // 在第一个逗号处分割:左侧 = 时长,右侧 = 标题。\n const duration = parseFloat(attrs.substring(0, comma));\n const title = attrs.substring(comma + 1).trim();\n return isNaN(duration) ? null : { duration, title: title || undefined };\n}\n\n/**\n * Parse an `#EXT-X-BYTERANGE` tag value into a byte-range descriptor.\n *\n * The format is `<length>[@<offset>]`. If `@offset` is omitted the offset\n * is `undefined` (meaning the byte range is contiguous from the previous one).\n *\n * 将 `#EXT-X-BYTERANGE` 标签值解析为字节范围描述符。\n *\n * 格式为 `<长度>[@<偏移>]`。如果省略 `@偏移`,则 `offset` 为 `undefined`\n * (表示该字节范围紧接上一个字节范围)。\n *\n * @param line - A complete `#EXT-X-BYTERANGE` tag line, e.g. `\"#EXT-X-BYTERANGE:1024@0\"`\n * @param line - 完整的 `#EXT-X-BYTERANGE` 标签行\n * @returns An object with `length` and optional `offset`, or `null` if `length` is not a valid number\n * @returns 包含 `length` 和可选 `offset` 的对象,如果 `length` 不是有效数字则返回 `null`\n *\n * @example\n * ```ts\n * parseByteRange(\"#EXT-X-BYTERANGE:1024@0\"); // => { length: 1024, offset: 0 }\n * // no offset\n * // 无偏移\n * parseByteRange(\"#EXT-X-BYTERANGE:2048\"); // => { length: 2048 }\n * parseByteRange(\"#EXT-X-BYTERANGE:abc\"); // => null\n * ```\n */\nexport function parseByteRange(line: string): { length: number; offset?: number } | null {\n const attrs = getTagAttributes(line);\n const at = attrs.indexOf(\"@\");\n if (at === -1) {\n // No offset portion — only length.\n // 无偏移部分 — 仅有长度。\n const length = parseInt(attrs, 10);\n return isNaN(length) ? null : { length };\n }\n // Split on '@': left = length, right = offset.\n // 在 '@' 处分割:左侧 = 长度,右侧 = 偏移。\n const length = parseInt(attrs.substring(0, at), 10);\n const offset = parseInt(attrs.substring(at + 1), 10);\n if (isNaN(length)) return null;\n return { length, offset: isNaN(offset) ? undefined : offset };\n}\n\n/**\n * Convert an encryption method string to its {@link EncryptionMethod} enum value.\n *\n * 将加密方法字符串转换为 {@link EncryptionMethod} 枚举值。\n *\n * Recognised methods:\n * - `\"AES-128\"` → `EncryptionMethod.AES_128`\n * - `\"SAMPLE-AES\"` / `\"SAMPLE-AES-CTR\"` → `EncryptionMethod.SAMPLE_AES`\n * - Anything else → `EncryptionMethod.NONE`\n *\n * @param method - The raw `METHOD` attribute value\n * @param method - 原始的 `METHOD` 属性值\n * @returns The corresponding enum value\n * @returns 对应的枚举值\n *\n * @example\n * ```ts\n * parseEncryptionMethod(\"AES-128\"); // => EncryptionMethod.AES_128\n * parseEncryptionMethod(\"SAMPLE-AES\"); // => EncryptionMethod.SAMPLE_AES\n * parseEncryptionMethod(\"NONE\"); // => EncryptionMethod.NONE\n * // fallback\n * // 回退\n * parseEncryptionMethod(\"UNKNOWN\"); // => EncryptionMethod.NONE\n * ```\n */\nexport function parseEncryptionMethod(method: string): EncryptionMethod {\n switch (method.toUpperCase()) {\n case \"AES-128\":\n return EncryptionMethod.AES_128;\n case \"SAMPLE-AES\":\n return EncryptionMethod.SAMPLE_AES;\n case \"SAMPLE-AES-CTR\":\n return EncryptionMethod.SAMPLE_AES;\n default:\n return EncryptionMethod.NONE;\n }\n}\n\n/**\n * Convert a key format string to its {@link KeyFormat} enum value.\n *\n * 将密钥格式字符串转换为 {@link KeyFormat} 枚举值。\n *\n * @param format - The raw `KEYFORMAT` attribute value\n * @param format - 原始的 `KEYFORMAT` 属性值\n * @returns The corresponding enum value\n * @returns 对应的枚举值\n *\n * @example\n * ```ts\n * parseKeyFormat(\"com.apple.streamingkeydelivery\"); // => KeyFormat.COMMON_ENCRYPTION\n * // default\n * // 默认\n * parseKeyFormat(\"identity\"); // => KeyFormat.IDENTITY\n * ```\n */\nexport function parseKeyFormat(format: string): KeyFormat {\n switch (format) {\n case \"com.apple.streamingkeydelivery\":\n return KeyFormat.COMMON_ENCRYPTION;\n default:\n return KeyFormat.IDENTITY;\n }\n}\n\n/**\n * Parse `EXT-X-KEY` attributes into a key-info descriptor.\n *\n * Extracts the encryption `method`, optional `uri`, optional `iv` (decoded from\n * hex), optional `keyFormat`, and optional `keyFormatVersions`.\n *\n * 将 `EXT-X-KEY` 属性解析为密钥信息描述符。\n *\n * 提取加密方法 `method`、可选的 `uri`、可选的 `iv`(从十六进制解码)、\n * 可选的 `keyFormat` 和可选的 `keyFormatVersions`。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A key-info descriptor object\n * @returns 密钥信息描述符对象\n *\n * @example\n * ```ts\n * // Typical AES-128 key\n * // 典型的 AES-128 密钥\n * const attrs = parseAttributes('METHOD=AES-128,URI=\"https://key.bin\",IV=0xABCD');\n * parseKeyAttributes(attrs);\n * // => {\n * // method: EncryptionMethod.AES_128,\n * // uri: \"https://key.bin\",\n * // iv: Uint8Array [ 171, 205 ]\n * // }\n *\n * // Key with Common Encryption format\n * // 使用 Common Encryption 格式的密钥\n * const attrs2 = parseAttributes(\n * 'METHOD=SAMPLE-AES,URI=\"skd://key\",KEYFORMAT=\"com.apple.streamingkeydelivery\",KEYFORMATVERSIONS=\"1\"'\n * );\n * parseKeyAttributes(attrs2);\n * // => {\n * // method: EncryptionMethod.SAMPLE_AES,\n * // uri: \"skd://key\",\n * // keyFormat: KeyFormat.COMMON_ENCRYPTION,\n * // keyFormatVersions: \"1\"\n * // }\n * ```\n */\nexport function parseKeyAttributes(attrs: Record<string, string>): {\n method: EncryptionMethod;\n uri?: string;\n iv?: Uint8Array;\n keyFormat?: KeyFormat;\n keyFormatVersions?: string;\n} {\n return {\n method: parseEncryptionMethod(attrs[\"METHOD\"] || \"NONE\"),\n uri: attrs[\"URI\"],\n iv: attrs[\"IV\"] ? (parseHexBytes(attrs[\"IV\"]) ?? undefined) : undefined,\n keyFormat: attrs[\"KEYFORMAT\"] ? parseKeyFormat(attrs[\"KEYFORMAT\"]) : undefined,\n keyFormatVersions: attrs[\"KEYFORMATVERSIONS\"],\n };\n}\n\n/**\n * Parse `EXT-X-MAP` attributes into a map-info descriptor.\n *\n * The `URI` attribute is **required**; returns `null` if absent.\n * An optional `BYTERANGE` attribute can further specify the byte range of the\n * media initialisation segment.\n *\n * 将 `EXT-X-MAP` 属性解析为映射信息描述符。\n *\n * `URI` 属性是**必需的**;如果缺失则返回 `null`。\n * 可选的 `BYTERANGE` 属性可以进一步指定媒体初始化段的字节范围。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A map-info descriptor, or `null` if `URI` is missing\n * @returns 映射信息描述符,如果缺少 `URI` 则返回 `null`\n *\n * @example\n * ```ts\n * const attrs = parseAttributes('URI=\"init.mp4\",BYTERANGE=\"1024@0\"');\n * parseMapAttributes(attrs);\n * // => { uri: \"init.mp4\", byteRange: { length: 1024, offset: 0 } }\n * ```\n */\nexport function parseMapAttributes(attrs: Record<string, string>): {\n uri: string;\n byteRange?: { length: number; offset?: number };\n} | null {\n const uri = attrs[\"URI\"];\n if (!uri) return null;\n const byteRange = attrs[\"BYTERANGE\"] ? parseByteRangeText(attrs[\"BYTERANGE\"]) : undefined;\n return { uri, byteRange };\n}\n\n/**\n * Parse a `BYTERANGE` attribute value string (not a full tag line).\n *\n * This is a **private helper** used by tag-specific parsers that encounter\n * `BYTERANGE` as a nested attribute rather than a standalone tag.\n *\n * 解析 `BYTERANGE` 属性值字符串(非完整标签行)。\n *\n * 这是一个**私有辅助函数**,供那些将 `BYTERANGE` 作为嵌套属性而非独立标签\n * 处理的标签解析器使用。\n *\n * @param text - The raw `BYTERANGE` value, e.g. `\"1024@0\"`\n * @param text - 原始的 `BYTERANGE` 值,例如 `\"1024@0\"`\n * @returns A byte-range descriptor with `length` and optional `offset`\n * @returns 包含 `length` 和可选 `offset` 的字节范围描述符\n *\n * @internal\n */\nfunction parseByteRangeText(text: string): { length: number; offset?: number } {\n const at = text.indexOf(\"@\");\n if (at === -1) return { length: parseInt(text, 10) };\n return {\n length: parseInt(text.substring(0, at), 10),\n offset: parseInt(text.substring(at + 1), 10),\n };\n}\n\n/**\n * Parse `EXT-X-PART` attributes into a partial-segment descriptor (LL-HLS).\n *\n * Used for Low-Latency HLS where each segment may be composed of multiple\n * independently-addressable partial segments. The `DURATION` and `URI`\n * attributes are **required**; returns `null` if either is missing/invalid.\n *\n * 将 `EXT-X-PART` 属性解析为部分段描述符(LL-HLS)。\n *\n * 用于低延迟 HLS,其中每个分段可能由多个可独立寻址的部分段组成。\n * `DURATION` 和 `URI` 属性是**必需的**;如果任一缺失或无效则返回 `null`。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A partial-segment descriptor, or `null` on validation failure\n * @returns 部分段描述符,验证失败则返回 `null`\n *\n * @example\n * ```ts\n * const attrs = parseAttributes('DURATION=2.0,URI=\"part.ts\",INDEPENDENT=YES');\n * parsePartAttributes(attrs);\n * // => { duration: 2.0, uri: \"part.ts\", independent: true, gap: false }\n * ```\n */\nexport function parsePartAttributes(attrs: Record<string, string>): {\n duration: number;\n uri: string;\n byteRange?: { length: number; offset?: number };\n independent?: boolean;\n gap?: boolean;\n} | null {\n const duration = parseFloat(attrs[\"DURATION\"]);\n const uri = attrs[\"URI\"];\n if (isNaN(duration) || !uri) return null;\n return {\n duration,\n uri,\n byteRange: attrs[\"BYTERANGE\"] ? parseByteRangeText(attrs[\"BYTERANGE\"]) : undefined,\n independent: attrs[\"INDEPENDENT\"] === \"YES\",\n gap: attrs[\"GAP\"] === \"YES\",\n };\n}\n\n/**\n * Parse `EXT-X-SERVER-CONTROL` attributes (LL-HLS).\n *\n * Provides server-side directives for Low-Latency HLS playback, including\n * skip-boundary, blocking-reload, and hold-back parameters.\n *\n * 解析 `EXT-X-SERVER-CONTROL` 属性(LL-HLS)。\n *\n * 提供低延迟 HLS 播放的服务器端指令,包括跳过边界、阻塞重载和保持参数。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A server-control descriptor\n * @returns 服务器控制描述符\n *\n * @example\n * ```ts\n * const attrs = parseAttributes('CAN-SKIP-UNTIL=12.0,CAN-BLOCK-RELOAD=YES,HOLD-BACK=6.0');\n * parseServerControl(attrs);\n * // => { canSkipUntil: 12.0, canBlockReload: true, holdBack: 6.0 }\n * ```\n */\nexport function parseServerControl(attrs: Record<string, string>): {\n canSkipUntil?: number;\n canSkipDateTimes?: boolean;\n canBlockReload?: boolean;\n holdBack?: number;\n partHoldBack?: number;\n} {\n return {\n canSkipUntil: attrs[\"CAN-SKIP-UNTIL\"] ? parseFloat(attrs[\"CAN-SKIP-UNTIL\"]) : undefined,\n canSkipDateTimes: attrs[\"CAN-SKIP-DATERANGES\"] === \"YES\",\n canBlockReload: attrs[\"CAN-BLOCK-RELOAD\"] === \"YES\",\n holdBack: attrs[\"HOLD-BACK\"] ? parseFloat(attrs[\"HOLD-BACK\"]) : undefined,\n partHoldBack: attrs[\"PART-HOLD-BACK\"] ? parseFloat(attrs[\"PART-HOLD-BACK\"]) : undefined,\n };\n}\n\n/**\n * Parse `EXT-X-PRELOAD-HINT` attributes (LL-HLS).\n *\n * Informs the client about a resource (a partial segment or init segment)\n * that is currently being produced and will be available shortly.\n *\n * 解析 `EXT-X-PRELOAD-HINT` 属性(LL-HLS)。\n *\n * 向客户端告知某个资源(部分段或初始化段)正在生成中,即将可用。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A preload-hint descriptor, or `null` if `TYPE` or `URI` is missing\n * @returns 预加载提示描述符,如果缺少 `TYPE` 或 `URI` 则返回 `null`\n *\n * @example\n * ```ts\n * const attrs = parseAttributes('TYPE=PART,URI=\"part.ts\",BYTERANGE-START=0,BYTERANGE-LENGTH=512');\n * parsePreloadHint(attrs);\n * // => { type: \"PART\", uri: \"part.ts\", byteRangeStart: 0, byteRangeLength: 512 }\n * ```\n */\nexport function parsePreloadHint(attrs: Record<string, string>): {\n type: \"PART\" | \"MAP\";\n uri: string;\n byteRangeStart?: number;\n byteRangeLength?: number;\n} | null {\n const type = attrs[\"TYPE\"] as \"PART\" | \"MAP\";\n const uri = attrs[\"URI\"];\n if (!type || !uri) return null;\n return {\n type,\n uri,\n byteRangeStart: attrs[\"BYTERANGE-START\"] ? parseInt(attrs[\"BYTERANGE-START\"], 10) : undefined,\n byteRangeLength: attrs[\"BYTERANGE-LENGTH\"] ? parseInt(attrs[\"BYTERANGE-LENGTH\"], 10) : undefined,\n };\n}\n\n/**\n * Parse `EXT-X-RENDITION-REPORT` attributes (LL-HLS).\n *\n * Reports the last media sequence number and part index for a rendition\n * in a low-latency stream, allowing the client to synchronise across renditions.\n *\n * 解析 `EXT-X-RENDITION-REPORT` 属性(LL-HLS)。\n *\n * 报告低延迟流中某个变体的最后媒体序列号和部分索引,\n * 使客户端能够在不同变体之间进行同步。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A rendition-report descriptor, or `null` if `URI` is missing\n * @returns 变体报告描述符,如果缺少 `URI` 则返回 `null`\n *\n * @example\n * ```ts\n * const attrs = parseAttributes('URI=\"audio.m3u8\",LAST-MSN=42,LAST-PART=3');\n * parseRenditionReport(attrs);\n * // => { uri: \"audio.m3u8\", lastMSN: 42, lastPart: 3 }\n * ```\n */\nexport function parseRenditionReport(attrs: Record<string, string>): {\n uri: string;\n lastMSN?: number;\n lastPart?: number;\n} | null {\n const uri = attrs[\"URI\"];\n if (!uri) return null;\n return {\n uri,\n lastMSN: attrs[\"LAST-MSN\"] ? parseInt(attrs[\"LAST-MSN\"], 10) : undefined,\n lastPart: attrs[\"LAST-PART\"] ? parseInt(attrs[\"LAST-PART\"], 10) : undefined,\n };\n}\n\n/**\n * Parse `EXT-X-DATERANGE` attributes.\n *\n * Date-range tags carry timed metadata (e.g. ad markers, chapters) with an\n * `ID`, `START-DATE`, and optional `END-DATE`, `DURATION`, etc. All\n * unrecognised attributes are preserved in the `attributes` dictionary.\n *\n * 解析 `EXT-X-DATERANGE` 属性。\n *\n * 日期范围标签携带定时元数据(如广告标记、章节),包含 `ID`、`START-DATE`\n * 以及可选的 `END-DATE`、`DURATION` 等。所有未识别的属性都保留在 `attributes` 字典中。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A date-range descriptor, or `null` if `ID` is missing\n * @returns 日期范围描述符,如果缺少 `ID` 则返回 `null`\n *\n * @example\n * ```ts\n * const attrs = parseAttributes(\n * 'ID=\"ad1\",CLASS=\"com.apple.hls.interstitial\",START-DATE=\"2024-01-01T00:00:00Z\",DURATION=30.0'\n * );\n * parseDateRange(attrs);\n * // => {\n * // id: \"ad1\",\n * // class: \"com.apple.hls.interstitial\",\n * // startDate: \"2024-01-01T00:00:00Z\",\n * // duration: 30.0\n * // }\n * ```\n */\nexport function parseDateRange(attrs: Record<string, string>): {\n id: string;\n class?: string;\n startDate: string;\n endDate?: string;\n duration?: number;\n plannedDuration?: number;\n endOnNext?: boolean;\n attributes: Record<string, string>;\n} | null {\n const id = attrs[\"ID\"];\n if (!id) return null;\n return {\n id,\n class: attrs[\"CLASS\"],\n startDate: attrs[\"START-DATE\"] || \"\",\n endDate: attrs[\"END-DATE\"],\n duration: attrs[\"DURATION\"] ? parseFloat(attrs[\"DURATION\"]) : undefined,\n plannedDuration: attrs[\"PLANNED-DURATION\"] ? parseFloat(attrs[\"PLANNED-DURATION\"]) : undefined,\n endOnNext: attrs[\"END-ON-NEXT\"] === \"YES\",\n attributes: attrs,\n };\n}\n\n/**\n * Parse `EXT-X-STREAM-INF` (or `EXT-X-I-FRAME-STREAM-INF`) attributes into a\n * variant-stream descriptor.\n *\n * These tags appear in **master playlists** and describe available variant\n * streams. The `BANDWIDTH` attribute is **required**; returns `null` if it is\n * missing or not a valid number.\n *\n * 将 `EXT-X-STREAM-INF`(或 `EXT-X-I-FRAME-STREAM-INF`)属性解析为变体流描述符。\n *\n * 这些标签出现在**主播放列表**中,用于描述可用的变体流。\n * `BANDWIDTH` 属性是**必需的**;如果缺失或不是有效数字则返回 `null`。\n *\n * @param attrs - Pre-parsed attribute dictionary from {@link parseAttributes}\n * @param attrs - 通过 {@link parseAttributes} 预解析的属性字典\n * @returns A variant-stream descriptor, or `null` if `BANDWIDTH` is invalid\n * @returns 变体流描述符,如果 `BANDWIDTH` 无效则返回 `null`\n *\n * @example\n * ```ts\n * // Standard variant stream\n * // 标准变体流\n * const attrs = parseAttributes(\n * 'BANDWIDTH=2000000,AVERAGE-BANDWIDTH=1800000,CODECS=\"avc1.4d401e,mp4a.40.2\",RESOLUTION=1920x1080,FRAME-RATE=30'\n * );\n * parseStreamInf(attrs);\n * // => {\n * // bandwidth: 2000000,\n * // averageBandwidth: 1800000,\n * // codecs: \"avc1.4d401e,mp4a.40.2\",\n * // resolution: { width: 1920, height: 1080 },\n * // frameRate: 30\n * // }\n *\n * // I-Frame-only stream (used for trick-play)\n * // 仅 I 帧流(用于快进/快退)\n * const iframeAttrs = parseAttributes(\n * 'BANDWIDTH=500000,CODECS=\"avc1.4d401e\",RESOLUTION=640x360,URI=\"iframe.m3u8\"'\n * );\n * parseStreamInf(iframeAttrs);\n * // => { bandwidth: 500000, codecs: \"avc1.4d401e\", resolution: { width: 640, height: 360 } }\n *\n * // Audio-only variant\n * // 纯音频变体\n * const audioAttrs = parseAttributes(\n * 'BANDWIDTH=128000,CODECS=\"mp4a.40.2\",AUDIO=\"aac\",VIDEO=\"none\"'\n * );\n * parseStreamInf(audioAttrs);\n * // => { bandwidth: 128000, codecs: \"mp4a.40.2\", audio: \"aac\", video: \"none\" }\n * ```\n */\nexport function parseStreamInf(attrs: Record<string, string>): {\n bandwidth: number;\n averageBandwidth?: number;\n codecs?: string;\n resolution?: { width: number; height: number };\n frameRate?: number;\n hdcpLevel?: string;\n audio?: string;\n video?: string;\n subtitles?: string;\n closedCaptions?: string;\n score?: number;\n} | null {\n const bandwidth = parseInt(attrs[\"BANDWIDTH\"], 10);\n if (isNaN(bandwidth)) return null;\n return {\n bandwidth,\n averageBandwidth: attrs[\"AVERAGE-BANDWIDTH\"] ? parseInt(attrs[\"AVERAGE-BANDWIDTH\"], 10) : undefined,\n codecs: attrs[\"CODECS\"],\n resolution: attrs[\"RESOLUTION\"] ? (parseResolution(attrs[\"RESOLUTION\"]) ?? undefined) : undefined,\n frameRate: attrs[\"FRAME-RATE\"] ? parseFloat(attrs[\"FRAME-RATE\"]) : undefined,\n hdcpLevel: attrs[\"HDCP-LEVEL\"],\n audio: attrs[\"AUDIO\"],\n video: attrs[\"VIDEO\"],\n subtitles: attrs[\"SUBTITLES\"],\n closedCaptions: attrs[\"CLOSED-CAPTIONS\"],\n score: attrs[\"SCORE\"] ? parseFloat(attrs[\"SCORE\"]) : undefined,\n };\n}\n","/**\n * M3U8 Playlist Parser\n * Parses both master and media playlists with full tag support including LL-HLS.\n *\n * M3U8 播放列表解析器\n * 解析主播放列表和媒体播放列表,全面支持包括 LL-HLS 在内的所有标签。\n */\n\nimport {\n MediaPlaylist,\n MasterPlaylist,\n Segment,\n PlaylistType,\n KeyInfo,\n MapInfo,\n PartialSegment,\n PreloadHint,\n RenditionReport,\n ServerControl,\n VariantStream,\n MediaRendition,\n DateRange,\n} from \"../types\";\nimport {\n getTagName,\n getTagAttributes,\n parseAttributes,\n parseExtInf,\n parseByteRange,\n parseKeyAttributes,\n parseMapAttributes,\n parsePartAttributes,\n parseServerControl,\n parsePreloadHint,\n parseRenditionReport,\n parseDateRange,\n parseStreamInf,\n} from \"./tags\";\n\n/**\n * Union type representing the result of parsing an M3U8 playlist string.\n *\n * The discriminant `type` indicates whether the parsed result is a\n * {@link MediaPlaylist} or a {@link MasterPlaylist}.\n *\n * 表示 M3U8 播放列表字符串解析结果的联合类型。\n *\n * 判别字段 `type` 指示解析结果是 {@link MediaPlaylist} 还是 {@link MasterPlaylist}。\n */\nexport type ParseResult = { type: \"media\"; playlist: MediaPlaylist } | { type: \"master\"; playlist: MasterPlaylist };\n\n/**\n * Heuristically determine whether a playlist text is a **master playlist**\n * by checking for the presence of the `#EXT-X-STREAM-INF` tag.\n *\n * 通过检查是否存在 `#EXT-X-STREAM-INF` 标签,启发式判断播放列表文本是否为**主播放列表**。\n *\n * @param text - The raw playlist content\n * @param text - 原始播放列表内容\n * @returns `true` if the text contains at least one `#EXT-X-STREAM-INF` tag\n * @returns 如果文本至少包含一个 `#EXT-X-STREAM-INF` 标签则返回 `true`\n *\n * @example\n * ```ts\n * // Master playlist\n * // 主播放列表\n * isMasterPlaylist('#EXTM3U\\n#EXT-X-STREAM-INF:BANDWIDTH=2000000\\nvideo.m3u8');\n * // => true\n *\n * // Media playlist\n * // 媒体播放列表\n * isMasterPlaylist('#EXTM3U\\n#EXTINF:10.0,\\nsegment.ts\\n#EXT-X-ENDLIST');\n * // => false\n * ```\n */\nexport function isMasterPlaylist(text: string): boolean {\n return text.includes(\"#EXT-X-STREAM-INF\");\n}\n\n/**\n * Parse an M3U8 playlist string and return either a {@link MediaPlaylist} or\n * a {@link MasterPlaylist}, auto-detecting the playlist type.\n *\n * This is the **top-level entry point** for playlist parsing. It first calls\n * {@link isMasterPlaylist} to decide which specialised parser to delegate to.\n *\n * 解析 M3U8 播放列表字符串,返回 {@link MediaPlaylist} 或 {@link MasterPlaylist},自动检测播放列表类型。\n *\n * 这是播放列表解析的**顶层入口**。它首先调用 {@link isMasterPlaylist} 来决定委托给哪个专用解析器。\n *\n * @param text - The raw M3U8 playlist content\n * @param text - 原始 M3U8 播放列表内容\n * @param baseUrl - Optional base URL for resolving relative URIs\n * @param baseUrl - 可选的基础 URL,用于解析相对 URI\n * @returns A {@link ParseResult} discriminated union\n * @returns {@link ParseResult} 可辨识联合类型\n *\n * @example\n * ```ts\n * // Parsing a media (VOD) playlist\n * // 解析媒体(点播)播放列表\n * const mediaText = [\n * '#EXTM3U',\n * '#EXT-X-VERSION:3',\n * '#EXT-X-TARGETDURATION:10',\n * '#EXTINF:10.0,',\n * 'segment0.ts',\n * '#EXTINF:8.0,',\n * 'segment1.ts',\n * '#EXT-X-ENDLIST',\n * ].join('\\n');\n *\n * const result = parseM3U8(mediaText, 'https://example.com/');\n * // result.type === \"media\"\n * // result.playlist.segments.length === 2\n * // result.playlist.segments[0].uri === \"https://example.com/segment0.ts\"\n *\n * // Parsing a master playlist\n * // 解析主播放列表\n * const masterText = [\n * '#EXTM3U',\n * '#EXT-X-STREAM-INF:BANDWIDTH=2000000,RESOLUTION=1920x1080',\n * 'high.m3u8',\n * '#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360',\n * 'low.m3u8',\n * ].join('\\n');\n *\n * const result2 = parseM3U8(masterText);\n * // result2.type === \"master\"\n * // result2.playlist.variants.length === 2\n * // result2.playlist.variants[0].bandwidth === 2000000\n * ```\n */\nexport function parseM3U8(text: string, baseUrl?: string): ParseResult {\n if (isMasterPlaylist(text)) {\n return { type: \"master\", playlist: parseMasterPlaylist(text, baseUrl) };\n }\n return { type: \"media\", playlist: parseMediaPlaylist(text, baseUrl) };\n}\n\n/**\n * Parse a **media playlist** (VOD / live / event) from text.\n *\n * This function implements a **line-by-line stateful parser** that processes\n * each HLS tag and accumulates segment descriptors. Per-segment state (key,\n * map, discontinuity, gap, byte-range, program-date-time, date-range, bitrate)\n * is carried forward across consecutive `#EXTINF` + URI pairs and reset after\n * each segment is emitted.\n *\n * 从文本解析**媒体播放列表**(点播 / 直播 / 事件)。\n *\n * 此函数实现了一个**逐行有状态解析器**,处理每个 HLS 标签并累积分段描述符。\n * 每个分段的状态(密钥、映射、不连续、间隙、字节范围、节目日期时间、日期范围、比特率)\n * 在连续的 `#EXTINF` + URI 对之间向前传递,并在每个分段生成后重置。\n *\n * @param text - The raw media playlist content\n * @param text - 原始媒体播放列表内容\n * @param baseUrl - Optional base URL for resolving relative segment URIs\n * @param baseUrl - 可选的基础 URL,用于解析相对分段 URI\n * @returns A fully parsed {@link MediaPlaylist} object\n * @returns 完整解析的 {@link MediaPlaylist} 对象\n *\n * @example\n * ```ts\n * // Basic VOD playlist\n * // 基本点播播放列表\n * const text = [\n * '#EXTM3U',\n * '#EXT-X-VERSION:4',\n * '#EXT-X-TARGETDURATION:10',\n * '#EXT-X-MEDIA-SEQUENCE:0',\n * '#EXT-X-PLAYLIST-TYPE:VOD',\n * '#EXTINF:10.0,First Segment',\n * 'segment0.ts',\n * '#EXTINF:8.5,Second Segment',\n * 'segment1.ts',\n * '#EXT-X-ENDLIST',\n * ].join('\\n');\n *\n * const playlist = parseMediaPlaylist(text, 'https://example.com/');\n * console.log(playlist.version); // 4\n * console.log(playlist.targetDuration); // 10\n * console.log(playlist.segments.length); // 2\n * console.log(playlist.segments[0].title); // \"First Segment\"\n * console.log(playlist.duration); // 18.5\n *\n * // Live playlist with encryption\n * // 带加密的直播播放列表\n * const liveText = [\n * '#EXTM3U',\n * '#EXT-X-VERSION:3',\n * '#EXT-X-TARGETDURATION:6',\n * '#EXT-X-MEDIA-SEQUENCE:100',\n * '#EXT-X-KEY:METHOD=AES-128,URI=\"https://key.bin\",IV=0xABCDEF',\n * '#EXTINF:6.0,',\n * 'segment100.ts',\n * '#EXTINF:6.0,',\n * 'segment101.ts',\n * ].join('\\n');\n *\n * const livePlaylist = parseMediaPlaylist(liveText);\n * console.log(livePlaylist.segments[0].key?.method); // EncryptionMethod.AES_128\n * console.log(livePlaylist.segments[0].sequence); // 100\n * ```\n */\nexport function parseMediaPlaylist(text: string, baseUrl?: string): MediaPlaylist {\n // Split into lines, handling both LF and CRLF line endings.\n // 按行分割,同时处理 LF 和 CRLF 换行符。\n const lines = text.split(/\\r?\\n/);\n const segments: Segment[] = [];\n const partialSegments: PartialSegment[] = [];\n const renditionReports: RenditionReport[] = [];\n\n // --- Playlist-level state accumulated from global tags ---\n // --- 从全局标签累积的播放列表级别状态 ---\n let version = 3;\n let playlistType: PlaylistType = PlaylistType.LIVE;\n let targetDuration = 0;\n let mediaSequence = 0;\n let discontinuitySequence = 0;\n let allowCache: boolean | undefined;\n let independentSegments: boolean | undefined;\n let startOffset: number | undefined;\n let iFramesOnly = false;\n let partTargetDuration: number | undefined;\n let serverControl: ServerControl | undefined;\n let preloadHint: PreloadHint | undefined;\n\n // --- Per-segment state that is carried forward across segments ---\n // --- 跨分段向前传递的每个分段的状态 ---\n // These values are set by tags like #EXT-X-KEY, #EXT-X-MAP, etc. and\n // apply to all subsequent segments until overridden.\n // 这些值由 #EXT-X-KEY、#EXT-X-MAP 等标签设置,并应用于后续所有分段,直到被覆盖。\n let currentKey: KeyInfo | undefined;\n let currentMap: MapInfo | undefined;\n let currentDiscontinuity = false;\n let currentGap = false;\n let currentByteRange: { length: number; offset?: number } | undefined;\n let currentProgramDateTime: Date | undefined;\n let currentDateRange: DateRange | undefined;\n let currentBitrate: number | undefined;\n\n let totalDuration = 0;\n let segmentIndex = 0;\n\n // Main parse loop: iterate line by line.\n // 主解析循环:逐行迭代。\n let i = 0;\n while (i < lines.length) {\n const line = lines[i].trim();\n\n // Skip empty lines and pure comments (lines starting with '#' but not '#EXT').\n // 跳过空行和纯注释(以 '#' 开头但不是 '#EXT' 的行)。\n if (!line || (line.startsWith(\"#\") && !line.startsWith(\"#EXT\"))) {\n i++;\n continue;\n }\n\n if (line.startsWith(\"#EXT\")) {\n const tagName = getTagName(line);\n const attrStr = getTagAttributes(line);\n const attrs = parseAttributes(attrStr);\n\n switch (tagName) {\n case \"#EXTM3U\":\n // Start of playlist marker. No action needed — just continue parsing.\n // 播放列表起始标记。无需操作 — 继续解析即可。\n break;\n\n case \"#EXT-X-VERSION\":\n // HLS protocol version. Defaults to 3 if unparseable.\n // HLS 协议版本。如果无法解析则默认为 3。\n version = parseInt(attrStr, 10) || 3;\n break;\n\n case \"#EXT-X-PLAYLIST-TYPE\":\n // VOD, EVENT, or live (default). EVENT is treated as live here because\n // new segments can still be appended; only #EXT-X-ENDLIST signals VOD.\n // VOD、EVENT 或直播(默认)。此处 EVENT 被视为直播,因为仍可追加新分段;\n // 只有 #EXT-X-ENDLIST 才表示点播。\n if (attrStr === \"VOD\") playlistType = PlaylistType.VOD;\n else if (attrStr === \"EVENT\") playlistType = PlaylistType.LIVE; // EVENT is like live\n break;\n\n case \"#EXT-X-TARGETDURATION\":\n // Maximum segment duration in seconds. Required for every playlist.\n // 最大分段时长(秒)。每个播放列表都必须包含此值。\n targetDuration = parseFloat(attrStr) || 0;\n break;\n\n case \"#EXT-X-MEDIA-SEQUENCE\":\n // Sequence number of the first segment. Used to calculate each segment's\n // absolute sequence number: mediaSequence + segmentIndex.\n // 第一个分段的序列号。用于计算每个分段的绝对序列号:mediaSequence + segmentIndex。\n mediaSequence = parseInt(attrStr, 10) || 0;\n break;\n\n case \"#EXT-X-DISCONTINUITY-SEQUENCE\":\n // Starting discontinuity sequence number. Incremented per #EXT-X-DISCONTINUITY.\n // 起始不连续序列号。每个 #EXT-X-DISCONTINUITY 标签使其递增。\n discontinuitySequence = parseInt(attrStr, 10) || 0;\n break;\n\n case \"#EXT-X-ENDLIST\":\n // Explicitly marks the playlist as VOD (no more segments will be added).\n // 显式将播放列表标记为点播(不再添加新分段)。\n playlistType = PlaylistType.VOD;\n break;\n\n case \"#EXT-X-ALLOW-CACHE\":\n // Whether the client may cache downloaded segments.\n // 客户端是否可以缓存已下载的分段。\n allowCache = attrStr === \"YES\";\n break;\n\n case \"#EXT-X-INDEPENDENT-SEGMENTS\":\n // All segments are independently decodable (no cross-segment dependencies).\n // 所有分段均可独立解码(无跨分段依赖)。\n independentSegments = true;\n break;\n\n case \"#EXT-X-START\":\n // Preferred start time offset for playback.\n // 播放的首选起始时间偏移。\n startOffset = attrs[\"TIME-OFFSET\"] ? parseFloat(attrs[\"TIME-OFFSET\"]) : undefined;\n break;\n\n case \"#EXT-X-I-FRAMES-ONLY\":\n // The playlist contains only I-frames (used for trick-play / scrubbing).\n // 播放列表仅包含 I 帧(用于快进/快退/拖拽预览)。\n iFramesOnly = true;\n break;\n\n case \"#EXT-X-PART-INF\":\n // LL-HLS: target duration for partial segments within a segment.\n // LL-HLS:分段内部分段的目标时长。\n partTargetDuration = attrs[\"PART-TARGET\"] ? parseFloat(attrs[\"PART-TARGET\"]) : undefined;\n break;\n\n case \"#EXT-X-SERVER-CONTROL\":\n // LL-HLS: server-side control parameters (skip, block-reload, hold-back).\n // LL-HLS:服务器端控制参数(跳过、阻塞重载、保持)。\n serverControl = parseServerControl(attrs);\n break;\n\n case \"#EXT-X-KEY\":\n // Encryption key for subsequent segments. Carried forward until the next\n // #EXT-X-KEY tag (or #EXT-X-SESSION-KEY in master playlists).\n // 后续分段的加密密钥。向前传递直到下一个 #EXT-X-KEY 标签出现\n // (或主播放列表中的 #EXT-X-SESSION-KEY)。\n currentKey = parseKeyAttributes(attrs);\n if (currentKey && currentKey.uri && baseUrl) {\n currentKey.uri = resolveAgainstBase(currentKey.uri, baseUrl);\n }\n break;\n\n case \"#EXT-X-MAP\":\n // Media initialisation segment for subsequent segments.\n // 后续分段的媒体初始化段。\n currentMap = parseMapAttributes(attrs) ?? undefined;\n if (currentMap && currentMap.uri && baseUrl) {\n currentMap.uri = resolveAgainstBase(currentMap.uri, baseUrl);\n }\n break;\n\n case \"#EXT-X-DISCONTINUITY\":\n // Signals a discontinuity (e.g. codec, encoding, or timeline change).\n // Also increments the global discontinuity sequence.\n // 表示不连续点(如编解码器、编码或时间线变化)。\n // 同时递增全局不连续序列号。\n currentDiscontinuity = true;\n discontinuitySequence++;\n break;\n\n case \"#EXT-X-GAP\":\n // Marks a \"gap\" segment that should not be played (e.g. for ad insertion).\n // 标记不应播放的\"间隙\"分段(如用于广告插入)。\n currentGap = true;\n break;\n\n case \"#EXT-X-BITRATE\":\n // Advisory bitrate for the next segment.\n // 下一个分段的参考比特率。\n currentBitrate = parseInt(attrStr, 10) || undefined;\n break;\n\n case \"#EXT-X-PROGRAM-DATE-TIME\":\n // Absolute wall-clock date/time of the next segment's first sample.\n // 下一个分段首个采样点的绝对挂钟日期/时间。\n currentProgramDateTime = new Date(attrStr);\n break;\n\n case \"#EXT-X-DATERANGE\": {\n // Timed metadata range (e.g. ads, chapters). Carried forward for the\n // segment immediately following this tag.\n // 定时元数据范围(如广告、章节)。向前传递到紧接此标签之后的分段。\n const dateRange = parseDateRange(attrs);\n if (dateRange) currentDateRange = dateRange;\n break;\n }\n\n case \"#EXTINF\": {\n // --- Segment entry ---\n // This tag defines a segment's duration and optional title.\n // The URI follows on the very next line.\n // --- 分段条目 ---\n // 此标签定义分段的时长和可选标题。\n // URI 紧随在下一行。\n const info = parseExtInf(line);\n // Advance to the URI line.\n // 前进到 URI 行。\n i++;\n if (i < lines.length) {\n const uri = lines[i].trim();\n // Only treat the line as a URI if it's non-empty and not a comment/tag.\n // 仅当该行非空且非注释/标签时才将其视为 URI。\n if (uri && !uri.startsWith(\"#\")) {\n // Resolve relative URIs against the base URL.\n // 相对于基础 URL 解析相对 URI。\n const resolvedUri = baseUrl ? resolveAgainstBase(uri, baseUrl) : uri;\n const segment: Segment = {\n sequence: mediaSequence + segmentIndex,\n duration: info?.duration ?? 0,\n uri: resolvedUri,\n title: info?.title,\n byteRange: currentByteRange,\n key: currentKey,\n map: currentMap,\n programDateTime: currentProgramDateTime,\n discontinuity: currentDiscontinuity,\n gap: currentGap,\n dateRange: currentDateRange,\n bitrate: currentBitrate,\n };\n\n segments.push(segment);\n totalDuration += segment.duration;\n segmentIndex++;\n }\n }\n\n // Reset per-segment state so the next segment starts fresh.\n // 重置每个分段的状态,以便下一个分段从干净状态开始。\n currentDiscontinuity = false;\n currentGap = false;\n currentByteRange = undefined;\n currentProgramDateTime = undefined;\n currentDateRange = undefined;\n currentBitrate = undefined;\n break;\n }\n\n case \"#EXT-X-BYTERANGE\":\n // Byte-range for the next segment/part. If no offset is given the range\n // is contiguous from the previous byte-range end.\n // 下一个分段/部分的字节范围。如果未给出偏移量,则紧接上一个字节范围末尾。\n currentByteRange = parseByteRange(line) ?? undefined;\n break;\n\n case \"#EXT-X-PART\": {\n // LL-HLS: partial segment within a larger segment.\n // LL-HLS:较大分段内的部分段。\n const part = parsePartAttributes(attrs);\n if (part) {\n part.uri = baseUrl ? resolveAgainstBase(part.uri, baseUrl) : part.uri;\n partialSegments.push(part);\n }\n break;\n }\n\n case \"#EXT-X-PRELOAD-HINT\": {\n // LL-HLS: resource that is being produced and will be available soon.\n // LL-HLS:正在生成且即将可用的资源。\n const hint = parsePreloadHint(attrs);\n if (hint) {\n hint.uri = baseUrl ? resolveAgainstBase(hint.uri, baseUrl) : hint.uri;\n preloadHint = hint;\n }\n break;\n }\n\n case \"#EXT-X-RENDITION-REPORT\": {\n // LL-HLS: last-MSN / last-Part for an alternate rendition.\n // LL-HLS:备用变体的最后 MSN / Part。\n const report = parseRenditionReport(attrs);\n if (report) {\n report.uri = baseUrl ? resolveAgainstBase(report.uri, baseUrl) : report.uri;\n renditionReports.push(report);\n }\n break;\n }\n\n default:\n // Unknown tag — could be a custom/vendor tag. Silently skip.\n // 未知标签 — 可能是自定义/厂商标签。静默跳过。\n break;\n }\n }\n\n i++;\n }\n\n return {\n type: playlistType,\n version,\n targetDuration,\n mediaSequence,\n discontinuitySequence,\n segments,\n partialSegments: partialSegments.length > 0 ? partialSegments : undefined,\n preloadHint,\n renditionReports: renditionReports.length > 0 ? renditionReports : undefined,\n serverControl,\n allowCache,\n independentSegments,\n startOffset,\n iFramesOnly,\n partTargetDuration,\n duration: totalDuration,\n endTime: totalDuration,\n raw: text,\n };\n}\n\n/**\n * Parse a **master playlist** from text.\n *\n * A master playlist enumerates the available variant streams (different\n * bitrates/resolutions), media renditions (audio, subtitles, etc.), and\n * session-level configuration (session data & session keys).\n *\n * 从文本解析**主播放列表**。\n *\n * 主播放列表枚举可用的变体流(不同比特率/分辨率)、媒体变体(音频、字幕等)\n * 以及会话级配置(会话数据和会话密钥)。\n *\n * @param text - The raw master playlist content\n * @param text - 原始主播放列表内容\n * @param baseUrl - Optional base URL for resolving relative variant/rendition URIs\n * @param baseUrl - 可选的基础 URL,用于解析相对变体/媒体 URI\n * @returns A fully parsed {@link MasterPlaylist} object\n * @returns 完整解析的 {@link MasterPlaylist} 对象\n *\n * @example\n * ```ts\n * // Multi-bitrate master playlist\n * // 多码率主播放列表\n * const text = [\n * '#EXTM3U',\n * '#EXT-X-VERSION:4',\n * '#EXT-X-INDEPENDENT-SEGMENTS',\n * '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"English\",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE=\"en\",URI=\"audio-en.m3u8\"',\n * '#EXT-X-STREAM-INF:BANDWIDTH=2000000,AVERAGE-BANDWIDTH=1800000,CODECS=\"avc1.4d401e,mp4a.40.2\",RESOLUTION=1920x1080,FRAME-RATE=30,AUDIO=\"aac\"',\n * 'high.m3u8',\n * '#EXT-X-STREAM-INF:BANDWIDTH=800000,CODECS=\"avc1.4d401e,mp4a.40.2\",RESOLUTION=640x360,AUDIO=\"aac\"',\n * 'low.m3u8',\n * '#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=200000,CODECS=\"avc1.4d401e\",RESOLUTION=640x360,URI=\"iframe.m3u8\"',\n * ].join('\\n');\n *\n * const master = parseMasterPlaylist(text, 'https://example.com/');\n * console.log(master.version); // 4\n * console.log(master.variants.length); // 3 (2 streams + 1 I-frame)\n * console.log(master.variants[1].bandwidth); // 800000\n * console.log(master.mediaRenditions?.length); // 1\n * console.log(master.mediaRenditions![0].name); // \"English\"\n *\n * // Session data & keys\n * // 会话数据和密钥\n * const sessionText = [\n * '#EXTM3U',\n * '#EXT-X-SESSION-DATA:DATA-ID=\"userId\",VALUE=\"abc123\"',\n * '#EXT-X-SESSION-KEY:METHOD=AES-128,URI=\"https://key.bin\"',\n * '#EXT-X-STREAM-INF:BANDWIDTH=1000000',\n * 'stream.m3u8',\n * ].join('\\n');\n *\n * const sessionMaster = parseMasterPlaylist(sessionText);\n * console.log(sessionMaster.sessionData); // { userId: \"abc123\" }\n * console.log(sessionMaster.sessionKeys?.length); // 1\n * ```\n */\nexport function parseMasterPlaylist(text: string, baseUrl?: string): MasterPlaylist {\n // Split into lines, handling both LF and CRLF line endings.\n // 按行分割,同时处理 LF 和 CRLF 换行符。\n const lines = text.split(/\\r?\\n/);\n const variants: VariantStream[] = [];\n const mediaRenditions: MediaRendition[] = [];\n const sessionData: Record<string, string> = {};\n const sessionKeys: KeyInfo[] = [];\n\n let version = 3;\n let independentSegments: boolean | undefined;\n\n // Holds the attributes of the most recent #EXT-X-STREAM-INF tag so they\n // can be combined with the URI on the following line.\n // 保存最近一个 #EXT-X-STREAM-INF 标签的属性,以便与下一行的 URI 组合。\n let currentStreamAttrs: Record<string, string> | null = null;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n // Skip empty lines and non-tag comments.\n // 跳过空行和非标签注释。\n if (!line || (line.startsWith(\"#\") && !line.startsWith(\"#EXT\"))) continue;\n\n if (line.startsWith(\"#EXT\")) {\n const tagName = getTagName(line);\n const attrStr = getTagAttributes(line);\n const attrs = parseAttributes(attrStr);\n\n switch (tagName) {\n case \"#EXTM3U\":\n // Start of playlist marker.\n // 播放列表起始标记。\n break;\n\n case \"#EXT-X-VERSION\":\n // HLS protocol version.\n // HLS 协议版本。\n version = parseInt(attrStr, 10) || 3;\n break;\n\n case \"#EXT-X-INDEPENDENT-SEGMENTS\":\n // All segments across all variants are independently decodable.\n // 所有变体中的所有分段均可独立解码。\n independentSegments = true;\n break;\n\n case \"#EXT-X-STREAM-INF\": {\n // Variant stream descriptor. The URI follows on the next line.\n // 变体流描述符。URI 紧随在下一行。\n currentStreamAttrs = attrs;\n // Advance to read the URI line.\n // 前进以读取 URI 行。\n i++;\n if (i < lines.length) {\n const uri = lines[i].trim();\n if (uri && !uri.startsWith(\"#\")) {\n const info = parseStreamInf(currentStreamAttrs);\n if (info) {\n variants.push({\n uri: baseUrl ? resolveAgainstBase(uri, baseUrl) : uri,\n bandwidth: info.bandwidth,\n averageBandwidth: info.averageBandwidth,\n codecs: info.codecs,\n resolution: info.resolution,\n frameRate: info.frameRate,\n hdcpLevel: info.hdcpLevel,\n audio: info.audio,\n video: info.video,\n subtitles: info.subtitles,\n closedCaptions: info.closedCaptions,\n score: info.score,\n attributes: currentStreamAttrs,\n });\n }\n }\n }\n currentStreamAttrs = null;\n break;\n }\n\n case \"#EXT-X-I-FRAME-STREAM-INF\": {\n // I-frame-only variant (for trick-play). Unlike #EXT-X-STREAM-INF,\n // the URI is included as an attribute rather than on the next line.\n // 仅 I 帧变体(用于快进/快退)。与 #EXT-X-STREAM-INF 不同,\n // URI 作为属性包含在内,而非在下一行。\n const info = parseStreamInf(attrs);\n const uri = attrs[\"URI\"];\n if (info && uri) {\n variants.push({\n uri: baseUrl ? resolveAgainstBase(uri, baseUrl) : uri,\n bandwidth: info.bandwidth,\n codecs: info.codecs,\n resolution: info.resolution,\n attributes: attrs,\n });\n }\n break;\n }\n\n case \"#EXT-X-MEDIA\": {\n // Alternate media rendition (audio, video, subtitles, closed-captions).\n // 备用媒体变体(音频、视频、字幕、隐藏式字幕)。\n const rendition: MediaRendition = {\n type: (attrs[\"TYPE\"] || \"AUDIO\") as MediaRendition[\"type\"],\n uri: attrs[\"URI\"] ? (baseUrl ? resolveAgainstBase(attrs[\"URI\"], baseUrl) : attrs[\"URI\"]) : undefined,\n groupId: attrs[\"GROUP-ID\"] || \"\",\n language: attrs[\"LANGUAGE\"],\n assocLanguage: attrs[\"ASSOC-LANGUAGE\"],\n name: attrs[\"NAME\"] || \"\",\n default: attrs[\"DEFAULT\"] === \"YES\",\n autoselect: attrs[\"AUTOSELECT\"] === \"YES\",\n forced: attrs[\"FORCED\"] === \"YES\",\n instreamId: attrs[\"INSTREAM-ID\"],\n characteristics: attrs[\"CHARACTERISTICS\"],\n channels: attrs[\"CHANNELS\"],\n };\n mediaRenditions.push(rendition);\n break;\n }\n\n case \"#EXT-X-SESSION-DATA\":\n // Arbitrary session-level key-value data.\n // 任意的会话级键值数据。\n if (attrs[\"DATA-ID\"] && attrs[\"VALUE\"]) {\n sessionData[attrs[\"DATA-ID\"]] = attrs[\"VALUE\"];\n }\n break;\n\n case \"#EXT-X-SESSION-KEY\": {\n // Session-wide encryption key (applies to all variants).\n // 会话范围的加密密钥(适用于所有变体)。\n const keyInfo = parseKeyAttributes(attrs);\n if (keyInfo && keyInfo.uri && baseUrl) {\n keyInfo.uri = resolveAgainstBase(keyInfo.uri, baseUrl);\n }\n sessionKeys.push(keyInfo);\n break;\n }\n }\n }\n }\n\n return {\n version,\n variants,\n mediaRenditions: mediaRenditions.length > 0 ? mediaRenditions : undefined,\n sessionData: Object.keys(sessionData).length > 0 ? sessionData : undefined,\n sessionKeys: sessionKeys.length > 0 ? sessionKeys : undefined,\n independentSegments,\n raw: text,\n };\n}\n\n/**\n * Resolve a (possibly relative) URI against a base URL.\n *\n * Handles the following cases:\n * - **Absolute URI** (`https://...`, `data:...`) → returned as-is\n * - **Protocol-relative** (`//example.com/...`) → `https:` prepended\n * - **Absolute path** (`/path/file.ts`) → resolved against the base origin\n * - **Relative path** (`file.ts`, `../file.ts`) → resolved against the base directory\n *\n * 根据基础 URL 解析(可能为相对的)URI。\n *\n * 处理以下情况:\n * - **绝对 URI**(`https://...`、`data:...`)→ 原样返回\n * - **协议相对**(`//example.com/...`)→ 在前面加上 `https:`\n * - **绝对路径**(`/path/file.ts`)→ 相对于基础源进行解析\n * - **相对路径**(`file.ts`、`../file.ts`)→ 相对于基础目录进行解析\n *\n * @param uri - The URI to resolve (may be relative or absolute)\n * @param uri - 待解析的 URI(可以是相对或绝对)\n * @param baseUrl - The base URL to resolve against\n * @param baseUrl - 用于解析的基础 URL\n * @returns The resolved absolute URL string\n * @returns 解析后的绝对 URL 字符串\n *\n * @internal\n */\nfunction resolveAgainstBase(uri: string, baseUrl: string): string {\n // Absolute URI (has scheme) — return as-is.\n // 绝对 URI(带有协议方案)— 原样返回。\n if (/^[a-zA-Z][a-zA-Z0-9+\\-.]*:/.test(uri)) return uri;\n\n // Protocol-relative URI — prepend https:.\n // 协议相对 URI — 在前面加上 https:。\n if (uri.startsWith(\"//\")) return \"https:\" + uri;\n\n // Absolute path — resolve against the base origin.\n // 绝对路径 — 相对于基础源进行解析。\n if (uri.startsWith(\"/\")) {\n try {\n const base = new URL(baseUrl);\n return `${base.protocol}//${base.host}${uri}`;\n } catch {\n // If baseUrl is not a valid absolute URL, return the URI unchanged.\n // 如果 baseUrl 不是有效的绝对 URL,则原样返回 URI。\n return uri;\n }\n }\n\n // Relative path — resolve against the base directory (everything before the last '/').\n // 相对路径 — 相对于基础目录(最后一个 '/' 之前的所有内容)进行解析。\n const baseDir = baseUrl.substring(0, baseUrl.lastIndexOf(\"/\") + 1);\n return baseDir + uri;\n}\n\n/**\n * Convenience helper to extract the {@link PlaylistType} from an already-parsed\n * media playlist.\n *\n * 从已解析的媒体播放列表中提取 {@link PlaylistType} 的便捷辅助函数。\n *\n * @param playlist - A parsed media playlist\n * @param playlist - 已解析的媒体播放列表\n * @returns The playlist type (`VOD` or `LIVE`)\n * @returns 播放列表类型(`VOD` 或 `LIVE`)\n *\n * @example\n * ```ts\n * const playlist = parseMediaPlaylist('#EXTM3U\\n#EXT-X-ENDLIST\\n...');\n * detectPlaylistType(playlist); // => PlaylistType.VOD\n * ```\n */\nexport function detectPlaylistType(playlist: MediaPlaylist): PlaylistType {\n return playlist.type;\n}\n","/**\n * URL resolution utilities for HLS-IO.\n *\n * Handles relative and absolute URIs for segments and keys. This module provides\n * the foundation for all network resource location within the HLS pipeline —\n * resolving segment URLs relative to playlists, constructing cache keys, parsing\n * query strings, and inferring container formats from file extensions.\n *\n * HLS-IO 的 URL 解析工具集。\n * 处理分段和密钥的相对与绝对 URI。本模块为 HLS 流水线中的所有网络资源定位\n * 提供基础能力 —— 包括相对播放列表解析分段 URL、构造缓存键、解析查询字符串,\n * 以及从文件扩展名推断容器格式。\n *\n * @module url\n */\n\n/**\n * Check if a URI is absolute (has a scheme or is protocol-relative).\n *\n * An absolute URI either begins with a scheme (e.g. `https://`, `data:`) or is\n * protocol-relative (starts with `//`). This distinction is critical because\n * absolute URIs should never be resolved against a base URL.\n *\n * 检查 URI 是否为绝对路径(具有协议前缀或为协议相对路径)。\n *\n * 绝对 URI 要么以协议开头(如 `https://`、`data:`),要么是协议相对路径(以 `//` 开头)。\n * 这一判断至关重要,因为绝对 URI 不应再基于其他 URL 进行解析。\n *\n * @param uri - The URI string to test\n * 待检测的 URI 字符串\n * @returns `true` if the URI is absolute\n * 如果 URI 为绝对路径则返回 `true`\n *\n * @example\n * ```ts\n * isAbsoluteUri(\"https://example.com/video.m3u8\"); // true — has scheme\n * isAbsoluteUri(\"//cdn.example.com/seg.ts\"); // true — protocol-relative\n * isAbsoluteUri(\"/path/to/segment.ts\"); // false — path-absolute but no scheme\n * isAbsoluteUri(\"segment.ts\"); // false — relative path\n * ```\n */\nexport function isAbsoluteUri(uri: string): boolean {\n // Matches any [scheme]: or // prefix (protocol-relative)\n // 匹配任意 [scheme]: 或 // 前缀(协议相对路径)\n return /^([a-zA-Z][a-zA-Z0-9+\\-.]*:|\\/\\/)/.test(uri);\n}\n\n/**\n * Check if a URI is a full URL (starts with `http://` or `https://`).\n *\n * Unlike `isAbsoluteUri`, this only returns `true` for HTTP(S) URLs. It will\n * return `false` for other absolute URIs like `data:`, `blob:`, or `//`.\n *\n * 检查 URI 是否为完整的 HTTP(S) URL(以 `http://` 或 `https://` 开头)。\n *\n * 与 `isAbsoluteUri` 不同,此函数仅对 HTTP(S) URL 返回 `true`。对于其他绝对\n * URI(如 `data:`、`blob:` 或 `//` 协议相对路径)将返回 `false`。\n *\n * @param uri - The URI string to test\n * 待检测的 URI 字符串\n * @returns `true` if the URI starts with `http://` or `https://`\n * 如果 URI 以 `http://` 或 `https://` 开头则返回 `true`\n *\n * @example\n * ```ts\n * isHttpUrl(\"https://example.com/video.m3u8\"); // true\n * isHttpUrl(\"http://cdn.example.com/seg.ts\"); // true\n * isHttpUrl(\"//cdn.example.com/seg.ts\"); // false — protocol-relative\n * isHttpUrl(\"data:text/plain,hello\"); // false — not HTTP\n * ```\n */\nexport function isHttpUrl(uri: string): boolean {\n return /^https?:\\/\\//i.test(uri);\n}\n\n/**\n * Check if a URI is a protocol-relative URL (starts with `//`).\n *\n * Protocol-relative URLs inherit the current page's protocol (`http:` or `https:`).\n * They are useful for avoiding mixed-content warnings when serving from CDNs.\n *\n * 检查 URI 是否为协议相对 URL(以 `//` 开头)。\n *\n * 协议相对 URL 会继承当前页面的协议(`http:` 或 `https:`)。从 CDN 提供服务时,\n * 使用协议相对 URL 可以有效避免混合内容警告。\n *\n * @param uri - The URI string to test\n * 待检测的 URI 字符串\n * @returns `true` if the URI starts with `//`\n * 如果 URI 以 `//` 开头则返回 `true`\n *\n * @example\n * ```ts\n * isProtocolRelative(\"//cdn.example.com/seg.ts\"); // true\n * isProtocolRelative(\"https://example.com\"); // false\n * isProtocolRelative(\"/path/to/file\"); // false\n * ```\n */\nexport function isProtocolRelative(uri: string): boolean {\n return /^\\/\\//.test(uri);\n}\n\n/**\n * Resolve a relative URI against a base URL to produce a fully-qualified URL.\n *\n * This is the core URL resolution function in HLS-IO. HLS playlists often contain\n * relative URIs for segments and keys — this function resolves them following the\n * RFC 3986 URI resolution algorithm adapted for HLS contexts:\n *\n * 1. **Empty URI** → return the base URL as-is.\n * 2. **Already absolute** (has scheme or `//`) → return the URI unchanged.\n * 3. **Protocol-relative** (`//host/path`) → prepend the current page's protocol\n * (`http:` or `https:`).\n * 4. **Path-absolute** (`/path/segment.ts`) → replace the base URL's path entirely,\n * keeping only the origin (`scheme://host`).\n * 5. **Path-relative** (`segment.ts` or `subdir/segment.ts`) → resolve against the\n * base URL's directory (everything before the last `/`).\n *\n * 这是 HLS-IO 中核心的 URL 解析函数。HLS 播放列表中常包含分段和密钥的相对 URI ——\n * 此函数按照 RFC 3986 的 URI 解析算法(适配 HLS 上下文)进行解析:\n *\n * 1. **空 URI** → 直接返回基础 URL。\n * 2. **已是绝对路径**(有协议前缀或 `//`)→ 原样返回 URI。\n * 3. **协议相对路径**(`//host/path`)→ 在前面加上当前页面的协议(`http:` 或 `https:`)。\n * 4. **路径绝对路径**(`/path/segment.ts`)→ 完全替换基础 URL 的路径,仅保留源(`scheme://host`)。\n * 5. **路径相对路径**(`segment.ts` 或 `subdir/segment.ts`)→ 基于基础 URL 的目录\n * (最后一个 `/` 之前的部分)进行解析。\n *\n * @param uri - The potentially-relative URI to resolve\n * 需要解析的可能为相对路径的 URI\n * @param baseUrl - The absolute base URL to resolve against\n * 用于解析的绝对基础 URL\n * @returns The fully-resolved absolute URL\n * 解析后的完整绝对 URL\n *\n * @example\n * ```ts\n * // Case 1: Empty URI → returns base URL\n * resolveUri(\"\", \"https://example.com/playlist.m3u8\");\n * // \"https://example.com/playlist.m3u8\"\n *\n * // Case 2: Already absolute → returned unchanged\n * resolveUri(\"https://other.com/seg.ts\", \"https://example.com/playlist.m3u8\");\n * // \"https://other.com/seg.ts\"\n *\n * // Case 3: Protocol-relative → browser protocol prepended\n * resolveUri(\"//cdn.example.com/seg.ts\", \"https://example.com/playlist.m3u8\");\n * // \"https://cdn.example.com/seg.ts\"\n *\n * // Case 4: Path-absolute → replaces base path entirely\n * resolveUri(\"/videos/seg.ts\", \"https://example.com/hls/playlist.m3u8\");\n * // \"https://example.com/videos/seg.ts\"\n *\n * // Case 5: Path-relative → resolved against base directory\n * resolveUri(\"seg.ts\", \"https://example.com/hls/playlist.m3u8\");\n * // \"https://example.com/hls/seg.ts\"\n *\n * resolveUri(\"subdir/seg.ts\", \"https://example.com/hls/playlist.m3u8\");\n * // \"https://example.com/hls/subdir/seg.ts\"\n * ```\n */\nexport function resolveUri(uri: string, baseUrl: string): string {\n // Empty URI: return the base as-is (caller probably meant \"use the playlist URL itself\")\n // 空 URI:原样返回基础 URL(调用者可能意指\"使用播放列表 URL 本身\")\n if (!uri) return baseUrl;\n\n // Already absolute (has scheme or //): return unchanged — no resolution needed\n // 已经是绝对路径(有协议前缀或 //):原样返回 —— 无需解析\n if (isAbsoluteUri(uri)) return uri;\n\n // Protocol-relative (//host/path): inherit the browser's current protocol\n // 协议相对路径(//host/path):继承浏览器当前页面的协议\n if (isProtocolRelative(uri)) {\n if (typeof window !== \"undefined\" && window.location) {\n return window.location.protocol + uri;\n }\n // Fallback for non-browser environments (SSR, workers, etc.)\n // 非浏览器环境的回退方案(SSR、Worker 等)\n return \"https:\" + uri;\n }\n\n // Path-absolute (/path/segment.ts): take only the origin from the base URL\n // 路径绝对路径(/path/segment.ts):从基础 URL 中只取源部分\n if (uri.startsWith(\"/\")) {\n const base = new URL(baseUrl);\n return `${base.protocol}//${base.host}${uri}`;\n }\n\n // Path-relative (segment.ts or subdir/segment.ts): resolve against the base's directory\n // 路径相对路径(segment.ts 或 subdir/segment.ts):基于基础 URL 的目录解析\n // \"https://example.com/hls/playlist.m3u8\" → \"https://example.com/hls/\"\n const baseDir = baseUrl.substring(0, baseUrl.lastIndexOf(\"/\") + 1);\n const resolved = baseDir + uri;\n\n // Normalize ../ and ./ segments in the resolved path\n // 规范化解析结果中的 ../ 和 ./ 路径段\n if (resolved.includes(\"../\") || resolved.includes(\"./\")) {\n try {\n return new URL(resolved).href;\n } catch {\n // If URL constructor fails (e.g. non-http base), return as-is\n return resolved;\n }\n }\n\n return resolved;\n}\n\n/**\n * Extract the base URL (directory) from a full URL.\n *\n * Strips the resource portion (everything after the last `/`) and returns the\n * directory prefix. This is used to resolve relative URIs — the base URL is\n * the \"current directory\" of the resource being processed.\n *\n * 从完整的 URL 中提取基础 URL(目录部分)。\n *\n * 去除资源部分(最后一个 `/` 之后的所有内容),返回目录前缀。这用于解析相对 URI ——\n * 基础 URL 即正在处理的资源的\"当前目录\"。\n *\n * **Edge cases handled:**\n * - **No scheme** (`path/to/file`): returns everything up to and including the last `/`.\n * - **Root path** (`https://example.com`): appends `/` to make it a valid directory base.\n * - **Trailing slash already present** (`https://example.com/hls/`): preserved as-is.\n *\n * **边界情况处理:**\n * - **无协议**(`path/to/file`):返回最后一个 `/` 之前的所有内容(含 `/`)。\n * - **根路径**(`https://example.com`):追加 `/` 使其成为有效的目录基础。\n * - **已有尾部斜杠**(`https://example.com/hls/`):原样保留。\n *\n * @param url - The full URL to extract the base from\n * 待提取基础的完整 URL\n * @returns The base directory URL (always ends with `/`)\n * 基础目录 URL(始终以 `/` 结尾)\n *\n * @example\n * ```ts\n * getBaseUrl(\"https://example.com/hls/playlist.m3u8\");\n * // \"https://example.com/hls/\"\n *\n * getBaseUrl(\"https://example.com/hls/\");\n * // \"https://example.com/hls/\"\n *\n * getBaseUrl(\"https://example.com\");\n * // \"https://example.com/\"\n *\n * getBaseUrl(\"relative/path/file.txt\");\n * // \"relative/path/\"\n * ```\n */\nexport function getBaseUrl(url: string): string {\n // Locate the scheme separator (e.g. the \"://\" in \"https://\")\n // 定位协议分隔符(如 \"https://\" 中的 \"://\")\n const schemeEnd = url.indexOf(\"://\");\n\n if (schemeEnd === -1) {\n // No scheme → treat as a relative path, find the last directory separator\n // 无协议 → 视为相对路径,找到最后一个目录分隔符\n const lastSlash = url.lastIndexOf(\"/\");\n if (lastSlash === -1) return url + \"/\";\n return url.substring(0, lastSlash + 1);\n }\n\n // Find the last path separator (directory of the resource)\n // 找到最后一个路径分隔符(资源所在的目录)\n // \"https://example.com/hls/playlist.m3u8\" → lastSlash at index 30 → \"https://example.com/hls/\"\n const lastSlash = url.lastIndexOf(\"/\");\n // If the last slash is at or before the scheme end + 3 (e.g. \"https://\" = 8 chars),\n // we're at the root (e.g. \"https://example.com\") → append \"/\"\n // 如果最后一个斜杠在协议结束位置之前或等于协议结束位置 + 3(如 \"https://\" 共 8 个字符),\n // 说明是根路径(如 \"https://example.com\")→ 追加 \"/\"\n if (lastSlash <= schemeEnd + 2) return url + \"/\";\n return url.substring(0, lastSlash + 1);\n}\n\n/**\n * Build a query string from a record of key-value pairs (without the leading `?`).\n *\n * Keys and values are percent-encoded via `encodeURIComponent`. Entries with\n * `undefined` or `null` values are silently skipped.\n *\n * 从键值对记录构建查询字符串(不含开头的 `?`)。\n *\n * 键和值通过 `encodeURIComponent` 进行百分号编码。值为 `undefined` 或 `null`\n * 的条目将被静默跳过。\n *\n * @param params - A record of query parameters to serialize\n * 待序列化的查询参数记录\n * @returns The encoded query string (e.g. `\"key1=val1&key2=val2\"`)\n * 编码后的查询字符串\n *\n * @example\n * ```ts\n * buildQueryString({ a: 1, b: \"hello world\", c: true });\n * // \"a=1&b=hello%20world&c=true\"\n *\n * buildQueryString({ x: undefined, y: null, z: \"valid\" });\n * // \"z=valid\" — undefined/null values are skipped\n * ```\n */\nexport function buildQueryString(params: Record<string, string | number | boolean>): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(params)) {\n // Skip entries with undefined or null values\n // 跳过值为 undefined 或 null 的条目\n if (value !== undefined && value !== null) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n }\n }\n return parts.join(\"&\");\n}\n\n/**\n * Append query parameters to a URL, intelligently choosing `?` or `&` as the\n * separator depending on whether the URL already contains a query string.\n *\n * 将查询参数追加到 URL,根据 URL 是否已包含查询字符串,智能选择 `?` 或 `&` 作为分隔符。\n *\n * @param url - The base URL to append parameters to\n * 待追加参数的基础 URL\n * @param params - The query parameters to append\n * 待追加的查询参数\n * @returns The URL with the appended query string, or the original URL if params are empty\n * 追加了查询字符串的 URL;如果 params 为空则返回原始 URL\n *\n * @example\n * ```ts\n * appendQueryParams(\"https://example.com/seg.ts\", { token: \"abc123\" });\n * // \"https://example.com/seg.ts?token=abc123\"\n *\n * appendQueryParams(\"https://example.com/seg.ts?session=1\", { token: \"abc123\" });\n * // \"https://example.com/seg.ts?session=1&token=abc123\"\n * ```\n */\nexport function appendQueryParams(url: string, params: Record<string, string | number | boolean>): string {\n const qs = buildQueryString(params);\n if (!qs) return url;\n // Use '&' if the URL already contains a '?', otherwise use '?' to start the query string\n // 如果 URL 已包含 '?' 则使用 '&',否则使用 '?' 来开启查询字符串\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return url + separator + qs;\n}\n\n/**\n * Parse query parameters from a URL into a plain record.\n *\n * All values are returned as decoded strings. The fragment (`#...`) is not\n * included in the parsed result (it's stripped by `indexOf(\"?\")` naturally).\n *\n * 从 URL 中解析查询参数,返回普通键值记录。\n *\n * 所有值都以解码后的字符串形式返回。URL 片段(`#...`)不会包含在解析结果中\n * (`indexOf(\"?\")` 自然会将其排除在外)。\n *\n * @param url - The URL to parse query parameters from\n * 待解析查询参数的 URL\n * @returns A record of decoded key-value pairs\n * 解码后的键值对记录\n *\n * @example\n * ```ts\n * parseQueryParams(\"https://example.com/seg.ts?token=abc&ts=123\");\n * // { token: \"abc\", ts: \"123\" }\n *\n * parseQueryParams(\"https://example.com/seg.ts\");\n * // {} — no query string\n * ```\n */\nexport function parseQueryParams(url: string): Record<string, string> {\n const params: Record<string, string> = {};\n const queryIndex = url.indexOf(\"?\");\n // No query string present → return empty record\n // 没有查询字符串 → 返回空对象\n if (queryIndex === -1) return params;\n\n // Split on '&' to get individual param pairs, then split each on first '='\n // 按 '&' 拆分得到各参数对,再按第一个 '=' 拆分\n const query = url.substring(queryIndex + 1);\n for (const part of query.split(\"&\")) {\n const eq = part.indexOf(\"=\");\n if (eq === -1) continue; // malformed pair with no '=', skip\n const key = decodeURIComponent(part.substring(0, eq));\n const value = decodeURIComponent(part.substring(eq + 1));\n params[key] = value;\n }\n return params;\n}\n\n/**\n * Normalize a URL by resolving redundant `../` and `./` segments.\n *\n * For HTTP and protocol-relative URLs, delegates to the browser's `URL` constructor\n * which handles normalization natively. For other URL-like strings, performs a\n * manual path-segment resolution (collapsing `.` and `..`).\n *\n * 通过解析多余的 `../` 和 `./` 段来规范化 URL。\n *\n * 对于 HTTP 和协议相对 URL,委托给浏览器的 `URL` 构造函数进行原生规范化处理。\n * 对于其他类 URL 字符串,执行手动路径段解析(折叠 `.` 和 `..`)。\n *\n * @param url - The URL string to normalize / 待规范化的 URL 字符串\n * @returns The normalized URL with `.`/`..` resolved / 解析了 `.`/`..` 的规范化 URL\n *\n * @example\n * ```ts\n * normalizeUrl(\"https://example.com/a/b/../c/./d.ts\");\n * // \"https://example.com/a/c/d.ts\"\n *\n * normalizeUrl(\"a/b/../c/./d.ts\");\n * // \"a/c/d.ts\"\n * ```\n */\nexport function normalizeUrl(url: string): string {\n try {\n // For HTTP/protocol-relative URLs, delegate to the native URL constructor\n // 对于 HTTP/协议相对 URL,委托给原生 URL 构造函数\n if (isHttpUrl(url) || isProtocolRelative(url)) {\n // Protocol-relative URLs need a scheme prepended for the URL constructor to parse them\n // 协议相对 URL 需要添加协议前缀才能被 URL 构造函数解析\n const resolved = new URL(isProtocolRelative(url) ? \"https:\" + url : url);\n return resolved.href;\n }\n } catch {\n // If the URL constructor throws (e.g. invalid URL), fall through to manual resolution\n // 如果 URL 构造函数抛出异常(如无效 URL),回退到手动解析\n }\n\n // Manual path-segment resolution: split on '/', process each segment\n // 手动路径段解析:按 '/' 拆分,逐段处理\n const parts = url.split(\"/\");\n const result: string[] = [];\n\n for (const part of parts) {\n if (part === \"..\") {\n // \"..\" means \"go up one directory\" — remove the last segment\n // \"..\" 表示\"向上一级目录\"—— 移除最后一个段\n result.pop();\n } else if (part !== \".\" && part !== \"\") {\n // Skip \".\" (current directory) and empty segments (from double slashes)\n // 跳过 \".\"(当前目录)和空段(来自双斜杠)\n result.push(part);\n }\n }\n\n return result.join(\"/\");\n}\n\n/**\n * Guess the segment container format from a URI's file extension.\n *\n * HLS supports two main segment formats: **MPEG-TS** (`.ts`) and **fMP4** (`.m4s`,\n * `.mp4`, `.m4v`, `.cmfv`, `.cmfa`). This function inspects the file extension\n * (stripping query strings and fragments first) to determine the likely format.\n *\n * 根据 URI 的文件扩展名推断分段容器格式。\n *\n * HLS 支持两种主要的分段格式:**MPEG-TS**(`.ts`)和 **fMP4**(`.m4s`、`.mp4`、\n * `.m4v`、`.cmfv`、`.cmfa`)。此函数检查文件扩展名(先去除查询字符串和片段)\n * 来确定可能的格式。\n *\n * @param uri - The segment URI to inspect / 待检查的分段 URI\n * @returns `\"ts\"` for MPEG-TS segments, `\"fmp4\"` for fragmented MP4 segments,\n * or `\"unknown\"` if the format cannot be determined\n * MPEG-TS 分段返回 `\"ts\"`,fMP4 分段返回 `\"fmp4\"`,无法确定时返回 `\"unknown\"`\n *\n * @example\n * ```ts\n * guessFormatFromUri(\"https://cdn.example.com/segment.ts\");\n * // \"ts\"\n *\n * guessFormatFromUri(\"https://cdn.example.com/segment.m4s?token=abc\");\n * // \"fmp4\"\n *\n * guessFormatFromUri(\"https://cdn.example.com/init.mp4\");\n * // \"fmp4\"\n *\n * guessFormatFromUri(\"https://cdn.example.com/segment.cmfv\");\n * // \"fmp4\" — Common Media File Format (CMAF) video\n *\n * guessFormatFromUri(\"https://cdn.example.com/segment.cmfa\");\n * // \"fmp4\" — CMAF audio\n *\n * guessFormatFromUri(\"https://cdn.example.com/segment.bin\");\n * // \"unknown\" — unrecognized extension\n * ```\n */\nexport function guessFormatFromUri(uri: string): \"ts\" | \"fmp4\" | \"unknown\" {\n // Strip query string and fragment before checking the extension\n // 在检查扩展名之前去除查询字符串和片段\n // \"segment.ts?token=abc#frag\" → \"segment.ts\"\n const clean = uri.split(\"?\")[0].split(\"#\")[0];\n\n // Classic MPEG Transport Stream\n // 经典 MPEG 传输流\n if (clean.endsWith(\".ts\") || clean.endsWith(\".TS\")) return \"ts\";\n\n // Fragmented MP4 / ISOBMFF segments\n // 分片 MP4 / ISOBMFF 分段\n if (clean.endsWith(\".m4s\") || clean.endsWith(\".mp4\") || clean.endsWith(\".m4v\")) return \"fmp4\";\n\n // Common Media Application Format (CMAF) — video and audio tracks\n // 通用媒体应用格式(CMAF)—— 视频和音频轨道\n if (clean.endsWith(\".cmfv\") || clean.endsWith(\".cmfa\")) return \"fmp4\";\n\n // Extension not recognized as either TS or fMP4\n // 扩展名既不是 TS 也不是 fMP4\n return \"unknown\";\n}\n\n/**\n * Strip query parameters and fragment from a URI, returning only the path portion.\n *\n * This is used to generate stable deduplication keys for segments — CDNs often\n * append varying query parameters (tokens, timestamps) to the same segment URI,\n * but the path uniquely identifies the resource.\n *\n * 从 URI 中去除查询参数和片段标识符,仅返回路径部分。\n *\n * 用于为分片生成稳定的去重键 — CDN 经常在同一分片 URI 上附加不同的查询参数\n * (令牌、时间戳),但路径唯一标识资源。\n *\n * @param uri - The URI to strip / 待处理的 URI\n * @returns The URI without query string or fragment / 不含查询字符串和片段的 URI\n *\n * @example\n * ```ts\n * stripQueryParams(\"https://cdn.example.com/seg.ts?token=abc&ts=123\");\n * // \"https://cdn.example.com/seg.ts\"\n *\n * stripQueryParams(\"https://cdn.example.com/seg.ts#frag\");\n * // \"https://cdn.example.com/seg.ts\"\n *\n * stripQueryParams(\"segment.ts\");\n * // \"segment.ts\"\n * ```\n */\nexport function stripQueryParams(uri: string): string {\n return uri.split(\"?\")[0].split(\"#\")[0];\n}\n\n/**\n * Generate a deterministic cache key for a segment URI.\n *\n * The URI is stripped of query parameters and fragments before key generation,\n * because CDNs often append varying tokens/timestamps to the same segment path.\n * This ensures that the same logical segment is deduplicated regardless of\n * query parameter differences.\n *\n * When byte-range requests are used (e.g. for low-latency HLS partial segments),\n * the same URI may deliver different content depending on the byte range. This\n * function appends the byte-range offset and length to the stripped URI to produce\n * a unique cache key that distinguishes byte-range variants of the same resource.\n *\n * 为分段 URI 生成确定性的缓存键。\n *\n * 生成键之前会去除 URI 的查询参数和片段标识符,因为 CDN 经常在同一分片路径上\n * 附加不同的令牌/时间戳。这确保了同一逻辑分片无论查询参数如何变化都能被去重。\n *\n * 当使用字节范围请求时(例如低延迟 HLS 的部分分段),同一个 URI 可能根据字节范围\n * 返回不同的内容。此函数将字节范围的偏移量和长度追加到去除参数后的 URI,生成唯一\n * 的缓存键,以区分同一资源的不同字节范围变体。\n *\n * @param uri - The segment URI / 分段 URI\n * @param byteRange - Optional byte range descriptor with `length` and optional `offset`\n * 可选的字节范围描述符,包含 `length` 和可选的 `offset`\n * @returns A cache key string. For byte-range requests, the format is\n * `\"{path}#{offset}-{length}\"`. For full-segment requests, returns the path without query.\n * 缓存键字符串。对于字节范围请求,格式为 `\"{path}#{offset}-{length}\"`。\n * 对于完整分段请求,返回不含查询参数的路径。\n *\n * @example\n * ```ts\n * // Full segment — query params stripped\n * segmentCacheKey(\"https://cdn.example.com/segment.ts?token=abc\");\n * // \"https://cdn.example.com/segment.ts\"\n *\n * // Byte-range segment — offset and length appended to stripped path\n * segmentCacheKey(\"https://cdn.example.com/segment.ts?token=abc\", { offset: 0, length: 50000 });\n * // \"https://cdn.example.com/segment.ts#0-50000\"\n *\n * // Byte-range without explicit offset — offset defaults to 0\n * segmentCacheKey(\"https://cdn.example.com/segment.ts\", { length: 50000 });\n * // \"https://cdn.example.com/segment.ts#0-50000\"\n * ```\n */\nexport function segmentCacheKey(uri: string, byteRange?: { length: number; offset?: number }): string {\n // Strip query parameters and fragments for stable deduplication\n // 去除查询参数和片段以实现稳定的去重\n const cleanUri = stripQueryParams(uri);\n\n if (byteRange) {\n // Format: \"{cleanUri}#{offset}-{length}\"\n // Default offset to 0 if not specified (beginning of the resource)\n // 格式:\"{cleanUri}#{offset}-{length}\"\n // 如果未指定偏移量,默认为 0(资源起始位置)\n return `${cleanUri}#${byteRange.offset ?? 0}-${byteRange.length}`;\n }\n // No byte range → cache by clean URI\n // 没有字节范围 → 按去除参数后的 URI 缓存\n return cleanUri;\n}\n","/**\n * Playlist Manager\n * Manages M3U8 playlist fetching, polling, and update detection.\n *\n * Polling Strategy:\n * 轮询策略:\n * - Live: Poll based on target duration (default: targetDuration / 2)\n * Live 模式: 根据 target duration 进行轮询(默认: targetDuration / 2)\n * - VOD: Fetch once, no polling needed\n * VOD 模式: 仅获取一次,无需轮询\n * - LL-HLS: Use blocking reload when supported by server\n * LL-HLS 模式: 当服务器支持时使用阻塞式重新加载\n *\n * Optimizations:\n * 优化措施:\n * - Conditional GET (ETag/If-None-Match, Last-Modified/If-Modified-Since)\n * 条件 GET 请求(ETag/If-None-Match, Last-Modified/If-Modified-Since)\n * - Skip support for delta playlists (EXT-X-SKIP)\n * 支持增量播放列表(EXT-X-SKIP)\n * - Blocking reload for LL-HLS\n * LL-HLS 阻塞式重新加载\n */\n\nimport { MediaPlaylist, MasterPlaylist, PlaylistType, Segment, HlsIOError, PartialSegment, HlsIOConfig } from \"../types\";\nimport { Fetcher } from \"../network/fetcher\";\nimport { parseM3U8, ParseResult } from \"../parser/m3u8-parser\";\nimport { resolveUri } from \"../utils/url\";\nimport { Logger } from \"@skax/logger\";\n\n/**\n * Configuration for the PlaylistManager.\n *\n * PlaylistManager 的配置项。\n */\nexport interface PlaylistManagerConfig {\n /** The URL of the M3U8 playlist */\n url: Required<HlsIOConfig>[\"url\"];\n /** The type of playlist (LIVE, VOD, EVENT) */\n playlistType: PlaylistType;\n /** Custom playlist refresh interval in ms. 0 means auto-calculate. */\n playlistRefreshInterval: number;\n /** Whether low-latency HLS mode is enabled */\n lowLatencyMode: boolean;\n /** Whether to resolve relative URIs using baseUrl */\n resolveRelativeUris: boolean;\n /** Base URL for resolving relative URIs */\n baseUrl: string;\n /** Whether to follow HTTP redirects and update the URL for future requests */\n followRedirectUrl?: HlsIOConfig[\"followRedirectUrl\"];\n /** Max retry attempts for playlist fetch (default: 5) */\n playlistRetryCount?: number;\n /** Base delay between retries in ms (default: 1000) */\n playlistRetryDelay?: number;\n /** Retry backoff multiplier (default: 2) */\n playlistRetryBackoff?: number;\n}\n\n/**\n * Callback hooks for PlaylistManager lifecycle events.\n *\n * PlaylistManager 生命周期事件的回调钩子。\n */\nexport interface PlaylistManagerCallbacks {\n /** Fired when a media playlist is fully loaded and parsed\n * 媒体播放列表加载并解析完成时触发\n */\n onPlaylistLoaded: (playlist: MediaPlaylist) => void;\n /** Fired when new segments are detected after a playlist refresh\n * 播放列表刷新后检测到新分片时触发\n */\n onSegmentsUpdated: (segments: Segment[], playlist: MediaPlaylist) => void;\n /** Fired when partial segments are available (LL-HLS)\n * LL-HLS 部分分片可用时触发\n */\n onPartialSegments: (parts: PartialSegment[]) => void;\n /** Fired when the playlist type is first detected or changes\n * 首次检测到或播放列表类型变更时触发\n */\n onPlaylistTypeDetected: (type: PlaylistType) => void;\n /** Fired when the playlist has ended (VOD/EVENT)\n * 播放列表结束时触发(VOD/EVENT 模式)\n */\n onEnded: () => void;\n /** Fired when an error occurs during playlist loading\n * 播放列表加载出错时触发\n */\n onError: (error: HlsIOError) => void;\n}\n\n/**\n * PlaylistManager orchestrates M3U8 playlist fetching, periodic polling for live\n * streams, and detection of new segments when the playlist is updated.\n *\n * PlaylistManager 负责 M3U8 播放列表的获取、直播流的周期性轮询,以及播放列表\n * 更新时新分片的检测。\n *\n * @example\n * ```typescript\n * import { PlaylistManager } from \"./playlist/playlist-manager\";\n * import { Fetcher } from \"./network/fetcher\";\n * import { PlaylistType } from \"./types\";\n *\n * const fetcher = new Fetcher();\n * const config = {\n * url: \"https://example.com/live.m3u8\",\n * playlistType: PlaylistType.LIVE,\n * playlistRefreshInterval: 0, // auto-calculate\n * // 自动计算\n * lowLatencyMode: false,\n * resolveRelativeUris: true,\n * baseUrl: \"https://example.com/\",\n * };\n *\n * const callbacks = {\n * onPlaylistLoaded: (pl) => console.log(\"Playlist loaded | 播放列表已加载\", pl),\n * onSegmentsUpdated: (segs) => console.log(\"New segments | 新分片\", segs),\n * onPartialSegments: (parts) => {},\n * onPlaylistTypeDetected: (type) => console.log(\"Type | 类型:\", type),\n * onEnded: () => console.log(\"Playlist ended | 播放列表已结束\"),\n * onError: (err) => console.error(\"Error | 错误:\", err),\n * };\n *\n * const manager = new PlaylistManager(fetcher, config, callbacks, true);\n *\n * // Start loading the playlist\n * // 开始加载播放列表\n * await manager.start();\n *\n * // Later, force a refresh\n * // 稍后,强制刷新\n * await manager.refresh();\n *\n * // When done, destroy the manager\n * // 完成后销毁 manager\n * manager.destroy();\n * ```\n */\nexport class PlaylistManager {\n private _fetcher: Fetcher;\n private _config: PlaylistManagerConfig;\n private _logger: Logger;\n private _callbacks: PlaylistManagerCallbacks;\n /** Timer handle for periodic playlist polling\n * 周期性播放列表轮询的定时器句柄\n */\n private _pollTimer: ReturnType<typeof setInterval> | null = null;\n /** Whether the manager has been destroyed\n * manager 是否已被销毁\n */\n private _isDestroyed = false;\n /** The most recently parsed media playlist\n * 最近一次解析的媒体播放列表\n */\n private _lastPlaylist: MediaPlaylist | null = null;\n /**\n * ETag value from the last response, used for conditional GET.\n * ETag 值来自上次响应,用于条件 GET 请求。\n * When the server returns 304 Not Modified, we skip re-parsing and save bandwidth.\n * 当服务器返回 304 Not Modified 时,跳过重新解析以节省带宽。\n */\n private _lastETag: string | null = null;\n /**\n * Last-Modified header from the last response, used for conditional GET.\n * Last-Modified 头来自上次响应,用于条件 GET 请求。\n */\n private _lastModified: string | null = null;\n /** Monotonically increasing counter for segment update batches\n * 分片更新批次的单调递增计数器\n */\n private _segmentUpdateSeq: number = 0;\n /** Total number of poll cycles executed\n * 已执行的轮询周期总数\n */\n private _pollCount = 0;\n /** Whether a poll-triggered playlist request is in progress\n * 轮询触发的播放列表请求是否正在进行中\n */\n private _isPollRequestInFlight = false;\n /** RFC 8216 §6.3.4: tracks if last poll found new segments */\n private _lastPollHadChanges = false;\n /**\n * The initial URL from config — never changes after construction.\n * Used as fallback when the current URL (possibly redirected) fails.\n * 来自配置的初始 URL — 构造后不再改变。\n * 当当前 URL(可能已重定向)失败时用作回退。\n */\n private _originalUrl: string;\n\n /**\n * The current working URL for fetching — may be updated by redirects or variant selection.\n * 当前用于获取的工作 URL — 可能被重定向或变体选择更新。\n */\n private _playlistUrl: string;\n\n /**\n * Public: the resolved media playlist URL (after master → variant selection).\n * 公开字段:解析后的媒体播放列表 URL(经过 master → variant 选择后)。\n */\n playlistUrl: string;\n\n /**\n * Create a new PlaylistManager.\n * 创建一个新的 PlaylistManager。\n *\n * @param fetcher - Network fetcher instance\n * 网络请求器实例\n * @param config - Playlist manager configuration\n * 播放列表管理器配置\n * @param callbacks - Lifecycle callback hooks\n * 生命周期回调钩子\n * @param debug - Enable debug logging (default: false)\n * 启用调试日志(默认: false)\n */\n constructor(fetcher: Fetcher, config: PlaylistManagerConfig, callbacks: PlaylistManagerCallbacks, debug = false) {\n this._fetcher = fetcher;\n this._config = config;\n this._logger = new Logger(\"HLS-IO:Playlist\", debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n this._callbacks = callbacks;\n this._originalUrl = config.url;\n this._playlistUrl = config.url;\n this.playlistUrl = config.url;\n }\n\n /**\n * Start loading the playlist.\n * Handles both master playlists (auto-selects the first variant) and media playlists.\n *\n * 开始加载播放列表。\n * 同时处理主播放列表(自动选择第一个变体)和媒体播放列表。\n *\n * @returns The parsed playlist (media or master)\n * 解析后的播放列表(媒体或主播放列表)\n *\n * @example\n * ```typescript\n * // Start loads the playlist and kicks off polling for live streams\n * // start 加载播放列表并为直播流启动轮询\n * const playlist = await manager.start();\n * if (playlist.type === \"media\") {\n * console.log(`Loaded ${playlist.segments.length} segments | 已加载 ${playlist.segments.length} 个分片`);\n * }\n * ```\n */\n async start(): Promise<MediaPlaylist | MasterPlaylist> {\n this._logger.info(`Loading playlist: ${this._playlistUrl}`);\n const result = await this._loadPlaylist();\n\n if (result.type === \"master\") {\n // Master playlist - for now, auto-select the first variant\n // 主播放列表 - 目前自动选择第一个变体\n // In a full implementation, this would involve ABR logic\n // 完整实现将涉及 ABR(自适应码率)逻辑\n this._logger.info(\"Master playlist detected, selecting first variant\");\n if (result.playlist.variants.length > 0) {\n const variant = result.playlist.variants[0];\n const variantUrl = this._config.resolveRelativeUris ? resolveUri(variant.uri, this._config.baseUrl || this._playlistUrl) : variant.uri;\n\n // Recurse with the variant URL\n // 使用变体 URL 递归加载\n this._playlistUrl = variantUrl;\n this.playlistUrl = variantUrl;\n try {\n const mediaResult = await this._loadPlaylist();\n if (mediaResult.type === \"media\") {\n this._handleMediaPlaylist(mediaResult.playlist);\n return mediaResult.playlist;\n }\n } finally {\n //\n }\n }\n return result.playlist;\n }\n\n this._handleMediaPlaylist(result.playlist);\n return result.playlist;\n }\n\n /**\n * Stop polling and release all resources.\n * After calling destroy(), the instance should not be used again.\n *\n * 停止轮询并释放所有资源。\n * 调用 destroy() 后,不应再使用该实例。\n *\n * @example\n * ```typescript\n * // When playback stops or user navigates away\n * // 当播放停止或用户离开时\n * manager.destroy();\n * ```\n */\n destroy(): void {\n this._isDestroyed = true;\n this._stopPolling();\n this._lastPlaylist = null;\n }\n\n /**\n * Force an immediate playlist refresh, bypassing the normal polling schedule.\n * Useful for scenarios like seek-to-live or recovering from a stall.\n *\n * 强制立即刷新播放列表,绕过正常的轮询计划。\n * 适用于跳转到直播边缘或从卡顿中恢复等场景。\n *\n * @returns The refreshed media playlist, or null on failure\n * 刷新后的媒体播放列表,失败时为 null\n *\n * @example\n * ```typescript\n * // Force refresh after recovering from a network stall\n * // 从网络卡顿恢复后强制刷新\n * const refreshed = await manager.refresh();\n * if (refreshed) {\n * console.log(`Now have ${refreshed.segments.length} segments | 现在有 ${refreshed.segments.length} 个分片`);\n * }\n * ```\n */\n async refresh(): Promise<MediaPlaylist | null> {\n try {\n const result = await this._loadPlaylist();\n if (result.type === \"media\") {\n this._handleMediaPlaylist(result.playlist);\n return result.playlist;\n }\n } catch (error) {\n this._logger.error(`Refresh failed: ${(error as Error).message}`);\n }\n return null;\n }\n\n /**\n * Get the last successfully parsed media playlist.\n *\n * 获取最近一次成功解析的媒体播放列表。\n *\n * @returns The last media playlist, or null if none loaded yet\n * 最近的媒体播放列表,如果尚未加载则为 null\n */\n getLastPlaylist(): MediaPlaylist | null {\n return this._lastPlaylist;\n }\n\n // ==================== Private Methods ====================\n // ==================== 私有方法 ====================\n\n /**\n * Load a playlist from the network.\n *\n * Implements conditional GET using ETag (If-None-Match) and Last-Modified\n * (If-Modified-Since) headers. When the server responds with 304 Not Modified,\n * we avoid re-parsing the playlist body, saving CPU and bandwidth.\n *\n * 从网络加载播放列表。\n *\n * 使用 ETag(If-None-Match)和 Last-Modified(If-Modified-Since)头实现条件 GET。\n * 当服务器返回 304 Not Modified 时,避免重新解析播放列表正文,节省 CPU 和带宽。\n *\n * @returns Parsed playlist result\n * 解析后的播放列表结果\n */\n private async _loadPlaylist(): Promise<ParseResult> {\n try {\n const headers: Record<string, string> = {};\n\n if (this._lastETag) {\n headers[\"If-None-Match\"] = this._lastETag;\n }\n if (this._lastModified) {\n headers[\"If-Modified-Since\"] = this._lastModified;\n }\n\n const result = await this._fetcher.fetch(this._playlistUrl, { headers });\n\n // Capture redirect\n if (this._config.followRedirectUrl && result?.url !== this._playlistUrl) {\n this._logger.info(`Playlist URL redirected: ${this._playlistUrl} → ${result.url}`);\n this._playlistUrl = result.url;\n }\n\n const text = new TextDecoder().decode(result.data);\n const baseUrl = this._config.resolveRelativeUris ? this._config.baseUrl || this._playlistUrl : undefined;\n const parsed = parseM3U8(text, baseUrl);\n\n return parsed;\n } catch (error) {\n const lastError = error as Error;\n this._logger.warn(`Playlist fetch failed: ${lastError.message}`);\n\n if (this._playlistUrl !== this._originalUrl) {\n this._logger.info(`Resetting URL to original: ${this._originalUrl}`);\n this._playlistUrl = this._originalUrl;\n }\n\n const err: HlsIOError = {\n type: \"NETWORK\",\n subType: \"M3U8\",\n message: `Failed to load playlist: ${lastError.message}`,\n originalError: lastError,\n playlistUrl: this._playlistUrl,\n };\n this._callbacks.onError(err);\n\n // Circuit-breaker\n // if (this._consecutiveErrors >= this._maxConsecutiveErrors) {\n // this._logger.error(\"Max consecutive playlist load errors reached, stopping poll\");\n // }\n\n this._stopPolling();\n\n throw lastError;\n }\n }\n\n /**\n * Handle a freshly parsed media playlist.\n *\n * This is the core orchestration method that:\n * 1. Detects playlist type changes (LIVE ↔ VOD)\n * 2. Finds new segments by comparing with the previous playlist\n * 3. Starts or restarts the polling timer for live streams\n * 4. Fires callbacks for downstream consumers\n *\n * 处理新解析的媒体播放列表。\n *\n * 这是核心编排方法,负责:\n * 1. 检测播放列表类型变更(LIVE ↔ VOD)\n * 2. 通过与上一个播放列表比较来发现新分片\n * 3. 为直播流启动或重启轮询定时器\n * 4. 触发下游消费者的回调\n *\n * @param playlist - The parsed media playlist\n * 解析后的媒体播放列表\n */\n private _handleMediaPlaylist(playlist: MediaPlaylist): void {\n const isFirst = !this._lastPlaylist;\n\n // Notify playlist type if detected\n // 检测到播放列表类型时通知\n if (isFirst || this._lastPlaylist?.type !== playlist.type) {\n this._callbacks.onPlaylistTypeDetected(playlist.type);\n }\n\n // Check for new segments: compare old vs new segment lists by sequence number\n // 检测新分片:按序列号比较旧分片列表和新分片列表\n if (!isFirst && this._lastPlaylist) {\n const newSegments = this._detectNewSegments(this._lastPlaylist.segments, playlist.segments);\n this._lastPollHadChanges = newSegments.length > 0;\n if (this._lastPollHadChanges) {\n this._logger.info(`Detected ${newSegments.length} new segments`);\n this._segmentUpdateSeq++;\n this._callbacks.onSegmentsUpdated(newSegments, playlist);\n }\n\n // Partial segments (LL-HLS): forwarded immediately for low-latency playback\n // 部分分片(LL-HLS):立即转发以实现低延迟播放\n if (playlist.partialSegments && playlist.partialSegments.length > 0) {\n this._callbacks.onPartialSegments(playlist.partialSegments);\n }\n }\n\n // Store last playlist\n // 存储最新的播放列表\n this._lastPlaylist = playlist;\n\n // Emit playlist loaded\n // 触发播放列表已加载事件\n this._callbacks.onPlaylistLoaded(playlist);\n\n // Start/restart polling if live\n // 如果是直播,启动/重启轮询\n // Live streams continuously produce new segments, so we must keep polling.\n // 直播流不断产生新分片,因此必须持续轮询。\n if (playlist.type === PlaylistType.LIVE && !this._isDestroyed) {\n this._schedulePoll(playlist);\n }\n\n // VOD - check if ended\n // VOD 模式 - 检查是否已结束\n // VOD playlists are static; once loaded, emit the 'ended' signal.\n // VOD 播放列表是静态的;加载完成后触发 'ended' 信号。\n if (playlist.type === PlaylistType.VOD) {\n this._callbacks.onEnded();\n }\n }\n\n /**\n * Detect new segments by comparing old and new segment arrays.\n *\n * New segment detection strategy:\n * 新分片检测策略:\n * - Each segment has a unique media sequence number (EXT-X-MEDIA-SEQUENCE).\n * 每个分片有一个唯一的媒体序列号(EXT-X-MEDIA-SEQUENCE)。\n * - We build a Set of old sequence numbers and filter new segments not in that set.\n * 构建旧序列号的集合,过滤出不在该集合中的新分片。\n * - This is O(n + m) and handles both appended segments (live) and complete playlist reloads.\n * 时间复杂度 O(n + m),同时处理追加分片(直播)和完整播放列表重载。\n *\n * @param oldSegments - Previously known segments\n * 之前已知的分片\n * @param newSegments - Newly parsed segments\n * 新解析的分片\n * @returns Segments that are new (not present in oldSegments)\n * 新增的分片(不在 oldSegments 中)\n */\n private _detectNewSegments(oldSegments: Segment[], newSegments: Segment[]): Segment[] {\n const oldSeqSet = new Set(oldSegments.map((s) => s.sequence));\n return newSegments.filter((s) => !oldSeqSet.has(s.sequence));\n }\n\n /**\n * Schedule the next playlist poll based on playlist characteristics.\n *\n * Polling strategy:\n * 轮询策略:\n * - If a custom refresh interval is configured, use it directly.\n * 如果配置了自定义刷新间隔,直接使用。\n * - Otherwise, calculate based on targetDuration:\n * 否则,基于 targetDuration 计算:\n * - Standard HLS recommendation: poll at targetDuration / 2.\n * 标准 HLS 建议:以 targetDuration / 2 的间隔轮询。\n * - Minimum interval: 250ms (to avoid over-polling).\n * 最小间隔:250ms(避免过度轮询)。\n * - Maximum interval: 2 × targetDuration (to avoid missing segments).\n * 最大间隔:2 × targetDuration(避免错过分片)。\n * - For very short segments (targetDuration ≤ 2s), poll at the minimum 250ms.\n * 对于极短分片(targetDuration ≤ 2s),以最小间隔 250ms 轮询。\n * - For LL-HLS with blocking reload, a different approach is used.\n * 对于支持阻塞式重新加载的 LL-HLS,使用不同的方式。\n *\n * @param playlist - The current media playlist\n * 当前的媒体播放列表\n */\n private _schedulePoll(playlist: MediaPlaylist): void {\n this._stopPolling();\n\n let interval = this._config.playlistRefreshInterval;\n\n if (!Number.isFinite(interval) || interval <= 0) {\n // RFC 8216 §6.3.4: after a change, wait >= targetDuration;\n // after no change, wait >= targetDuration/2.\n // RFC 8216 §6.3.4: 变更后等待>=targetDuration; 无变更等待>=targetDuration/2。\n const baseInterval = this._lastPollHadChanges ? playlist.targetDuration * 1000 : playlist.targetDuration * 500;\n interval = Math.max(250, Math.min(baseInterval, playlist.targetDuration * 2000));\n\n // For very short segments, poll more aggressively\n if (playlist.targetDuration <= 2) {\n interval = this._lastPollHadChanges ? Math.max(250, playlist.targetDuration * 1000) : Math.max(250, playlist.targetDuration * 500);\n }\n }\n\n this._logger.debug(`Polling every ${interval}ms (targetDuration=${playlist.targetDuration})`);\n\n this._pollTimer = setInterval(async () => {\n if (this._isDestroyed) return;\n\n // Keep LIVE m3u8 polling strictly serial: do not start a new request\n // while the previous poll request is still in flight.\n if (this._isPollRequestInFlight) {\n this._logger.debug(\"Skip poll tick: previous playlist request still in flight\");\n return;\n }\n\n this._pollCount++;\n this._isPollRequestInFlight = true;\n\n try {\n const result = await this._loadPlaylist();\n if (result.type === \"media\") {\n this._handleMediaPlaylist(result.playlist);\n\n // Adaptive polling: adjust interval based on changes\n // 自适应轮询:根据变化情况调整间隔\n // Idea: if no new segments are detected over several polls, the interval\n // could be increased. Conversely, rapid changes warrant a shorter interval.\n // 思路:如果在多次轮询中没有检测到新分片,可以增加间隔。反之,快速变化则需要缩短间隔。\n // if (this._pollCount > 3 && this._consecutiveErrors === 0) {\n // // If no changes for a while, we could decrease polling frequency\n // // But for now we keep it consistent\n // // 如果一段时间没有变化,可以降低轮询频率\n // // 但目前保持一致\n // }\n }\n } catch {\n // Error already reported in loadPlaylist\n // 错误已在 loadPlaylist 中上报\n } finally {\n this._isPollRequestInFlight = false;\n }\n }, interval);\n }\n\n /**\n * Stop the polling timer, if active.\n *\n * 停止轮询定时器(如果正在运行)。\n */\n private _stopPolling(): void {\n if (this._pollTimer) {\n clearInterval(this._pollTimer);\n this._pollTimer = null;\n }\n }\n}\n","/**\n * Segment Cache\n * Provides caching strategies (LRU and FIFO) for VOD segment data.\n * In live mode, old segments are evicted automatically as the window slides.\n *\n * Segment 缓存\n * 为 VOD 分片数据提供缓存策略(LRU 和 FIFO)。\n * 在直播模式下,随着窗口滑动,旧分片会被自动驱逐。\n *\n * LRU (Least Recently Used):\n * LRU(最近最少使用):\n * - Tracks access order; evicts the least recently accessed entry when at capacity.\n * 跟踪访问顺序;容量满时驱逐最久未访问的条目。\n * - Best for VOD with seeking, where recently accessed segments are more likely to be re-accessed.\n * 最适合支持 Seek 的 VOD 场景,最近访问的分片更可能被再次访问。\n *\n * FIFO (First-In First-Out):\n * FIFO(先进先出):\n * - Evicts the oldest-inserted entry, regardless of access patterns.\n * 驱逐最早插入的条目,不考虑访问模式。\n * - Best for linear playback without seeking, where older segments won't be revisited.\n * 最适合无 Seek 的线性播放场景,旧分片不会被再次访问。\n */\n\nimport { Segment, CacheEntry, CacheStats } from \"../types\";\nimport { segmentCacheKey } from \"../utils/url\";\n\n/**\n * An in-memory segment cache with configurable eviction policy (LRU or FIFO).\n *\n * 基于内存的分片缓存,可配置驱逐策略(LRU 或 FIFO)。\n *\n * @example\n * ```typescript\n * import { SegmentCache } from \"./segment/cache\";\n *\n * // Create an LRU cache with max 50 entries\n * // 创建一个最多 50 个条目的 LRU 缓存\n * const cache = new SegmentCache(50, \"lru\");\n *\n * // Store a segment\n * // 存储一个分片\n * cache.set(segment, arrayBufferData);\n *\n * // Retrieve a segment\n * // 获取一个分片\n * const data = cache.get(segment);\n * if (data) {\n * // Use the cached data\n * // 使用缓存数据\n * }\n *\n * // Evict segments before a given sequence number (live sliding window)\n * // 驱逐指定序列号之前的分片(直播滑动窗口)\n * const removed = cache.evictBeforeSequence(150);\n * console.log(`Evicted ${removed} old segments | 已驱逐 ${removed} 个旧分片`);\n *\n * // Check cache stats\n * // 查看缓存统计\n * const stats = cache.getStats();\n * console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}, Evictions: ${stats.evictions}`);\n * ```\n */\nexport class SegmentCache {\n /** Internal storage mapping cache keys to entries */\n /** 内部存储,将缓存键映射到条目 */\n private _cache: Map<string, CacheEntry>;\n /**\n * Ordered list of keys representing access/insertion order.\n * 表示访问/插入顺序的有序键列表。\n * - LRU mode: index 0 = least recently used, last index = most recently used\n * LRU 模式: 索引 0 = 最久未使用, 最后一个索引 = 最近使用\n * - FIFO mode: index 0 = first inserted, last index = most recently inserted\n * FIFO 模式: 索引 0 = 最早插入, 最后一个索引 = 最近插入\n */\n private _accessOrder: string[];\n /** Maximum number of entries before eviction kicks in */\n /** 触发驱逐前的最大条目数 */\n private _maxSize: number;\n /** Eviction policy: \"lru\" or \"fifo\" */\n /** 驱逐策略: \"lru\" 或 \"fifo\" */\n private _policy: \"lru\" | \"fifo\";\n /** Runtime statistics for cache performance monitoring */\n /** 用于缓存性能监控的运行时统计 */\n private _stats: CacheStats;\n\n /**\n * Create a new SegmentCache.\n * 创建新的 SegmentCache。\n *\n * @param maxSize - Maximum number of cached segments (default: 50)\n * @param maxSize - 最大缓存分片数(默认: 50)\n * @param policy - Eviction policy: \"lru\" (default) or \"fifo\"\n * @param policy - 驱逐策略: \"lru\"(默认)或 \"fifo\"\n */\n constructor(maxSize = 50, policy: \"lru\" | \"fifo\" = \"lru\") {\n this._cache = new Map();\n this._accessOrder = [];\n this._maxSize = Number.isFinite(maxSize) ? Math.max(1, Math.floor(maxSize)) : 50;\n this._policy = policy;\n this._stats = { size: 0, hits: 0, misses: 0, evictions: 0 };\n }\n\n /**\n * Store a segment's data in the cache.\n * If the segment already exists, its data and timestamp are updated\n * and it is moved to the end of the access order (most recently used).\n * If the cache is at capacity, the oldest entry is evicted before insertion.\n *\n * 将分片数据存储到缓存中。\n * 如果分片已存在,则更新其数据和时间戳,并将其移至访问顺序末尾(最近使用)。\n * 如果缓存已满,则在插入前驱逐最旧的条目。\n *\n * @param segment - The segment metadata\n * @param segment - 分片元数据\n * @param data - The raw segment data (possibly decrypted)\n * @param data - 原始分片数据(可能已解密)\n *\n * @example\n * ```typescript\n * const cache = new SegmentCache(10, \"lru\");\n *\n * // Cache a fetched segment\n * // 缓存已获取的分片\n * cache.set(segment, segmentData);\n *\n * // Updating an existing segment refreshes its LRU position\n * // 更新已有分片会刷新其 LRU 位置\n * cache.set(segment, newData); // moved to MRU position\n * // 移至 MRU 位置\n * ```\n */\n set(segment: Segment, data: ArrayBuffer): void {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n\n if (this._cache.has(key)) {\n // Update existing entry\n // 更新现有条目\n const entry = this._cache.get(key)!;\n entry.data = data;\n entry.segment = segment;\n entry.timestamp = Date.now();\n this._updateAccess(key);\n return;\n }\n\n // Evict if at capacity — ensure room before inserting\n // 容量已满时驱逐 — 确保插入前有空间\n if (this._cache.size >= this._maxSize) {\n this._evict();\n }\n\n const entry: CacheEntry = {\n segment,\n data,\n timestamp: Date.now(),\n key,\n };\n\n this._cache.set(key, entry);\n this._accessOrder.push(key);\n this._stats.size = this._cache.size;\n }\n\n /**\n * Retrieve a segment's cached data by segment metadata.\n * Returns null if the segment is not in the cache (cache miss).\n * On a hit, the segment is moved to the MRU position (for LRU tracking).\n *\n * 通过分片元数据检索缓存的分片数据。\n * 如果分片不在缓存中(缓存未命中),则返回 null。\n * 命中时,将分片移至 MRU 位置(用于 LRU 追踪)。\n *\n * @param segment - The segment to look up\n * @param segment - 要查找的分片\n * @returns The cached ArrayBuffer, or null if not found\n * @returns 缓存的 ArrayBuffer,未找到则为 null\n *\n * @example\n * ```typescript\n * const data = cache.get(segment);\n * if (data) {\n * // Cache hit — use data directly\n * // 缓存命中 — 直接使用数据\n * appendToBuffer(data);\n * } else {\n * // Cache miss — fetch from network\n * // 缓存未命中 — 从网络获取\n * await fetchAndCache(segment);\n * }\n * ```\n */\n get(segment: Segment): ArrayBuffer | null {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n const entry = this._cache.get(key);\n\n if (entry) {\n this._stats.hits++;\n // LRU: move to MRU end so it's evicted last\n // FIFO: do NOT update order — eviction is purely by insertion time\n if (this._policy === \"lru\") {\n this._updateAccess(key);\n }\n return entry.data;\n }\n\n this._stats.misses++;\n return null;\n }\n\n /**\n * Check whether a segment exists in the cache without updating access order.\n *\n * 检查分片是否存在于缓存中,不更新访问顺序。\n *\n * @param segment - The segment to check\n * @param segment - 要检查的分片\n * @returns true if the segment is cached\n * @returns 如果分片已缓存则返回 true\n */\n has(segment: Segment): boolean {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n return this._cache.has(key);\n }\n\n /**\n * Remove a specific segment from the cache.\n *\n * 从缓存中移除指定的分片。\n *\n * @param segment - The segment to delete\n * @param segment - 要删除的分片\n * @returns true if the segment was found and removed\n * @returns 如果分片被找到并移除则返回 true\n */\n delete(segment: Segment): boolean {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n const deleted = this._cache.delete(key);\n if (deleted) {\n this._accessOrder = this._accessOrder.filter((k) => k !== key);\n this._stats.size = this._cache.size;\n }\n return deleted;\n }\n\n /**\n * Remove all entries from the cache.\n *\n * 清除缓存中的所有条目。\n */\n clear(): void {\n this._cache.clear();\n this._accessOrder = [];\n this._stats.size = 0;\n }\n\n /**\n * Get a snapshot of the current cache statistics.\n *\n * 获取当前缓存统计的快照。\n *\n * @returns A copy of the cache statistics object\n * @returns 缓存统计对象的副本\n */\n getStats(): CacheStats {\n return { ...this._stats };\n }\n\n /**\n * Get the current number of cached entries.\n *\n * 获取当前缓存的条目数。\n */\n get size(): number {\n return this._cache.size;\n }\n\n /**\n * Get all cached keys in access/insertion order.\n *\n * 获取所有缓存键,按访问/插入顺序排列。\n */\n get keys(): string[] {\n return [...this._accessOrder];\n }\n\n /**\n * Evict all segments whose sequence number is less than the given value.\n *\n * This is the primary mechanism for implementing the live sliding window:\n * as the playlist advances its media sequence, segments older than the window\n * are purged from the cache to free memory.\n *\n * 驱逐所有序列号小于给定值的分片。\n *\n * 这是实现直播滑动窗口的主要机制:\n * 随着播放列表媒体序列号的推进,窗口之外的旧分片将被清除以释放内存。\n *\n * In live mode, the playlist periodically removes old segments and increments\n * EXT-X-MEDIA-SEQUENCE. This method should be called whenever the playlist's\n * starting sequence advances.\n * 在直播模式下,播放列表会定期移除旧分片并递增 EXT-X-MEDIA-SEQUENCE。\n * 每当播放列表的起始序列号推进时,都应调用此方法。\n *\n * In VOD mode, this can be used to evict already-played segments to keep\n * memory usage bounded.\n * 在 VOD 模式下,可用于驱逐已播放的分片以控制内存使用。\n *\n * @param sequence - Evict all segments with sequence < this value\n * @param sequence - 驱逐所有序列号小于此值的分片\n * @returns The number of segments evicted\n * @returns 被驱逐的分片数量\n *\n * @example\n * ```typescript\n * // Live stream: playlist starting sequence advanced from 100 to 105\n * // 直播流:播放列表起始序列号从 100 推进到 105\n * // Evict segments 0-104 that are no longer in the window\n * // 驱逐不再在窗口内的分片 0-104\n * const removed = cache.evictBeforeSequence(105);\n * console.log(`Evicted ${removed} segments before sequence 105 | 驱逐了序列 105 之前的 ${removed} 个分片`);\n *\n * // VOD: evict segments already played\n * // VOD:驱逐已播放的分片\n * cache.evictBeforeSequence(currentPlaybackSequence - 5);\n * ```\n */\n evictBeforeSequence(sequence: number): number {\n let removed = 0;\n for (const [key, entry] of this._cache) {\n if (entry.segment.sequence < sequence) {\n this._cache.delete(key);\n removed++;\n }\n }\n // Rebuild access order to remove stale keys\n // 重建访问顺序以移除过期键\n this._accessOrder = this._accessOrder.filter((key) => this._cache.has(key));\n this._stats.size = this._cache.size;\n this._stats.evictions += removed;\n return removed;\n }\n\n /**\n * Get all cached segments sorted by sequence number in ascending order.\n * Useful for inspecting the current buffer state.\n *\n * 获取所有缓存的分片,按序列号升序排列。\n * 用于检查当前缓冲区状态。\n *\n * @returns Sorted array of cached segments\n * @returns 按序列号排序的缓存分片数组\n */\n getSegmentsInOrder(): Segment[] {\n return [...this._cache.values()].map((entry) => entry.segment).sort((a, b) => a.sequence - b.sequence);\n }\n\n /**\n * Evict the least recently used (LRU) or first-in (FIFO) entry.\n * Uses Map iteration order: the first key in the Map is the oldest/LRU entry.\n *\n * 驱逐最近最少使用(LRU)或最先进入(FIFO)的条目。\n * 使用 Map 迭代顺序:Map 中的第一个键是最旧/LRU 条目。\n */\n private _evict(): void {\n if (this._cache.size === 0) return;\n\n // Map preserves insertion order; first key is the LRU/FIFO candidate\n // Map 保持插入顺序;第一个键是 LRU/FIFO 候选\n const firstKey = this._cache.keys().next().value;\n if (firstKey !== undefined) {\n this._cache.delete(firstKey);\n this._accessOrder = this._accessOrder.filter((k) => k !== firstKey);\n this._stats.evictions++;\n this._stats.size = this._cache.size;\n }\n }\n\n /**\n * Update the access order for LRU tracking.\n * Uses Map delete+set to move the key to the end in O(1) amortized time.\n *\n * 更新访问顺序以进行 LRU 追踪。\n * 使用 Map 的 delete+set 将键移至末尾,摊销时间复杂度 O(1)。\n *\n * @param key - The cache key to mark as most recently used\n * @param key - 要标记为最近使用的缓存键\n */\n private _updateAccess(key: string): void {\n if (this._policy === \"lru\") {\n // Remove and re-insert to move to end of Map iteration order\n // 删除并重新插入以移至 Map 迭代顺序的末尾\n const entry = this._cache.get(key);\n if (entry) {\n this._cache.delete(key);\n this._cache.set(key, entry);\n }\n }\n }\n}\n\n/**\n * Create a prefetch-aware cache manager for VOD content.\n * This implements a look-ahead strategy where upcoming segments\n * are prefetched based on playback position.\n *\n * 为 VOD 内容创建具有预取感知的缓存管理器。\n * 实现了一种前瞻策略,根据播放位置预取即将到来的分片。\n */\nexport interface PrefetchStrategy {\n /**\n * Get the next segments to prefetch.\n * 获取接下来要预取的分片。\n *\n * @param currentSequence - The current playback sequence number\n * @param currentSequence - 当前播放的序列号\n * @param allSegments - All known segments from the playlist\n * @param allSegments - 播放列表中所有已知分片\n * @param bufferAhead - How many segments ahead to prefetch\n * @param bufferAhead - 向前预取多少个分片\n * @returns The segments that should be prefetched next\n * @returns 接下来应预取的分片列表\n */\n getNextSegments(currentSequence: number, allSegments: Segment[], bufferAhead: number): Segment[];\n}\n\n/**\n * Look-ahead prefetch strategy: sorts all segments by sequence, finds the\n * segment after the current position, then takes the next `bufferAhead` segments.\n *\n * 前瞻预取策略:按序列号排序所有分片,找到当前位置之后的分片,\n * 然后取接下来的 `bufferAhead` 个分片。\n *\n * This strategy is robust against segments arriving out of order in the playlist,\n * because it always sorts before slicing. It is the default strategy.\n *\n * 该策略对播放列表中乱序到达的分片具有鲁棒性,因为它总是在切片前先排序。\n * 这是默认策略。\n */\nexport class LookAheadPrefetchStrategy implements PrefetchStrategy {\n /**\n * @param currentSequence - The current playback sequence\n * @param currentSequence - 当前播放序列号\n * @param allSegments - All known segments\n * @param allSegments - 所有已知分片\n * @param bufferAhead - Number of segments to prefetch\n * @param bufferAhead - 预取分片数\n * @returns Segments with sequence > currentSequence, up to bufferAhead count\n * @returns 序列号大于 currentSequence 的分片,最多 bufferAhead 个\n */\n getNextSegments(currentSequence: number, allSegments: Segment[], bufferAhead: number): Segment[] {\n const sorted = [...allSegments].sort((a, b) => a.sequence - b.sequence);\n const startIndex = sorted.findIndex((s) => s.sequence > currentSequence);\n if (startIndex === -1) return [];\n return sorted.slice(startIndex, startIndex + bufferAhead);\n }\n}\n\n/**\n * Sequential prefetch strategy: filters segments with sequence > currentSequence,\n * sorts them, then takes the first `bufferAhead` segments.\n *\n * 顺序预取策略:筛选序列号大于 currentSequence 的分片,排序后取前 `bufferAhead` 个。\n *\n * This is a simpler alternative to LookAheadPrefetchStrategy. It assumes segments\n * are roughly in order already and just needs to skip backward references.\n *\n * 这是 LookAheadPrefetchStrategy 的更简单替代方案。它假设分片已经大致有序,\n * 只需跳过向后的引用即可。\n */\nexport class SequentialPrefetchStrategy implements PrefetchStrategy {\n /**\n * @param currentSequence - The current playback sequence\n * @param currentSequence - 当前播放序列号\n * @param allSegments - All known segments\n * @param allSegments - 所有已知分片\n * @param bufferAhead - Number of segments to prefetch\n * @param bufferAhead - 预取分片数\n * @returns Up to bufferAhead segments after the current sequence\n * @returns 当前序列号之后最多 bufferAhead 个分片\n */\n getNextSegments(currentSequence: number, allSegments: Segment[], bufferAhead: number): Segment[] {\n return allSegments\n .filter((s) => s.sequence > currentSequence)\n .sort((a, b) => a.sequence - b.sequence)\n .slice(0, bufferAhead);\n }\n}\n","/**\n * Segment Manager\n * Manages segment fetching, buffering strategy, and discontinuity handling.\n *\n * Segment 管理器\n * 管理分片获取、缓冲策略和间断处理。\n *\n * Key Responsibilities:\n * 主要职责:\n * - Segment fetching with network I/O and retry logic\n * 分片获取,含网络 I/O 和重试逻辑\n * - Decryption orchestration (AES-128, SAMPLE-AES, etc.)\n * 解密编排(AES-128, SAMPLE-AES 等)\n * - Buffer management: prefetch for VOD, sliding window for live\n * 缓冲管理:VOD 预取,直播滑动窗口\n * - Discontinuity detection and notification\n * 间断检测与通知\n * - Partial segment support for LL-HLS\n * LL-HLS 部分分片支持\n *\n * Live vs VOD Segment Handling:\n * 直播 vs VOD 分片处理:\n * - Live: Fetch the most recent N segments (sliding window). Old segments\n * are evicted as the playlist's media sequence advances. The buffer always\n * stays near the live edge.\n * 直播:获取最近的 N 个分片(滑动窗口)。随着播放列表媒体序列号的推进,\n * 旧分片被驱逐。缓冲区始终保持在直播边缘附近。\n * - VOD: Prefetch segments sequentially starting from the current playback\n * position. Segments already played can optionally be evicted. All segments\n * are known upfront from the playlist.\n * VOD:从当前播放位置开始按顺序预取分片。已播放的分片可以选择性驱逐。\n * 所有分片在播放列表中都是预先已知的。\n */\n\nimport { Segment, PlaylistType, PartialSegment, HlsIOError, MapInfo } from \"../types\";\nimport { Fetcher } from \"../network/fetcher\";\nimport { SegmentCache, LookAheadPrefetchStrategy, PrefetchStrategy } from \"./cache\";\nimport { Decryptor } from \"../crypto/decrypt\";\nimport { resolveUri, guessFormatFromUri, segmentCacheKey } from \"../utils/url\";\nimport { Logger } from \"@skax/logger\";\n\n/**\n * Configuration for the SegmentManager.\n *\n * SegmentManager 的配置项。\n */\nexport interface SegmentManagerConfig {\n /** Playlist type (LIVE, VOD, EVENT) */\n /** 播放列表类型(LIVE, VOD, EVENT) */\n playlistType: PlaylistType;\n /** Number of segments to buffer ahead of the current playback position */\n /** 在当前播放位置之前缓冲的分片数 */\n bufferAhead: number;\n /** Maximum number of segments to keep in the cache */\n /** 缓存中最多保留的分片数 */\n maxCacheSize: number;\n /** Cache eviction policy: \"lru\" or \"fifo\" */\n /** 缓存驱逐策略: \"lru\" 或 \"fifo\" */\n cachePolicy: \"lru\" | \"fifo\";\n /** Whether to resolve relative segment URIs using baseUrl */\n /** 是否使用 baseUrl 解析相对分片 URI */\n resolveRelativeUris: boolean;\n /** Base URL for resolving relative segment URIs */\n /** 用于解析相对分片 URI 的基础 URL */\n baseUrl: string;\n /** Max retry attempts for segment fetch (default: 5) */\n segmentRetryCount?: number;\n /** Base delay between segment retries in ms (default: 500) */\n segmentRetryDelay?: number;\n /** Segment retry backoff multiplier (default: 2) */\n segmentRetryBackoff?: number;\n /** Segment/MAP/KEY fetch timeout in ms */\n /** 分片/MAP/KEY 获取超时(毫秒) */\n segmentTimeout?: number;\n /** Target duration from playlist (RFC 8216 §6.3.3 live start pos) */\n /** 播放列表 target duration(RFC 8216 §6.3.3 直播起始位置) */\n targetDuration?: number;\n}\n\n/**\n * Callback hooks for SegmentManager lifecycle and buffer events.\n *\n * SegmentManager 生命周期和缓冲事件的回调钩子。\n */\nexport interface SegmentManagerCallbacks {\n /** Fired when a segment has been fetched, decrypted, and cached successfully */\n /** 分片获取、解密并缓存成功后触发 */\n onSegmentReady: (segment: Segment) => void;\n /** Fired when the last segment in a VOD playlist has been loaded */\n /** VOD 播放列表的最后一个分片加载完成时触发 */\n onLastSegment: (segment: Segment) => void;\n /** Fired when a segment with a discontinuity tag is loaded */\n /** 加载带有间断标记的分片时触发 */\n onDiscontinuity: (segment: Segment) => void;\n /** Fired when a partial segment is available (LL-HLS) */\n /** LL-HLS 部分分片可用时触发 */\n onPartSegment: (part: PartialSegment) => void;\n /** Fired when EXT-X-MAP init segment is loaded */\n /** EXT-X-MAP 初始化段加载完成时触发 */\n onMapLoaded: (info: { mapUri: string; data: ArrayBuffer }) => void;\n /** Fired when the buffer falls below the configured bufferAhead threshold */\n /** 缓冲区低于配置的 bufferAhead 阈值时触发 */\n onBufferStall: (info: { buffered: number; needed: number }) => void;\n /** Fired when an error occurs during segment operations */\n /**\n * 1. 解码失败,会触发。例如 AES-128 解密失败,可能是由于网络错误导致密钥数据不完整,或者解密算法不兼容。\n * 2. 分片(segment 或 MAP)重试 `config.segmentRetryCount` 次操作出错时触发\n */\n onError: (error: HlsIOError) => void;\n /** Fired when decryption begins for a segment */\n /** 分片开始解密时触发 */\n onDecrypting: (segment: Segment) => void;\n /** Fired when decryption completes for a segment */\n /** 分片解密完成时触发 */\n onDecrypted: (segment: Segment) => void;\n}\n\n/**\n * SegmentManager orchestrates segment fetching, decryption, caching, and\n * buffer management for both live and VOD HLS streams.\n *\n * SegmentManager 为直播和 VOD HLS 流编排分片获取、解密、缓存和缓冲管理。\n *\n * It acts as the bridge between the playlist manager (which tells us *what*\n * segments exist) and the media playback pipeline (which consumes segment data).\n *\n * 它充当播放列表管理器(告诉我们存在哪些分片)和媒体播放管线(消费分片数据)\n * 之间的桥梁。\n *\n * @example\n * ```typescript\n * import { SegmentManager } from \"./segment/segment-manager\";\n * import { Fetcher } from \"./network/fetcher\";\n * import { Decryptor } from \"./crypto/decrypt\";\n * import { PlaylistType } from \"./types\";\n *\n * const fetcher = new Fetcher();\n * const decryptor = new Decryptor();\n *\n * const config = {\n * playlistType: PlaylistType.LIVE,\n * bufferAhead: 5,\n * maxCacheSize: 30,\n * cachePolicy: \"lru\" as const,\n * resolveRelativeUris: true,\n * baseUrl: \"https://example.com/\",\n * };\n *\n * const callbacks = {\n * onSegmentReady: (seg) => console.log(\"Ready | 就绪:\", seg.sequence),\n * onLastSegment: (seg) => console.log(\"Last | 最后一个:\", seg.sequence),\n * onDiscontinuity: (seg) => console.log(\"Discontinuity | 间断:\", seg.sequence),\n * onPartSegment: (part) => {},\n * onBufferStall: (info) => console.warn(\"Buffer stall | 缓冲不足:\", info),\n * onError: (err) => console.error(\"Error | 错误:\", err),\n * onDecrypting: (seg) => console.log(\"Decrypting | 解密中:\", seg.sequence),\n * onDecrypted: (seg) => console.log(\"Decrypted | 已解密:\", seg.sequence),\n * };\n *\n * const manager = new SegmentManager(fetcher, decryptor, config, callbacks, true);\n *\n * // Feed segments from playlist manager\n * // 从播放列表管理器传入分片\n * manager.updateSegments(playlist.segments);\n *\n * // Get the next segment for playback\n * // 获取下一个要播放的分片\n * const next = manager.advanceToNext();\n * if (next) {\n * const data = manager.getCachedSegment(next);\n * // feed data to media source\n * // 将数据传递给媒体源\n * }\n *\n * // Fetch a specific segment by sequence\n * // 按序列号获取特定分片\n * await manager.fetchSegment(42);\n *\n * // Cleanup\n * // 清理\n * manager.destroy();\n * ```\n */\nexport class SegmentManager {\n private _fetcher: Fetcher;\n /** Segment cache with configurable eviction policy (LRU/FIFO) */\n /** 可配置驱逐策略(LRU/FIFO)的分片缓存 */\n private _cache: SegmentCache;\n /** Decryptor for handling encrypted segments */\n /** 用于处理加密分片的解密器 */\n private _decryptor: Decryptor;\n private _config: SegmentManagerConfig;\n private _logger: Logger;\n /** Prefetch strategy: determines which segments to fetch next */\n /** 预取策略:决定接下来获取哪些分片 */\n private _prefetchStrategy: PrefetchStrategy;\n /** All known segments from the latest playlist refresh */\n /** 来自最新播放列表刷新的所有已知分片 */\n private _segments: Segment[] = [];\n /** The sequence number of the segment currently being played (or last played) */\n /** 当前正在播放(或最后播放)的分片的序列号 */\n private _currentSequence = -1;\n /**\n * Set of segment URIs currently being fetched to prevent duplicate requests.\n * 当前正在获取的分片 URI 集合,用于防止重复请求。\n */\n private _fetchQueue: Set<string> = new Set();\n /** Successfully loaded segment keys (uri + byte-range) to prevent re-fetching */\n /** 已成功加载的分片键(uri + byte-range),用于防止重复下载 */\n private _loadedSegmentKeys: Set<string> = new Set();\n /** Cached map binary payloads keyed by map cache key */\n /** 按 MAP 缓存键存储的初始化段二进制数据 */\n private _mapDataCache: Map<string, ArrayBuffer> = new Map();\n /** Maximum number of init maps to cache (prevent unbounded growth) */\n /** 最大缓存的 init map 数量(防止无限增长) */\n private readonly _MAX_MAP_CACHE = 10;\n private _callbacks: SegmentManagerCallbacks;\n /** Whether the manager has been destroyed */\n /** manager 是否已被销毁 */\n private _isDestroyed = false;\n /** Pending partial segments from LL-HLS playlists */\n /** 来自 LL-HLS 播放列表的待处理部分分片 */\n private _pendingPartials: PartialSegment[] = [];\n /** Serial fetch queue: segments waiting to be fetched one by one */\n /** 串行获取队列:等待逐个获取的分片 */\n private _pendingFetchQueue: Segment[] = [];\n /** Whether the serial fetch loop is currently running */\n /** 串行获取循环是否正在运行 */\n private _isFetchLoopRunning = false;\n\n /**\n * Create a new SegmentManager.\n * 创建一个新的 SegmentManager。\n *\n * @param fetcher - Network fetcher for segment downloads\n * - 用于分片下载的网络请求器\n * @param decryptor - Decryptor for handling encrypted segments\n * - 用于处理加密分片的解密器\n * @param config - Segment manager configuration\n * - 分片管理器配置\n * @param callbacks - Lifecycle and buffer event callbacks\n * - 生命周期和缓冲事件回调\n * @param debug - Enable debug logging (default: false)\n * - 启用调试日志(默认: false)\n */\n constructor(fetcher: Fetcher, decryptor: Decryptor, config: SegmentManagerConfig, callbacks: SegmentManagerCallbacks, debug = false) {\n const normalizedMaxCacheSize = Number.isFinite(config.maxCacheSize) ? Math.max(1, Math.floor(config.maxCacheSize)) : 50;\n const normalizedBufferAheadRaw = Number.isFinite(config.bufferAhead) ? Math.max(1, Math.floor(config.bufferAhead)) : 1;\n const normalizedBufferAhead = Math.min(normalizedBufferAheadRaw, normalizedMaxCacheSize);\n\n this._fetcher = fetcher;\n this._cache = new SegmentCache(normalizedMaxCacheSize, config.cachePolicy);\n this._decryptor = decryptor;\n this._config = {\n ...config,\n bufferAhead: normalizedBufferAhead,\n maxCacheSize: normalizedMaxCacheSize,\n };\n this._logger = new Logger(\"HLS-IO:Segment\", debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n this._callbacks = callbacks;\n this._prefetchStrategy = new LookAheadPrefetchStrategy();\n }\n\n /**\n * Update the segment list — called when the playlist is refreshed.\n *\n * This is the primary entry point for new segment data. When the playlist\n * manager detects new or updated segments, it calls this method to feed\n * them into the segment pipeline.\n *\n * 更新分片列表 — 在播放列表刷新时调用。\n *\n * 这是新分片数据的主要入口点。当播放列表管理器检测到新的或更新的分片时,\n * 会调用此方法将其送入分片管线。\n *\n * Live vs VOD handling:\n * 直播 vs VOD 处理:\n * - In live mode, we fetch the most recent N segments (sliding window).\n * 在直播模式下,获取最近的 N 个分片(滑动窗口)。\n * - In VOD mode, we prefetch segments ahead of the current playback position.\n * 在 VOD 模式下,预取当前播放位置之前的分片。\n *\n * @param newSegments - The full segment list from the latest playlist\n * @param newSegments - 来自最新播放列表的完整分片列表\n * @param partialSegments - Optional partial segments for LL-HLS\n * @param partialSegments - LL-HLS 的可选部分分片\n *\n * @example\n * ```typescript\n * // Called by PlaylistManager when new segments are detected\n * // 当 PlaylistManager 检测到新分片时调用\n * manager.updateSegments(playlist.segments, playlist.partialSegments);\n *\n * // Only new segments without existing cache entries will be fetched\n * // 只会获取没有现有缓存条目的新分片\n * ```\n */\n updateSegments(newSegments: Segment[], partialSegments?: PartialSegment[]): void {\n if (this._isDestroyed) return;\n\n const oldSegmentCount = this._segments.length;\n this._segments = newSegments;\n\n // Store partial segments for LL-HLS\n // 存储 LL-HLS 的部分分片\n // Partial segments allow sub-segment granularity for ultra-low-latency playback.\n // 部分分片允许子分片粒度以实现超低延迟播放。\n if (partialSegments) {\n this._pendingPartials = partialSegments;\n for (const part of partialSegments) {\n this._callbacks.onPartSegment(part);\n }\n }\n\n // Route to the appropriate fetching strategy based on playlist type\n // 根据播放列表类型选择合适的获取策略\n if (this._config.playlistType === PlaylistType.LIVE) {\n this._handleLiveSegments();\n } else {\n this._handleVODSegments();\n }\n\n this._logger.debug(`Segments updated: ${oldSegmentCount} -> ${newSegments.length}, ` + `type: ${this._config.playlistType}`);\n }\n\n /**\n * Fetch a specific segment by its media sequence number.\n *\n * Useful for seeking to a specific position or manually loading a segment\n * outside the normal prefetch window.\n *\n * 按媒体序列号获取指定的分片。\n *\n * 适用于 Seek 到特定位置或手动加载正常预取窗口之外的分片。\n *\n * @param sequence - The media sequence number of the segment to fetch\n * @param sequence - 要获取的分片的媒体序列号\n * @returns The loaded segment, or null if not found in the segment list\n * @returns 已加载的分片,如果在分片列表中未找到则为 null\n *\n * @example\n * ```typescript\n * // Fetch segment at sequence 150 (e.g., after a seek)\n * // 获取序列号 150 的分片(例如 Seek 之后)\n * const segment = await manager.fetchSegment(150);\n * if (segment) {\n * const data = manager.getCachedSegment(segment);\n * // Use the data\n * // 使用数据\n * }\n * ```\n */\n async fetchSegment(sequence: number): Promise<Segment | null> {\n const segment = this._segments.find((s) => s.sequence === sequence);\n if (!segment) return null;\n\n return this._loadSegment(segment);\n }\n\n /**\n * Get the next segment after the current playback position.\n *\n * This does NOT advance currentSequence — it's a read-only lookup.\n * Use advanceToNext() to both get and advance.\n *\n * 获取当前播放位置之后的下一个分片。\n *\n * 这不会推进 currentSequence — 这是一个只读查找。\n * 使用 advanceToNext() 可同时获取和推进。\n *\n * @returns The next segment, or null if at the end of the list\n * @returns 下一个分片,如果已到列表末尾则为 null\n *\n * @example\n * ```typescript\n * // Peek at the next segment without advancing\n * // 查看下一个分片但不推进\n * const next = manager.getNextSegment();\n * if (next && manager.getCachedSegment(next)) {\n * // Next segment is already cached — ready to play\n * // 下一个分片已缓存 — 可以播放\n * }\n * ```\n */\n getNextSegment(): Segment | null {\n const sorted = this.getSortedSegments();\n const idx = sorted.findIndex((s) => s.sequence > this._currentSequence);\n if (idx === -1) return null;\n return sorted[idx];\n }\n\n /**\n * Advance the current playback position to the next segment and return it.\n *\n * This method:\n * 1. Finds the segment with the smallest sequence > currentSequence\n * 2. Updates currentSequence to that segment's sequence\n * 3. Evicts old segments from the cache to keep memory bounded\n *\n * 将当前播放位置推进到下一个分片并返回它。\n *\n * 此方法:\n * 1. 查找序列号大于 currentSequence 的最小分片\n * 2. 将 currentSequence 更新为该分片的序列号\n * 3. 从缓存中驱逐旧分片以控制内存\n *\n * @returns The next segment, or null if at the end\n * @returns 下一个分片,如果已到末尾则为 null\n *\n * @example\n * ```typescript\n * // Advance through segments during playback\n * // 播放期间逐个推进分片\n * let segment = manager.advanceToNext();\n * while (segment) {\n * const data = manager.getCachedSegment(segment);\n * if (data) {\n * await playSegment(data);\n * }\n * segment = manager.advanceToNext();\n * }\n * ```\n */\n advanceToNext(): Segment | null {\n const next = this.getNextSegment();\n if (next) {\n this._currentSequence = next.sequence;\n // Evict old cached segments: keep only the recent window around currentSequence\n // 驱逐旧缓存分片:仅保留 currentSequence 附近的最近窗口\n // This prevents unbounded memory growth, especially in long VOD content.\n // 这可以防止内存无限增长,特别是在长 VOD 内容中。\n this._cache.evictBeforeSequence(this._currentSequence - this._config.maxCacheSize);\n\n // Refill the buffer after advancing: each consumed segment should trigger\n // fetching new segments to maintain the configured bufferAhead count.\n // 推进后重新填充缓冲区:每消费一个分片就触发获取新分片,\n // 以维持配置的 bufferAhead 数量。\n if (this._config.playlistType === PlaylistType.LIVE) {\n this._handleLiveSegments();\n } else {\n this._handleVODSegments();\n }\n }\n return next;\n }\n\n /**\n * Get cached data for a segment if available.\n *\n * 获取分片的缓存数据(如果可用)。\n *\n * @param segment - The segment to look up\n * @param segment - 要查找的分片\n * @returns The cached ArrayBuffer, or null if not cached\n * @returns 缓存的 ArrayBuffer,未缓存则为 null\n */\n getCachedSegment(segment: Segment): ArrayBuffer | null {\n return this._cache.get(segment);\n }\n\n /**\n * Get all segments sorted by sequence number in ascending order.\n *\n * 获取按序列号升序排列的所有分片。\n *\n * @returns A new sorted array of segments\n * @returns 排序后的新分片数组\n */\n getSortedSegments(): Segment[] {\n return [...this._segments].sort((a, b) => a.sequence - b.sequence);\n }\n\n /**\n * Get a snapshot of the segment cache statistics.\n *\n * 获取分片缓存统计的快照。\n *\n * @returns Cache statistics including hits, misses, evictions\n * @returns 缓存统计,包括命中、未命中、驱逐次数\n */\n getCacheStats() {\n return this._cache.getStats();\n }\n\n /**\n * Get the total duration of all known segments in seconds.\n *\n * 获取所有已知分片的总时长(秒)。\n *\n * @returns Total duration in seconds\n * @returns 总时长(秒)\n */\n get totalDuration(): number {\n return this._segments.reduce((sum, s) => sum + s.duration, 0);\n }\n\n /**\n * Get total duration of currently cached segment data in seconds.\n *\n * 获取当前已缓存分片数据的总时长(秒)。\n *\n * This reflects how much media data is already buffered in memory, regardless\n * of whether the playlist is LIVE or VOD.\n * 该值表示内存中已缓冲的媒体数据时长,不区分 LIVE 或 VOD。\n *\n * @returns Buffered duration in seconds\n * @returns 已缓冲时长(秒)\n */\n get bufferedDuration(): number {\n return this._cache.getSegmentsInOrder().reduce((sum, segment) => sum + segment.duration, 0);\n }\n\n /**\n * Get the last segment in the playlist (highest sequence number).\n *\n * 获取播放列表中的最后一个分片(最高序列号)。\n *\n * @returns The last segment, or null if no segments known\n * @returns 最后一个分片,如果没有已知分片则为 null\n */\n getLastSegment(): Segment | null {\n const sorted = this.getSortedSegments();\n return sorted.length > 0 ? sorted[sorted.length - 1] : null;\n }\n\n /**\n * Update the playlist type at runtime.\n *\n * This is useful when the playlist type is detected after the initial load\n * (e.g., an EVENT playlist that later reveals itself as LIVE).\n *\n * 在运行时更新播放列表类型。\n *\n * 适用于初始加载后检测到播放列表类型的情况(例如,EVENT 播放列表后来显示为 LIVE)。\n *\n * @param type - The new playlist type\n * @param type - 新的播放列表类型\n */\n setPlaylistType(type: PlaylistType): void {\n this._config.playlistType = type;\n }\n\n /**\n * Destroy the segment manager, clearing cache and aborting pending operations.\n *\n * 销毁分片管理器,清除缓存并中止待处理的操作。\n */\n destroy(): void {\n this._isDestroyed = true;\n this._cache.clear();\n this._segments = [];\n this._fetchQueue.clear();\n this._loadedSegmentKeys.clear();\n this._mapDataCache.clear();\n this._pendingFetchQueue = [];\n }\n\n // ==================== Private Methods ====================\n // ==================== 私有方法 ====================\n\n /**\n * Handle live segment fetching strategy.\n *\n * Live strategy:\n * 直播策略:\n * - Always fetch the most recent N segments (where N = bufferAhead).\n * 始终获取最近的 N 个分片(N = bufferAhead)。\n * - This keeps the buffer near the live edge, minimizing latency.\n * 这使缓冲区保持在直播边缘附近,最小化延迟。\n * - Old segments outside the sliding window are evicted.\n * 滑动窗口之外的旧分片将被驱逐。\n * - The sliding window size is controlled by maxCacheSize.\n * 滑动窗口大小由 maxCacheSize 控制。\n *\n * 处理直播分片获取策略。\n */\n private _handleLiveSegments(): void {\n const sorted = this.getSortedSegments();\n if (sorted.length === 0) return;\n\n // RFC 8216 §6.3.3: for live streams, start >= 3*targetDuration from end.\n // RFC 8216 §6.3.3: 直播流应从距末尾>=3*targetDuration处分片开始。\n if (this._currentSequence === -1) {\n const td = this._config.targetDuration || sorted[0].duration || 6;\n let acc = 0;\n for (let i = sorted.length - 1; i >= 0; i--) {\n acc += sorted[i].duration;\n if (acc >= td * 3) {\n this._currentSequence = sorted[i].sequence - 1;\n break;\n }\n }\n if (this._currentSequence === -1) {\n this._currentSequence = sorted[0].sequence - 1;\n }\n }\n\n // In live mode, fetch the last N segments (closest to the live edge)\n // 在直播模式下,获取最后 N 个分片(最接近直播边缘)\n const lastSegments = sorted.slice(-this._config.bufferAhead);\n\n for (const segment of lastSegments) {\n if (!this._cache.has(segment)) {\n this._enqueueFetch(segment);\n }\n }\n\n // Evict old segments: keep only the recent window\n // 驱逐旧分片:仅保留最近窗口\n // As the live playlist advances, segments older than the window are irrelevant.\n // 随着直播播放列表推进,窗口之前的旧分片已无关紧要。\n const oldestToKeep = sorted[sorted.length - 1]?.sequence - this._config.maxCacheSize;\n if (oldestToKeep > 0) {\n this._cache.evictBeforeSequence(oldestToKeep);\n }\n }\n\n /**\n * Handle VOD segment fetching strategy.\n *\n * VOD strategy:\n * VOD 策略:\n * - Use the configured prefetch strategy to determine which segments to fetch next.\n * 使用配置的预取策略来决定接下来获取哪些分片。\n * - Prefetch segments sequentially ahead of the current playback position.\n * 在当前播放位置之前按顺序预取分片。\n * - Monitor buffer health and emit stall warnings if the buffer falls behind.\n * 监控缓冲区健康状态,并在缓冲区落后时发出卡顿警告。\n *\n * 处理 VOD 分片获取策略。\n */\n private _handleVODSegments(): void {\n const sorted = this.getSortedSegments();\n if (sorted.length === 0) return;\n\n // Determine which segments to prefetch based on the strategy\n // 根据策略确定要预取的分片\n const toFetch = this._prefetchStrategy.getNextSegments(this._currentSequence, sorted, this._config.bufferAhead);\n\n for (const segment of toFetch) {\n if (!this._cache.has(segment)) {\n this._enqueueFetch(segment);\n }\n }\n\n // Check buffer health: compare against remaining segments instead of raw\n // bufferAhead, so short VOD tails (remaining < bufferAhead) don't report\n // false stalls.\n // 检查缓冲健康状态:与“剩余分片数”比较而不是直接与 bufferAhead 比较,\n // 避免 VOD 尾部(剩余分片数 < bufferAhead)出现误报卡顿。\n const buffered = sorted.filter((s) => s.sequence > this._currentSequence && this._cache.has(s)).length;\n const remaining = sorted.filter((s) => s.sequence > this._currentSequence).length;\n const needed = Math.min(this._config.bufferAhead, remaining);\n\n if (needed > 0 && buffered < needed && this._currentSequence >= 0) {\n // Buffer is running low — notify the consumer to potentially slow down or pause\n // 缓冲区不足 — 通知消费者可能需要减速或暂停\n this._callbacks.onBufferStall({\n buffered,\n needed,\n });\n }\n }\n\n /**\n * Enqueue a segment for serial fetching.\n *\n * Segments are fetched strictly one at a time in the order they are enqueued.\n * This prevents parallel requests that could overwhelm the network or CDN,\n * and ensures segments arrive in sequence order for smooth playback.\n *\n * 将分片加入串行获取队列。\n *\n * 分片严格按入队顺序逐个获取。这避免了并行请求可能导致的网络或 CDN 过载,\n * 并确保分片按顺序到达以实现流畅播放。\n *\n * @param segment - The segment to enqueue for fetching\n * @param segment - 要加入获取队列的分片\n */\n private _enqueueFetch(segment: Segment): void {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n if (this._loadedSegmentKeys.has(key)) return;\n if (this._fetchQueue.has(key)) return;\n\n this._fetchQueue.add(key);\n this._pendingFetchQueue.push(segment);\n\n // Start the serial fetch loop if not already running\n // 如果串行获取循环尚未运行,则启动它\n this._processFetchQueue();\n }\n\n /**\n * Process the serial fetch queue: fetch segments one by one.\n * Only one instance of this loop runs at a time (guarded by _isFetchLoopRunning).\n *\n * 处理串行获取队列:逐个获取分片。\n * 同一时间只有一个循环实例运行(由 _isFetchLoopRunning 守卫)。\n */\n private async _processFetchQueue(): Promise<void> {\n if (this._isFetchLoopRunning) return;\n this._isFetchLoopRunning = true;\n\n try {\n while (this._pendingFetchQueue.length > 0 && !this._isDestroyed) {\n const segment = this._pendingFetchQueue.shift()!;\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n\n // Skip if already loaded while waiting in queue\n // 如果在队列中等待时已被加载,则跳过\n if (this._loadedSegmentKeys.has(key)) {\n this._fetchQueue.delete(key);\n continue;\n }\n\n await this._loadSegment(segment);\n }\n } catch (err) {\n this._logger.error(`Fetch queue processing error: ${(err as Error).message}`);\n } finally {\n this._isFetchLoopRunning = false;\n }\n }\n\n /**\n * Load a single segment: fetch from network, optionally decrypt, cache, and emit callbacks.\n *\n * This is the core pipeline for a single segment:\n * 1. Resolve the segment URI (relative → absolute)\n * 2. Fetch raw bytes from the network (with optional byte-range)\n * 3. If encrypted, decrypt the data using the configured Decryptor\n * 4. Cache the (decrypted) data\n * 5. Detect the segment format (TS, fMP4, etc.)\n * 6. Emit lifecycle callbacks (onSegmentReady, onDiscontinuity, onLastSegment)\n *\n * 加载单个分片:从网络获取、可选解密、缓存并触发回调。\n *\n * 这是单个分片的核心管线:\n * 1. 解析分片 URI(相对 → 绝对)\n * 2. 从网络获取原始字节(可选 byte-range)\n * 3. 如果加密,使用配置的 Decryptor 解密数据\n * 4. 缓存(解密后的)数据\n * 5. 检测分片格式(TS, fMP4 等)\n * 6. 触发生命周期回调(onSegmentReady, onDiscontinuity, onLastSegment)\n *\n * @param segment - The segment to load\n * @param segment - 要加载的分片\n * @returns The loaded segment, or null on failure\n * @returns 已加载的分片,失败时为 null\n */\n private async _loadSegment(segment: Segment): Promise<Segment | null> {\n const queueKey = segmentCacheKey(segment.uri, segment.byteRange);\n\n if (this._loadedSegmentKeys.has(queueKey)) {\n this._fetchQueue.delete(queueKey);\n return segment;\n }\n\n if (this._isDestroyed) return null;\n\n try {\n this._logger.debug(`Fetching segment seq=${segment.sequence} uri=${segment.uri}`);\n\n // fMP4 media segments depend on EXT-X-MAP init data.\n // fMP4 媒体分片依赖 EXT-X-MAP 初始化数据。\n if (segment.map) {\n const mapData = await this._loadMap(segment.map);\n if (!mapData) {\n this._fetchQueue.delete(queueKey);\n return null;\n }\n segment.mapData = mapData;\n }\n\n // Prefetch decryption key if necessary BEFORE fetching the segment itself\n // 预取解密密钥,确保密钥请求排在分片自身数据前\n if (this._decryptor.needsDecryption(segment)) {\n try {\n await this._decryptor.prefetchKey(segment);\n } catch (keyErr) {\n this._callbacks.onError({\n type: \"DECRYPT\",\n message: `Key fetch failed for segment ${segment.uri}: ${(keyErr as Error).message}`,\n originalError: keyErr as Error,\n segmentUri: segment.uri,\n });\n this._fetchQueue.delete(queueKey);\n return null;\n }\n }\n\n const url = this._config.resolveRelativeUris && this._config.baseUrl ? resolveUri(segment.uri, this._config.baseUrl) : segment.uri;\n\n let result;\n let attempt = 0;\n const maxRetries = this._config.playlistType === PlaylistType.LIVE ? 0 : (this._config.segmentRetryCount ?? 5);\n let delay = this._config.segmentRetryDelay ?? 500;\n const backoff = this._config.segmentRetryBackoff ?? 2;\n\n while (true) {\n try {\n result = await this._fetcher.fetchSegment(url, segment.byteRange, this._config.segmentTimeout ?? undefined);\n break; // successfully fetched\n } catch (err) {\n attempt++;\n if (attempt > maxRetries || this._isDestroyed) {\n throw err;\n }\n this._logger.warn(`Segment fetch failed sequence=${segment.sequence}, retrying (${attempt}/${maxRetries}) in ${delay}ms`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay *= backoff;\n }\n }\n\n if (this._isDestroyed) return null;\n\n let data = result.data;\n segment.data = data;\n\n // Decrypt if needed\n if (this._decryptor.needsDecryption(segment)) {\n this._callbacks.onDecrypting(segment);\n try {\n const decrypted = await this._decryptor.decrypt(segment, data);\n if (this._isDestroyed) return null;\n data = decrypted.data;\n segment.data = data;\n this._callbacks.onDecrypted(segment);\n } catch (decryptErr) {\n if (this._isDestroyed) return null;\n this._callbacks.onError({\n type: \"DECRYPT\",\n message: `Decryption failed for ${segment.uri}: ${(decryptErr as Error).message}`,\n originalError: decryptErr as Error,\n segmentUri: segment.uri,\n });\n // Decryption errors are not retryable — skip immediately\n this._fetchQueue.delete(queueKey);\n return null;\n }\n }\n\n if (this._isDestroyed) return null;\n\n // Cache and emit\n this._cache.set(segment, data);\n this._loadedSegmentKeys.add(queueKey);\n\n const format = guessFormatFromUri(segment.uri);\n this._logger.debug(`Segment ${segment.sequence} loaded (${format}, ${data.byteLength} bytes)`);\n\n this._callbacks.onSegmentReady(segment);\n\n if (segment.discontinuity) {\n this._callbacks.onDiscontinuity(segment);\n }\n\n if (this._config.playlistType === PlaylistType.VOD) {\n const sorted = this.getSortedSegments();\n const lastSeq = sorted[sorted.length - 1]?.sequence;\n if (segment.sequence === lastSeq) {\n this._logger.info(`Last segment fetched: seq=${segment.sequence}`);\n this._callbacks.onLastSegment(segment);\n }\n }\n\n this._fetchQueue.delete(queueKey);\n return segment;\n } catch (error) {\n if (this._isDestroyed) {\n this._fetchQueue.delete(queueKey);\n return null;\n }\n const lastError = error as Error;\n // Timeout detection: AbortError from AbortController signal\n // 超时检测:来自 AbortController 信号的 AbortError\n if (lastError.name === \"AbortError\") {\n this._callbacks.onError({\n type: \"TIMEOUT\",\n subType: \"SEGMENT\",\n message: `Segment fetch timed out: ${segment.uri}`,\n originalError: lastError,\n segmentUri: segment.uri,\n });\n this._fetchQueue.delete(queueKey);\n return null;\n }\n this._logger.warn(`Segment fetch failed for ${segment.uri}: ${lastError.message}`);\n\n // 重试多次后仍失败,报告错误但继续播放(如果可能),因为没有这个分片可能会导致整个播放失败。\n this._callbacks.onError({\n type: \"NETWORK\",\n subType: \"SEGMENT\",\n message: `Segment fetch failed: ${lastError.message}`,\n originalError: lastError,\n segmentUri: segment.uri,\n });\n this._fetchQueue.delete(queueKey);\n return null;\n }\n }\n\n /**\n * Load EXT-X-MAP init segment with retry and cache.\n */\n private async _loadMap(map: MapInfo): Promise<ArrayBuffer | null> {\n const mapKey = segmentCacheKey(map.uri, map.byteRange);\n const cached = this._mapDataCache.get(mapKey);\n if (cached) {\n return cached;\n }\n\n if (this._isDestroyed) return null;\n\n try {\n this._logger.debug(`Fetching EXT-X-MAP uri=${map.uri}`);\n\n const mapUrl = this._config.resolveRelativeUris && this._config.baseUrl ? resolveUri(map.uri, this._config.baseUrl) : map.uri;\n\n let result;\n let attempt = 0;\n const maxRetries = this._config.playlistType === PlaylistType.LIVE ? 0 : (this._config.segmentRetryCount ?? 5);\n let delay = this._config.segmentRetryDelay ?? 500;\n const backoff = this._config.segmentRetryBackoff ?? 2; // Exponential backoff factor 指数退避\n\n while (true) {\n try {\n result = await this._fetcher.fetchSegment(mapUrl, map.byteRange, this._config.segmentTimeout ?? undefined);\n break;\n } catch (err) {\n attempt++;\n if (attempt > maxRetries || this._isDestroyed) {\n throw err;\n }\n this._logger.warn(`EXT-X-MAP fetch failed uri=${map.uri}, retrying (${attempt}/${maxRetries}) in ${delay}ms`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n delay = Math.min(delay * backoff, 5000); // cap delay at 5s\n }\n }\n\n if (this._isDestroyed) return null;\n\n // Evict oldest map if cache exceeds the max size\n // 如果缓存超过最大容量,驱逐最早的 map\n if (this._mapDataCache.size >= this._MAX_MAP_CACHE) {\n const oldestKey = this._mapDataCache.keys().next().value;\n if (oldestKey) this._mapDataCache.delete(oldestKey);\n }\n\n this._mapDataCache.set(mapKey, result.data);\n this._callbacks.onMapLoaded({ mapUri: mapUrl, data: result.data });\n return result.data;\n } catch (error) {\n if (this._isDestroyed) return null;\n const lastError = error as Error;\n if (lastError.name === \"AbortError\") {\n this._callbacks.onError({\n type: \"TIMEOUT\",\n subType: \"MAP\",\n message: `EXT-X-MAP fetch timed out: ${map.uri}`,\n originalError: lastError,\n segmentUri: map.uri,\n });\n return null;\n }\n this._logger.warn(`EXT-X-MAP fetch failed for ${map.uri}: ${lastError.message}`);\n\n // 重试多次后仍失败,报告错误但继续播放(如果可能),因为没有 init segment 可能会导致整个播放失败。\n this._callbacks.onError({\n type: \"NETWORK\",\n subType: \"MAP\",\n message: `EXT-X-MAP fetch failed: ${lastError.message}`,\n originalError: lastError,\n segmentUri: map.uri,\n });\n return null;\n }\n }\n}\n","/**\n * HLS-IO: Main Client Class\n *\n * A comprehensive HLS (HTTP Live Streaming) IO client that supports:\n * - Live and VOD playback modes\n * - .ts and .fmp4 segment formats\n * - Relative and absolute segment URIs\n * - Last segment callback for VOD\n * - Total duration tracking\n * - Smart segment fetching strategies\n * - Segment update events\n * - Configurable retry with event callbacks\n * - Heartbeat/keepalive with configurable interval\n * - VOD segment caching (LRU/FIFO)\n * - Optimized M3U8 polling\n * - LL-HLS (Low-Latency HLS) support\n * - Complete tag parsing\n * - Decryption entry point (AES-128)\n * - Discontinuity handling\n */\n\nimport EventEmitter from \"eventemitter3\";\nimport deepmerge from \"deepmerge\";\nimport { HlsIOConfig, DEFAULT_CONFIG, HlsIOEvents, HlsIOEventMap, PlaylistType, Segment, MediaPlaylist, MasterPlaylist, PartialSegment, CacheStats } from \"./types\";\nimport { Fetcher } from \"./network/fetcher\";\nimport { Decryptor, DecryptorConfig } from \"./crypto/decrypt\";\nimport { PlaylistManager } from \"./playlist/playlist-manager\";\nimport { SegmentManager } from \"./segment/segment-manager\";\nimport { Logger } from \"@skax/logger\";\nimport { segmentCacheKey } from \"./utils/url\";\n\n/**\n * HlsIO — the primary client class for HLS (HTTP Live Streaming) playback.\n *\n * HlsIO orchestrates all subsystems — network fetching, M3U8 playlist parsing and\n * polling, segment buffering, decryption, and event emission — into a single,\n * easy-to-use interface. It extends a typed EventEmitter so consumers can\n * listen for lifecycle, data, and error events.\n *\n * ## Quick Start\n *\n * ```ts\n * import { HlsIO, HlsIOEvents } from \"hls-io\";\n *\n * // 1. Create an instance with a playlist URL\n * const io = new HlsIO({\n * url: \"https://example.com/live.m3u8\",\n * bufferAhead: 5,\n * debug: true,\n * });\n *\n * // 2. Listen for segments\n * io.on(HlsIOEvents.SEGMENT_READY, (segment) => {\n * console.log(\"Segment ready:\", segment.sequence);\n * // Append segment.data to MediaSource buffer …\n * });\n *\n * // 3. Listen for errors\n * io.on(HlsIOEvents.ERROR, (error) => {\n * console.error(`${error.type}: ${error.message}`);\n * });\n *\n * // 4. Start playback\n * await io.start();\n * ```\n *\n * ## Live vs VOD\n *\n * HlsIO auto-detects whether the playlist is LIVE (no `#EXT-X-ENDLIST`) or VOD\n * (has `#EXT-X-ENDLIST`). You can also force a type via\n * `config.playlistType`. In **LIVE** mode the playlist is continuously refreshed;\n * in **VOD** mode it is fetched once and segments are cached according to the\n * configured {@link HlsIOConfig.cachePolicy | cache policy}.\n *\n * ## Low-Latency HLS (LL-HLS)\n *\n * Set `config.lowLatencyMode = true` to enable LL-HLS. Partial segments\n * (`EXT-X-PART`) are delivered through the `partSegment` event. Use blocking\n * playlist reload where the server supports it.\n *\n * ---\n *\n * HlsIO — HLS(HTTP Live Streaming)播放的主客户端类。\n *\n * HlsIO 将所有子系统——网络获取、M3U8 播放列表解析与轮询、分片缓冲、解密以及\n * 事件发射——编排为一个简单易用的接口。它继承自类型化的 EventEmitter,\n * 因此使用者可以监听生命周期、数据和错误事件。\n *\n * ## 快速开始\n *\n * ```ts\n * import { HlsIO, HlsIOEvents } from \"hls-io\";\n *\n * const io = new HlsIO({\n * url: \"https://example.com/live.m3u8\",\n * bufferAhead: 5,\n * debug: true,\n * });\n *\n * io.on(HlsIOEvents.SEGMENT_READY, (segment) => {\n * console.log(\"分片就绪:\", segment.sequence);\n * // 将 segment.data 追加到 MediaSource 缓冲区 …\n * });\n *\n * io.on(HlsIOEvents.ERROR, (error) => {\n * console.error(`${error.type}: ${error.message}`);\n * });\n *\n * await io.start();\n * ```\n *\n * ## 直播与点播\n *\n * HlsIO 会自动检测播放列表是 LIVE(无 `#EXT-X-ENDLIST`)还是 VOD(有\n * `#EXT-X-ENDLIST`)。你也可以通过 `config.playlistType` 强制指定类型。在\n * **LIVE** 模式下,播放列表会持续刷新;在 **VOD** 模式下,播放列表只获取一次,\n * 分片按照配置的 {@link HlsIOConfig.cachePolicy | 缓存策略} 进行缓存。\n *\n * ## 低延迟 HLS (LL-HLS)\n *\n * 设置 `config.lowLatencyMode = true` 以启用 LL-HLS。部分分片(`EXT-X-PART`)\n * 会通过 `partSegment` 事件投递。在服务器支持的情况下使用阻塞式播放列表重新加载。\n *\n * @class\n * @extends {EventEmitter<HlsIOEventMap>}\n *\n * @example\n * // === Minimal live-streaming player ===\n * // === 最小化的直播播放器 ===\n * import { HlsIO, HlsIOEvents } from \"hls-io\";\n *\n * const io = new HlsIO({\n * url: \"https://live.example.com/stream.m3u8\",\n * bufferAhead: 5,\n * lowLatencyMode: true,\n * debug: false,\n * });\n *\n * io.on(HlsIOEvents.SEGMENT_READY, (seg) => {\n * // Feed segment.data to MSE SourceBuffer\n * sourceBuffer.appendBuffer(seg.data!);\n * });\n *\n * io.on(HlsIOEvents.ERROR, (e) => {\n * console.error(`[${e.type}] ${e.message}`);\n * });\n *\n * io.on(HlsIOEvents.ENDED, () => {\n * console.log(\"VOD finished\");\n * });\n *\n * io.on(HlsIOEvents.DURATION_UPDATED, (duration) => {\n * console.log(`Total duration: ${duration.toFixed(2)}s`);\n * });\n *\n * io.on(HlsIOEvents.BUFFERED_TIME_UPDATED, (bufferedTime) => {\n * console.log(`Buffered: ${bufferedTime.toFixed(2)}s`);\n * });\n *\n * await io.start();\n */\nexport class HlsIO extends EventEmitter<HlsIOEventMap> {\n static VERSION = \"__VERSION__\";\n\n /**\n * Exported event-name constants so consumers can write\n * `HlsIO.EVENTS.SEGMENT_READY` instead of importing `HlsIOEvents` separately.\n *\n * 导出的事件名称常量,方便使用方以 `HlsIO.EVENTS.SEGMENT_READY` 形式引用,\n * 无需单独导入 `HlsIOEvents`。\n */\n static EVENTS = HlsIOEvents;\n\n /** Merged configuration (user overrides + defaults) */\n /** 合并后的配置(用户覆盖 + 默认值) */\n private _config: Required<HlsIOConfig>;\n\n /** Internal logger instance */\n /** 内部日志记录器实例 */\n private _logger: Logger;\n\n /** Network fetcher with retry, heartbeat, and abort support */\n /** 支持重试、心跳和中止的网络获取器 */\n private _fetcher: Fetcher;\n\n /** AES-128 decryptor (WebCrypto-backed when available) */\n /** AES-128 解密器(可用时使用 WebCrypto) */\n private _decryptor: Decryptor;\n\n /** M3U8 playlist manager — handles loading, parsing, and polling */\n /** M3U8 播放列表管理器 — 处理加载、解析和轮询 */\n private _playlistManager: PlaylistManager | null = null;\n\n /** Segment manager — handles fetching, caching, and prefetching strategy */\n /** 分片管理器 — 处理获取、缓存和预取策略 */\n private _segmentManager: SegmentManager | null = null;\n\n /** The most recently loaded media playlist */\n /** 最近加载的媒体播放列表 */\n private _currentPlaylist: MediaPlaylist | null = null;\n\n /** Whether the client is currently running */\n /** 客户端当前是否正在运行 */\n private _isRunning = false;\n\n /** Whether destroy() has been called — prevents reuse */\n /** destroy() 是否已被调用 — 阻止重复使用 */\n private _isDestroyed = false;\n\n /** Accumulated total duration of all segments in seconds */\n /** 所有分片的累计总时长(秒) */\n private _duration = 0;\n /** Historical sum of downloaded segment durations in seconds */\n /** 历史已下载分片时长累计(秒) */\n private _bufferedTime = 0;\n /** Dedup keys for segments used by cumulative duration across playlist polling */\n /** 轮询播放列表期间用于累计 duration 的分片去重键 */\n private _durationSegmentKeys: Set<string> = new Set();\n /** Dedup keys for downloaded segments used by bufferedTime */\n /** bufferedTime 使用的已下载分片去重键 */\n private _downloadedSegmentKeys: Set<string> = new Set();\n\n /**\n * Create a new HlsIO client instance.\n *\n * The constructor initialises the internal subsystems (logger, network fetcher,\n * decryptor) and wires up the retry event relay so the fetcher's internal retry\n * callback forwards events to the public `retry` event on the HlsIO instance.\n *\n * 构造函数初始化内部子系统(日志记录器、网络获取器、解密器)并连接重试事件\n * 中继,使得获取器的内部重试回调能够将事件转发到 HlsIO 实例上的公开 `retry`\n * 事件。\n *\n * @param {HlsIOConfig} config - Configuration object. `url` is required; all\n * other properties fall back to {@link DEFAULT_CONFIG}.\n * 配置对象。`url` 为必填项;其他所有属性将回退到 {@link DEFAULT_CONFIG}。\n *\n * @throws {Error} If `config.url` is not provided.\n *\n * @example\n * ```ts\n * import { HlsIO, PlaylistType } from \"hls-io\";\n *\n * // Minimal — auto-detect playlist type\n * // 最小配置 — 自动检测播放列表类型\n * const io = new HlsIO({\n * url: \"https://example.com/master.m3u8\",\n * });\n *\n * // Full live-stream config\n * // 完整的直播流配置\n * const liveIO = new HlsIO({\n * url: \"https://live.example.com/stream.m3u8\",\n * playlistType: PlaylistType.LIVE,\n * bufferAhead: 5,\n * heartbeatInterval: 10000,\n * lowLatencyMode: true,\n * debug: true,\n * });\n *\n * // Full VOD config with LRU caching\n * // 完整的点播配置,使用 LRU 缓存\n * const vodIO = new HlsIO({\n * url: \"https://vod.example.com/movie.m3u8\",\n * playlistType: PlaylistType.VOD,\n * maxCacheSize: 100,\n * cachePolicy: \"lru\",\n * onLastSegment: () => console.log(\"Playback complete! / 播放完成!\"),\n * });\n * ```\n */\n constructor(config: HlsIOConfig) {\n super();\n\n // Merge defaults with user config\n // 将默认值与用户配置合并\n this._config = deepmerge.all([DEFAULT_CONFIG, config], { clone: false }) as Required<HlsIOConfig>;\n this._logger = new Logger(\"HLS-IO\", this._config.debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n\n this._logger.info(\"Initializing HlsIO with config:\", this._config);\n\n // Initialize fetcher — the core network layer (no retry — retry is in each module)\n // 初始化获取器 — 核心网络层(不含重试 — 重试在各模块中实现)\n this._fetcher = new Fetcher(\n {\n ...this._config.fetchOptions,\n },\n this._config.debug,\n this._config.timeout,\n );\n\n // Initialize decryptor — WebCrypto-based AES-128 by default\n // 初始化解密器 — 默认使用基于 WebCrypto 的 AES-128\n const decryptorConfig: DecryptorConfig = {\n useWebCrypto: true,\n };\n this._decryptor = new Decryptor(decryptorConfig);\n\n this._logger.info(`HlsIO initialized`);\n }\n\n // ==================== Public API ====================\n\n /**\n * Start the HLS client.\n *\n * Loads the initial playlist (and auto-selects a variant if a master playlist\n * is detected), creates the playlist and segment managers, starts the heartbeat\n * (if configured), and begins periodic M3U8 polling for live streams.\n *\n * 启动 HLS 客户端。\n *\n * 加载初始播放列表(如果检测到主播放列表则自动选择变体),创建播放列表管理器\n * 和分片管理器,启动心跳(如果已配置),并开始直播流的定期 M3U8 轮询。\n *\n * @returns {Promise<void>} Resolves when the initial playlist has been loaded\n * and processing has started. Rejects with an error if startup fails.\n * 当初始播放列表加载完成并开始处理后解析。如果启动失败则拒绝并返回错误。\n *\n * @throws {Error} If the instance has been destroyed.\n *\n * @example\n * ```ts\n * import { HlsIO, HlsIOEvents } from \"hls-io\";\n *\n * const io = new HlsIO({ url: \"https://example.com/live.m3u8\" });\n *\n * io.on(HlsIOEvents.PLAYLIST_LOADED, (playlist) => {\n * console.log(\n * `Type: ${playlist.type}, segments: ${\"segments\" in playlist ? playlist.segments.length : 0}`\n * );\n * });\n *\n * try {\n * await io.start();\n * console.log(\"HlsIO started / HlsIO 已启动\");\n * } catch (err) {\n * console.error(\"Failed to start / 启动失败:\", err);\n * }\n * ```\n */\n async start(url?: string): Promise<void> {\n if (this._isDestroyed) {\n throw new Error(\"HlsIO has been destroyed\");\n }\n if (this._isRunning) {\n this._logger.warn(\"HlsIO is already running\");\n return;\n }\n\n this._isRunning = true;\n this._logger.info(\"Starting HlsIO...\");\n\n if (url) {\n // Update the URL in config\n // 更新配置中的 URL\n this._config.url = url;\n }\n\n if (!this._config.url) {\n throw new Error(\"HlsIO: url is required\");\n }\n\n try {\n // Start heartbeat if configured — keeps the connection alive and\n // helps detect network degradation early.\n // 如果已配置则启动心跳 — 保持连接活跃并帮助及早发现网络退化。\n if (this._config.heartbeatInterval > 0) {\n // this._fetcher.startHeartbeat(this._config.url, this._config.heartbeatInterval, (timestamp) => {\n // this.emit(HlsIOEvents.HEARTBEAT, timestamp);\n // });\n }\n\n // Create playlist manager — the brain behind M3U8 fetching and polling.\n // All callbacks bridge internal state changes to public events.\n // 创建播放列表管理器 — M3U8 获取和轮询背后的核心。\n // 所有回调将内部状态变更桥接到公开事件。\n this._playlistManager = new PlaylistManager(\n this._fetcher,\n {\n url: this._config.url,\n playlistType: this._config.playlistType,\n playlistRefreshInterval: this._config.playlistRefreshInterval,\n lowLatencyMode: this._config.lowLatencyMode,\n resolveRelativeUris: this._config.resolveRelativeUris,\n baseUrl: this._config.baseUrl || this._config.url,\n followRedirectUrl: this._config.followRedirectUrl,\n },\n {\n // Forward playlist loaded → private handler that wires up SegmentManager\n // 播放列表加载完成 → 转发到私有处理器,连接 SegmentManager\n onPlaylistLoaded: (playlist) => this._onPlaylistLoaded(playlist),\n\n // New segments detected during live polling → public event\n // 直播轮询期间检测到的新分片 → 公开事件\n onSegmentsUpdated: (segments, playlist) => this._onSegmentsUpdated(segments, playlist),\n\n // LL-HLS partial segments → public event (per-part emission in handler)\n // LL-HLS 部分分片 → 公开事件(在处理器中逐部分发射)\n onPartialSegments: (parts) => this._onPartialSegments(parts),\n\n // Playlist type determined → public event (useful for UI switching)\n // 播放列表类型已确定 → 公开事件(用于 UI 切换)\n onPlaylistTypeDetected: (type) => this.emit(HlsIOEvents.PLAYLIST_TYPE_DETECTED, type),\n\n // Playlist ended (VOD only) → private handler\n // 播放列表已结束(仅 VOD) → 私有处理器\n onEnded: () => this._onEnded(),\n\n // Any playlist-level error → public error event\n // 任何播放列表级别的错误 → 公开错误事件\n onError: (error) => this.emit(HlsIOEvents.ERROR, error),\n },\n this._config.debug,\n );\n\n // Load the initial playlist\n // 加载初始播放列表\n const result = await this._playlistManager.start();\n\n if (this._isMediaPlaylist(result)) {\n // Direct media playlist — wire up immediately\n // 直接是媒体播放列表 — 立即连接\n this._onPlaylistLoaded(result);\n } else {\n // Master playlist was handled inside PlaylistManager — it auto-selected\n // a variant and loaded the child media playlist. Retrieve it.\n // 主播放列表在 PlaylistManager 内部已处理 — 它自动选择了变体并加载了\n // 子媒体播放列表。取出它。\n const mediaPlaylist = this._playlistManager.getLastPlaylist();\n if (mediaPlaylist) {\n this._onPlaylistLoaded(mediaPlaylist);\n }\n }\n } catch (error) {\n this._logger.error(`Start failed: ${(error as Error).message}`);\n // Emit a structured error so consumers can handle startup failures\n // 发出结构化错误,以便使用者处理启动失败\n this.emit(HlsIOEvents.ERROR, {\n type: \"NETWORK\",\n message: `Failed to start: ${(error as Error).message}`,\n originalError: error as Error,\n playlistUrl: this._config.url,\n });\n this._isRunning = false;\n throw error;\n }\n }\n\n /**\n * Stop the HLS client gracefully.\n *\n * Stops all polling, cancels in-flight requests, tears down the playlist and\n * segment managers, and clears internal state. After calling `stop()` you may\n * call `start()` again to resume — the instance is **not** destroyed.\n *\n * 优雅地停止 HLS 客户端。\n *\n * 停止所有轮询,取消进行中的请求,拆除播放列表管理器和分片管理器,并清除内部\n * 状态。调用 `stop()` 后可以再次调用 `start()` 来恢复——实例**没有**被销毁。\n *\n * @returns {void}\n *\n * @example\n * ```ts\n * // Pause and resume playback\n * // 暂停并恢复播放\n * io.stop();\n * console.log(\"Paused / 已暂停\");\n *\n * // Later …\n * await io.start();\n * console.log(\"Resumed / 已恢复\");\n * ```\n */\n stop(): void {\n this._logger.info(\"Stopping HlsIO...\");\n this._isRunning = false;\n\n // Stop heartbeat keepalive\n // 停止心跳保活\n this._fetcher.stopHeartbeat();\n\n // Abort all in-flight network requests\n // 中止所有进行中的网络请求\n this._fetcher.cancelAll();\n\n // Tear down playlist manager (stops M3U8 polling)\n // 拆除播放列表管理器(停止 M3U8 轮询)\n if (this._playlistManager) {\n this._playlistManager.destroy();\n this._playlistManager = null;\n }\n\n // Tear down segment manager (clears cache & fetch queues)\n // 拆除分片管理器(清除缓存和获取队列)\n if (this._segmentManager) {\n this._segmentManager.destroy();\n this._segmentManager = null;\n }\n\n this._currentPlaylist = null;\n this._setDuration(0);\n this._setBufferedTime(0);\n this._durationSegmentKeys.clear();\n this._downloadedSegmentKeys.clear();\n }\n\n /**\n * Destroy the HLS client completely.\n *\n * Calls `stop()` to halt all activity, marks the instance as destroyed (so\n * further calls to `start()` will throw), and removes all event listeners.\n * After calling `destroy()` the instance **cannot** be reused — create a new\n * `HlsIO` instance instead.\n *\n * 完全销毁 HLS 客户端。\n *\n * 调用 `stop()` 停止所有活动,将实例标记为已销毁(因此后续调用 `start()` 会\n * 抛出异常),并移除所有事件监听器。调用 `destroy()` 后实例**不能**再使用——\n * 请创建一个新的 `HlsIO` 实例。\n *\n * @returns {void}\n *\n * @example\n * ```ts\n * // Full lifecycle\n * // 完整生命周期\n * const io = new HlsIO({ url: \"https://example.com/vod.m3u8\" });\n * await io.start();\n *\n * // … playback …\n *\n * io.destroy();\n * // io.start() would now throw\n * // io.start() 现在会抛出异常\n * ```\n */\n destroy(): void {\n this.stop();\n this._isDestroyed = true;\n this.removeAllListeners();\n this._logger.info(\"HlsIO destroyed\");\n }\n\n /**\n * Switch to a new stream URL without destroying the instance.\n *\n * Stops the current stream, updates the URL in config, clears internal state,\n * and restarts playback. All previously registered event listeners remain intact.\n * This is more efficient than calling destroy() + creating a new instance.\n *\n * 动态切换到新的流地址,无需销毁实例。\n *\n * 停止当前流,更新配置中的 URL,清除内部状态,然后重新开始播放。所有之前注册的\n * 事件监听器保持不变。这比调用 destroy() + 创建新实例更高效。\n *\n * @param {string} newUrl - The new M3U8 playlist URL.\n * 新的 M3U8 播放列表 URL。\n *\n * @returns {Promise<void>} Resolves when the new stream has started.\n * 当新流启动后解析。\n *\n * @example\n * ```ts\n * // Switch to a different stream\n * // 切换到不同的流\n * await io.switchUrl(\"https://example.com/another-stream.m3u8\");\n * ```\n */\n async switchUrl(newUrl: string): Promise<void> {\n if (this._isDestroyed) {\n throw new Error(\"HlsIO has been destroyed — cannot switch URL\");\n }\n\n this._logger.info(`Switching URL to ${newUrl}`);\n\n // Stop current stream (clears playlist manager, segment manager, resets isRunning)\n // 停止当前流(清除播放列表管理器、分片管理器、重置 isRunning)\n this.stop();\n\n // Clear current playlist reference so the new load starts fresh\n // 清除当前播放列表引用,使新加载从干净状态开始\n this._currentPlaylist = null;\n\n // Restart with the new URL\n // 使用新 URL 重新启动\n await this.start(newUrl);\n }\n\n /**\n * Advance the playback cursor by consuming segments that cover the given\n * duration in seconds. Returns all the segments that were consumed.\n *\n * This is useful for time-based seeking or simulating playback at a specific\n * speed. Segments that are consumed will have their cached data evicted\n * if they fall behind the cursor.\n *\n * 按给定的时长(秒)推进播放游标,消耗覆盖该时长的分片。返回所有已消耗的分片。\n *\n * 这对于基于时间的 seek 或以特定速度模拟播放非常有用。已消耗且落后于游标的\n * 分片缓存数据将被淘汰。\n *\n * @param {number} seconds - The amount of time to advance, in seconds.\n * 要推进的时长(秒)。\n *\n * @returns {Segment[]} The segments that were consumed to cover the duration.\n * Returns an empty array if no segments are available.\n * 为覆盖该时长而消耗的分片。如果没有可用分片则返回空数组。\n *\n * @example\n * ```ts\n * // Consume 10 seconds of content\n * // 消耗 10 秒的内容\n * const consumed = io.advanceByTime(10);\n * console.log(`Consumed ${consumed.length} segments / 消耗了 ${consumed.length} 个分片`);\n *\n * for (const seg of consumed) {\n * console.log(` #${seg.sequence}: ${seg.duration}s`);\n * }\n * ```\n */\n advanceByTime(seconds: number): Segment[] {\n const consumed: Segment[] = [];\n let remaining = seconds;\n // Cap iterations at the playlist size to prevent runaway loops\n // when seconds is excessively large.\n // 将迭代次数限制在播放列表大小以内,防止 seconds 过大时无限循环。\n const maxIterations = Math.max(this.segments.length, 1);\n let iterations = 0;\n\n while (remaining > 0 && iterations < maxIterations) {\n iterations++;\n const next = this.advance();\n if (!next) break;\n consumed.push(next);\n remaining -= next.duration;\n }\n\n return consumed;\n }\n\n /**\n * Get the cumulative duration of all observed segments in seconds.\n *\n * This value is accumulated across playlist polling and deduplicated by\n * segment cache key (`uri + byterange`). It therefore keeps historical\n * LIVE duration growth even when old segments slide out of the window.\n *\n * Changes are signalled via the {@link HlsIOEvents.DURATION_UPDATED} event,\n * which fires every time the value increases.\n *\n * 获取所有已观测分片的累计总时长(秒)。\n *\n * 该值会在播放列表轮询期间持续累计,并按分片缓存键(`uri + byterange`)去重。\n * 因此对于 LIVE 流,即使旧分片从窗口滑出,也会保留历史累计时长。\n *\n * 值变化时会通过 {@link HlsIOEvents.DURATION_UPDATED} 事件通知。\n *\n * @type {number}\n *\n * @example\n * ```ts\n * io.on(HlsIOEvents.DURATION_UPDATED, (duration) => {\n * console.log(`Total duration: ${duration.toFixed(2)}s`);\n * });\n *\n * // Synchronous access\n * console.log(io.duration);\n * ```\n */\n get duration(): number {\n return this._duration;\n }\n\n /**\n * Get the currently buffered media time in seconds.\n *\n * This value is the historical sum of downloaded segment durations.\n * Each segment is counted once (deduplicated by segment cache key).\n *\n * Changes are signalled via the {@link HlsIOEvents.BUFFERED_TIME_UPDATED}\n * event, which fires each time a new segment is downloaded and counted.\n *\n * 获取当前已缓冲的媒体时长(秒)。\n *\n * 该值是历史已下载分片时长累计。\n * 每个分片只计一次(按分片缓存键去重)。\n *\n * 值变化时会通过 {@link HlsIOEvents.BUFFERED_TIME_UPDATED} 事件通知。\n *\n * @type {number}\n *\n * @example\n * ```ts\n * io.on(HlsIOEvents.BUFFERED_TIME_UPDATED, (bufferedTime) => {\n * console.log(`Buffered: ${bufferedTime.toFixed(2)}s`);\n * });\n *\n * // Synchronous access\n * console.log(io.bufferedTime);\n * ```\n */\n get bufferedTime(): number {\n return this._bufferedTime;\n }\n\n /**\n * Get the current playlist type.\n *\n * Returns the detected (or forced) {@link PlaylistType}. If no playlist has\n * been loaded yet, falls back to the value in `config.playlistType`.\n *\n * 获取当前播放列表类型。\n *\n * 返回检测到的(或强制指定的){@link PlaylistType}。如果尚未加载任何播放列表,\n * 则回退到 `config.playlistType` 中的值。\n *\n * @type {PlaylistType}\n *\n * @example\n * ```ts\n * io.on(HlsIOEvents.PLAYLIST_TYPE_DETECTED, (type) => {\n * console.log(`Detected / 检测到: ${type}`);\n * });\n *\n * // Synchronous access after load\n * // 加载后的同步访问\n * if (io.playlistType === PlaylistType.VOD) {\n * console.log(\"VOD — will play to end / 点播 — 将播放至结尾\");\n * }\n * ```\n */\n get playlistType(): PlaylistType {\n return this._currentPlaylist?.type || this._config.playlistType;\n }\n\n /**\n * Get the current media playlist, if loaded.\n *\n * Returns `null` before `start()` completes. The playlist object contains\n * all segments, tags, and metadata parsed from the M3U8 file.\n *\n * 获取当前媒体播放列表(如果已加载)。\n *\n * 在 `start()` 完成之前返回 `null`。播放列表对象包含从 M3U8 文件中解析出的\n * 所有分片、标签和元数据。\n *\n * @type {MediaPlaylist | null}\n *\n * @example\n * ```ts\n * const pl = io.playlist;\n * if (pl) {\n * console.log(`Segments / 分片: ${pl.segments.length}`);\n * console.log(`Target duration / 目标时长: ${pl.targetDuration}s`);\n * console.log(`Version / 版本: ${pl.version}`);\n * }\n * ```\n */\n get playlist(): MediaPlaylist | null {\n return this._currentPlaylist;\n }\n\n /**\n * Get all segments from the current playlist.\n *\n * Returns an empty array if no playlist has been loaded. The returned array\n * is a direct reference to the playlist's segment list — mutate with care.\n *\n * 获取当前播放列表中的所有分片。\n *\n * 如果尚未加载播放列表,则返回空数组。返回的数组是对播放列表分片列表的直接\n * 引用——请谨慎修改。\n *\n * @type {Segment[]}\n *\n * @example\n * ```ts\n * const segs = io.segments;\n * console.log(`Loaded ${segs.length} segments / 已加载 ${segs.length} 个分片`);\n *\n * // Find the next segment after a given sequence\n * // 查找给定序列号之后的下一个分片\n * const next = segs.find(s => s.sequence > lastPlayedSeq);\n * ```\n */\n get segments(): readonly Segment[] {\n return this._currentPlaylist?.segments || [];\n }\n\n /**\n * Get the next segment to play, based on the internal playback cursor.\n *\n * Delegates to the {@link SegmentManager} which tracks which sequence number\n * was last consumed via `advance()`.\n *\n * 获取下一个要播放的分片,基于内部播放游标。\n *\n * 委托给 {@link SegmentManager},它通过 `advance()` 跟踪上次消费的序列号。\n *\n * @returns {Segment | null} The next segment, or `null` if none is queued or\n * the segment manager has not been created yet.\n * 下一个分片,如果没有排队的分片或分片管理器尚未创建则返回 `null`。\n *\n * @example\n * ```ts\n * const next = io.getNextSegment();\n * if (next) {\n * console.log(`Next / 下一个: seq=${next.sequence}, dur=${next.duration}s`);\n * }\n * ```\n */\n getNextSegment(): Segment | null {\n return this._segmentManager?.getNextSegment() || null;\n }\n\n /**\n * Advance the playback cursor to the next segment and return it.\n *\n * This also triggers cache eviction of segments that are far behind the new\n * cursor position, keeping memory usage bounded.\n *\n * 将播放游标前进到下一个分片并返回它。\n *\n * 这也会触发对远离新游标位置的分片进行缓存淘汰,保持内存使用在合理范围内。\n *\n * @returns {Segment | null} The segment that was advanced to, or `null`.\n * 前进到的分片,或 `null`。\n *\n * @example\n * ```ts\n * // Playback loop pattern\n * // 播放循环模式\n * while (true) {\n * const seg = io.advance();\n * if (!seg) break;\n * await playSegment(seg);\n * }\n * ```\n */\n advance(): Segment | null {\n return this._segmentManager?.advanceToNext() || null;\n }\n\n /**\n * Fetch a specific segment by its media sequence number.\n *\n * This is a targeted fetch — it bypasses the normal prefetch strategy and\n * fetches (and caches) the specified segment immediately. Useful for seeking.\n *\n * 根据媒体序列号获取特定分片。\n *\n * 这是一次针对性获取——它绕过正常的预取策略,立即获取(并缓存)指定的分片。\n * 适用于 seek 操作。\n *\n * @param {number} sequence - The media sequence number of the desired segment.\n * 所需分片的媒体序列号。\n *\n * @returns {Promise<Segment | null>} The fetched segment (with `data` populated),\n * or `null` if not found or the segment manager is unavailable.\n * 获取到的分片(已填充 `data`),如果未找到或分片管理器不可用则返回 `null`。\n *\n * @example\n * ```ts\n * // Seek to a specific sequence number\n * // 跳转到特定序列号\n * const target = await io.fetchSegment(42);\n * if (target) {\n * sourceBuffer.appendBuffer(target.data!);\n * }\n * ```\n */\n async fetchSegment(sequence: number): Promise<Segment | null> {\n if (!this._segmentManager) return null;\n return this._segmentManager.fetchSegment(sequence);\n }\n\n /**\n * Get segment cache statistics.\n *\n * Returns information about cache size, hit/miss counts, and evictions.\n * Useful for monitoring cache performance and memory usage.\n *\n * 获取分片缓存统计信息。\n *\n * 返回有关缓存大小、命中/未命中次数和淘汰数量的信息。用于监控缓存性能和\n * 内存使用情况。\n *\n * @returns {CacheStats | null} Cache statistics, or `null` if the segment\n * manager has not been created yet.\n * 缓存统计信息,如果分片管理器尚未创建则返回 `null`。\n *\n * @example\n * ```ts\n * setInterval(() => {\n * const stats = io.getCacheStats();\n * if (stats) {\n * console.log(\n * `Cache / 缓存: size=${stats.size}, hits=${stats.hits}, misses=${stats.misses}`\n * );\n * }\n * }, 5000);\n * ```\n */\n getCacheStats(): CacheStats | null {\n return this._segmentManager?.getCacheStats() || null;\n }\n\n /**\n * Enable or disable debug logging at runtime.\n *\n * When enabled, all internal subsystems (fetcher, playlist manager, segment\n * manager, decryptor) switch to verbose logging. Useful for troubleshooting.\n *\n * 在运行时启用或禁用调试日志。\n *\n * 启用后,所有内部子系统(获取器、播放列表管理器、分片管理器、解密器)将切换\n * 到详细日志模式。用于排查问题。\n *\n * @param {boolean} debug - `true` to enable debug logging, `false` to disable.\n * `true` 启用调试日志,`false` 禁用。\n *\n * @example\n * ```ts\n * // Enable verbose logging for debugging\n * // 启用详细日志以进行调试\n * io.setDebug(true);\n *\n * // … reproduce the issue …\n *\n * // Disable when done\n * // 完成后禁用\n * io.setDebug(false);\n * ```\n */\n setDebug(debug: boolean): void {\n this._config.debug = debug;\n this._logger.setLevel(debug ? Logger.LEVEL.DEBUG : Logger.LEVEL.WARN);\n this._decryptor.setDebug(debug);\n }\n\n /**\n * Update the fetcher's CORS configuration at runtime.\n *\n * This is useful when the initial CORS mode causes opaque responses and you\n * need to switch to `no-cors` or change credentials mid-session.\n *\n * 在运行时更新获取器的 CORS 配置。\n *\n * 当初始 CORS 模式导致不透明响应,需要切换为 `no-cors` 或在会话中途更改凭证\n * 模式时非常有用。\n *\n * @param {RequestMode} mode - The fetch mode: `\"cors\"`, `\"no-cors\"`, `\"same-origin\"`, or `\"navigate\"`.\n * fetch 模式:`\"cors\"`、`\"no-cors\"`、`\"same-origin\"` 或 `\"navigate\"`。\n * @param {RequestCredentials} [credentials] - The credentials mode. If omitted,\n * preserves the existing credentials setting from `config.fetchOptions`.\n * 凭证模式。如果省略,则保留 `config.fetchOptions` 中的现有凭证设置。\n *\n * @example\n * ```ts\n * // Switch to no-cors if CORS headers are missing\n * // 如果缺少 CORS 响应头,切换为 no-cors\n * io.setCorsMode(\"no-cors\");\n *\n * // Include cookies for authenticated CDN\n * // 为需要认证的 CDN 携带 cookie\n * io.setCorsMode(\"cors\", \"include\");\n * ```\n */\n setCorsMode(mode: RequestMode, credentials?: RequestCredentials): void {\n this._fetcher.updateFetchOptions({\n mode,\n credentials: credentials || this._config.fetchOptions?.credentials,\n });\n }\n\n /**\n * Configure a custom decryption key provider function.\n *\n * The key provider is called whenever a segment needs decryption. It receives\n * the segment's {@link KeyInfo} and the segment itself, and must return the raw\n * AES-128 key bytes as an `ArrayBuffer`. This is the extension point for\n * integrating with DRM license servers (e.g. FairPlay).\n *\n * 配置自定义的解密密钥提供函数。\n *\n * 密钥提供函数在分片需要解密时被调用。它接收分片的 {@link KeyInfo} 和分片本身,\n * 并且必须返回原始 AES-128 密钥字节(`ArrayBuffer`)。这是与 DRM 许可证服务器\n *(例如 FairPlay)集成的扩展点。\n *\n * @param {Function} keyProvider - A function that receives key info and the\n * segment and returns a Promise resolving to the raw key bytes.\n * 一个函数,接收密钥信息和分片,返回解析为原始密钥字节的 Promise。\n *\n * @example\n * ```ts\n * // Custom key provider that fetches from a DRM server\n * // 自定义密钥提供函数,从 DRM 服务器获取密钥\n * io.setKeyProvider(async (keyInfo, segment) => {\n * const response = await fetch(keyInfo.uri!, {\n * headers: { \"X-Custom-Auth\": \"my-token\" },\n * });\n * return response.arrayBuffer();\n * });\n * ```\n */\n setKeyProvider(keyProvider: (keyInfo: import(\"./types\").KeyInfo, segment: Segment) => Promise<ArrayBuffer>): void {\n this._decryptor.updateConfig({ keyProvider });\n }\n\n /**\n * Check whether the client is currently running.\n *\n * Returns `true` between `start()` and `stop()`/`destroy()` calls, inclusive\n * of the time `start()` is still loading.\n *\n * 检查客户端当前是否正在运行。\n *\n * 在 `start()` 和 `stop()`/`destroy()` 调用之间返回 `true`,包括 `start()`\n * 仍在加载期间。\n *\n * @type {boolean}\n *\n * @example\n * ```ts\n * if (io.running) {\n * console.log(\"Client is active / 客户端正在运行\");\n * } else {\n * console.log(\"Client is stopped / 客户端已停止\");\n * }\n * ```\n */\n get running(): boolean {\n return this._isRunning;\n }\n\n // ==================== Private Event Handlers ====================\n\n /**\n * Accumulate duration from segments with deduplication.\n *\n * Dedup uses the same cache key strategy as bufferedTime so repeated\n * playlist snapshots do not double count previously seen segments.\n */\n private _accumulateDurationFromSegments(segments: Segment[]): void {\n let addedDuration = 0;\n\n for (const segment of segments) {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n if (this._durationSegmentKeys.has(key)) {\n continue;\n }\n this._durationSegmentKeys.add(key);\n addedDuration += segment.duration;\n }\n\n if (addedDuration > 0) {\n this._setDuration(this._duration + addedDuration);\n }\n }\n\n /**\n * Set duration and emit update event only when value changes.\n */\n private _setDuration(nextDuration: number): void {\n if (this._duration === nextDuration) {\n return;\n }\n\n this._duration = nextDuration;\n this.emit(HlsIOEvents.DURATION_UPDATED, this._duration);\n }\n\n /**\n * Set bufferedTime and emit update event only when value changes.\n */\n private _setBufferedTime(nextBufferedTime: number): void {\n if (this._bufferedTime === nextBufferedTime) {\n return;\n }\n\n this._bufferedTime = nextBufferedTime;\n this.emit(HlsIOEvents.BUFFERED_TIME_UPDATED, this._bufferedTime);\n }\n\n /**\n * Prune both dedup Sets to only contain keys from the current playlist,\n * preventing unbounded growth on long-running LIVE streams.\n * 修剪两个去重 Set,仅保留当前播放列表中的 key,防止长 LIVE 流无限增长。\n */\n private _pruneDedupSets(segments: Segment[]): void {\n const currentKeys = new Set(segments.map((s) => segmentCacheKey(s.uri, s.byteRange)));\n for (const key of this._durationSegmentKeys) {\n if (!currentKeys.has(key)) this._durationSegmentKeys.delete(key);\n }\n for (const key of this._downloadedSegmentKeys) {\n if (!currentKeys.has(key)) this._downloadedSegmentKeys.delete(key);\n }\n }\n\n /**\n * Handle a newly loaded (or refreshed) media playlist.\n *\n * This is the central wiring point: it creates (or updates) the\n * {@link SegmentManager} and feeds the current segment list into it. All\n * segment-level callbacks are bound here and bridged to public HlsIO events.\n *\n * 处理新加载(或刷新)的媒体播放列表。\n *\n * 这是核心连接点:它创建(或更新){@link SegmentManager} 并将当前分片列表\n * 注入其中。所有分片级别的回调在此绑定并桥接到公开的 HlsIO 事件。\n *\n * @param {MediaPlaylist} playlist - The parsed media playlist.\n * 解析后的媒体播放列表。\n */\n private _onPlaylistLoaded(playlist: MediaPlaylist): void {\n const previousType = this._currentPlaylist?.type;\n const isFirstLoad = !this._segmentManager;\n\n this._currentPlaylist = playlist;\n\n // Prune dedup sets to only contain keys present in the current playlist,\n // preventing unbounded growth in long-running LIVE streams.\n // 修剪去重集合,仅保留当前播放列表中存在的 key,防止长 LIVE 流无限增长。\n this._pruneDedupSets(playlist.segments);\n\n this._accumulateDurationFromSegments(playlist.segments);\n\n // Create segment manager if this is the first playlist load, otherwise\n // update the existing one in case the playlist type has changed.\n // 如果是首次加载播放列表则创建分片管理器,否则在播放列表类型变更时更新现有管理器。\n if (!this._segmentManager) {\n this._segmentManager = new SegmentManager(\n this._fetcher,\n this._decryptor,\n {\n playlistType: playlist.type,\n bufferAhead: this._config.bufferAhead,\n maxCacheSize: this._config.maxCacheSize,\n cachePolicy: this._config.cachePolicy,\n resolveRelativeUris: this._config.resolveRelativeUris,\n baseUrl: this._config.baseUrl || this._config.url,\n segmentRetryCount: this._config.segmentRetryCount,\n segmentRetryDelay: this._config.segmentRetryDelay,\n segmentRetryBackoff: this._config.segmentRetryBackoff,\n segmentTimeout: this._config.segmentTimeout || this._config.timeout,\n targetDuration: playlist.targetDuration,\n },\n {\n // Segment fetched & cached → public SEGMENT_READY event\n // 分片已获取并缓存 → 公开 SEGMENT_READY 事件\n onSegmentReady: (segment) => {\n const key = segmentCacheKey(segment.uri, segment.byteRange);\n if (!this._downloadedSegmentKeys.has(key)) {\n this._downloadedSegmentKeys.add(key);\n this._setBufferedTime(this._bufferedTime + segment.duration);\n }\n this.emit(HlsIOEvents.SEGMENT_READY, segment);\n },\n\n // Last segment of VOD fetched → public LAST_SEGMENT event + user callback\n // VOD 的最后一个分片已获取 → 公开 LAST_SEGMENT 事件 + 用户回调\n onLastSegment: (segment) => {\n this.emit(HlsIOEvents.LAST_SEGMENT, segment);\n if (this._config.onLastSegment) {\n this._config.onLastSegment();\n }\n },\n\n // EXT-X-DISCONTINUITY detected → public DISCONTINUITY event\n // 检测到 EXT-X-DISCONTINUITY → 公开 DISCONTINUITY 事件\n onDiscontinuity: (segment) => {\n this.emit(HlsIOEvents.DISCONTINUITY, segment);\n },\n\n // LL-HLS partial segment ready → public PART_SEGMENT event\n // LL-HLS 部分分片就绪 → 公开 PART_SEGMENT 事件\n onPartSegment: (part) => {\n this.emit(HlsIOEvents.PART_SEGMENT, part);\n },\n\n // EXT-X-MAP init segment loaded → public MAP_LOADED event\n // EXT-X-MAP 初始化段加载完成 → 公开 MAP_LOADED 事件\n onMapLoaded: (info) => {\n this.emit(HlsIOEvents.MAP_LOADED, info);\n },\n\n // Buffer running low (VOD mode) → public BUFFER_STALL event\n // 缓冲区不足(VOD 模式)→ 公开 BUFFER_STALL 事件\n onBufferStall: (info) => {\n this.emit(HlsIOEvents.BUFFER_STALL, info);\n },\n\n // Any segment-level error → public ERROR event\n // 任何分片级别的错误 → 公开 ERROR 事件\n onError: (error) => {\n if (error.type === \"TIMEOUT\") {\n this.emit(HlsIOEvents.TIMEOUT, error);\n }\n this.emit(HlsIOEvents.ERROR, error);\n },\n\n // Decryption starting → public DECRYPTING event\n // 解密开始 → 公开 DECRYPTING 事件\n onDecrypting: (segment) => {\n this.emit(HlsIOEvents.DECRYPTING, segment);\n },\n\n // Decryption complete → public DECRYPTED event\n // 解密完成 → 公开 DECRYPTED 事件\n onDecrypted: (segment) => {\n this.emit(HlsIOEvents.DECRYPTED, segment);\n },\n },\n this._config.debug,\n );\n } else {\n // Update segment manager config if playlist type changed (e.g. LIVE→VOD)\n // 如果播放列表类型变更,更新分片管理器配置(例如 LIVE→VOD)\n this._segmentManager.setPlaylistType(playlist.type);\n }\n\n // LIVE mode should fetch immediately when new segments are detected.\n // For subsequent LIVE playlist loads without new segments, avoid redundant\n // updateSegments calls. We still sync on first load and type transitions.\n // LIVE 模式应在检测到新分片时立即抓取。\n // 对于后续“无新分片”的 LIVE 刷新,避免重复调用 updateSegments。\n // 首次加载和类型切换时仍然同步。\n const shouldSyncSegments = isFirstLoad || playlist.type !== PlaylistType.LIVE || previousType !== playlist.type;\n if (shouldSyncSegments) {\n this._segmentManager.updateSegments(playlist.segments, playlist.partialSegments);\n }\n\n // Emit the public playlist-level events\n // 发出公开的播放列表级别事件\n this.emit(HlsIOEvents.PLAYLIST_LOADED, playlist);\n this.emit(HlsIOEvents.MANIFEST_PARSED, playlist);\n }\n\n /**\n * Handle new segments detected during a live playlist refresh.\n *\n * Emits the public `SEGMENTS_UPDATED` event and instructs the segment manager\n * to incorporate the latest segment list from the current playlist.\n *\n * 处理直播播放列表刷新期间检测到的新分片。\n *\n * 发出公开的 `SEGMENTS_UPDATED` 事件,并指示分片管理器纳入当前播放列表中的\n * 最新分片列表。\n *\n * @param {Segment[]} newSegments - The segments that have been newly added\n * since the last playlist refresh.\n * 自上次播放列表刷新以来新添加的分片。\n */\n private _onSegmentsUpdated(newSegments: Segment[], playlist: MediaPlaylist): void {\n this._currentPlaylist = playlist;\n this._pruneDedupSets(playlist.segments);\n this._accumulateDurationFromSegments(newSegments);\n\n // Notify consumers that fresh segments are available\n // 通知使用者有新分片可用\n this.emit(HlsIOEvents.SEGMENTS_UPDATED, newSegments);\n\n // Push the full segment list into the segment manager so its internal\n // buffer stays in sync with the playlist.\n // 将完整的分片列表推入分片管理器,使其内部缓冲区与播放列表保持同步。\n if (this._segmentManager) {\n this._segmentManager.updateSegments(playlist.segments, playlist.partialSegments);\n }\n }\n\n /**\n * Handle partial segments from an LL-HLS playlist refresh.\n *\n * Each partial segment is emitted individually via the `PART_SEGMENT` event\n * so consumers can push them to the MSE buffer as soon as they arrive.\n *\n * 处理 LL-HLS 播放列表刷新中的部分分片。\n *\n * 每个部分分片通过 `PART_SEGMENT` 事件单独发出,以便使用者在其到达后立即\n * 推送到 MSE 缓冲区。\n *\n * @param {PartialSegment[]} parts - The partial segments detected in the\n * latest playlist refresh.\n * 在最近一次播放列表刷新中检测到的部分分片。\n */\n private _onPartialSegments(parts: PartialSegment[]): void {\n // Emit each partial segment individually for low-latency delivery\n // 逐个发出每个部分分片,以实现低延迟交付\n for (const part of parts) {\n this.emit(HlsIOEvents.PART_SEGMENT, part);\n }\n }\n\n /**\n * Handle the playlist \"ended\" state (VOD mode only).\n *\n * Called when the playlist manager detects an `#EXT-X-ENDLIST` tag or when\n * all segments have been consumed. Stops the heartbeat since no further\n * live communication is needed, and emits the public `ENDED` event.\n *\n * 处理播放列表\"已结束\"状态(仅 VOD 模式)。\n *\n * 当播放列表管理器检测到 `#EXT-X-ENDLIST` 标签或所有分片已消费完毕时调用。\n * 停止心跳(不再需要直播通信),并发出公开的 `ENDED` 事件。\n */\n private _onEnded(): void {\n // No need to keep the heartbeat alive for VOD\n // VOD 不需要保持心跳\n this._fetcher.stopHeartbeat();\n\n // Notify consumers that playback has reached the end\n // 通知使用者播放已到达结尾\n this.emit(HlsIOEvents.ENDED);\n }\n\n // ==================== Helpers ====================\n\n /**\n * Type guard that distinguishes a {@link MediaPlaylist} from a {@link MasterPlaylist}.\n *\n * A media playlist has a `segments` array; a master playlist has a `variants` array.\n *\n * 类型守卫,区分 {@link MediaPlaylist} 和 {@link MasterPlaylist}。\n *\n * 媒体播放列表有 `segments` 数组;主播放列表有 `variants` 数组。\n *\n * @param {MediaPlaylist | MasterPlaylist} playlist - The parsed playlist to check.\n * 待检查的解析后播放列表。\n *\n * @returns {boolean} `true` if the playlist is a media playlist.\n */\n private _isMediaPlaylist(playlist: MediaPlaylist | MasterPlaylist): playlist is MediaPlaylist {\n return \"segments\" in playlist && Array.isArray((playlist as MediaPlaylist).segments);\n }\n}\n"],"names":["HLSTag","PlaylistType","SegmentFormat","EncryptionMethod","KeyFormat","DEFAULT_CONFIG","url","playlistType","bufferAhead","maxCacheSize","heartbeatInterval","playlistRefreshInterval","fetchOptions","resolveRelativeUris","baseUrl","debug","lowLatencyMode","partTargetDuration","onLastSegment","cachePolicy","followRedirectUrl","timeout","segmentRetryCount","segmentTimeout","segmentRetryDelay","segmentRetryBackoff","HlsIOEvents","_array_like_to_array","Fetcher","_heartbeatTimer","_abortControllers","Map","_logger","Logger","LEVEL","DEBUG","WARN","_fetchOptions","_extends","mode","credentials","_timeout","startHeartbeat","intervalMs","callback","stopHeartbeat","info","setInterval","controller","timeoutId","response","error","AbortController","setTimeout","abort","fetch","method","signal","clearTimeout","ok","Date","now","warn","status","message","clearInterval","isHeartbeatActive","options","fetchOptionsHeaders","headers","optionsHeaders","mergedOptions","effectiveTimeout","err","data","Headers","forEach","value","key","Object","prototype","hasOwnProperty","call","console","set","Error","statusText","arrayBuffer","contentType","get","undefined","contentLength","parseInt","delete","fetchSegment","byteRange","start","end","offset","length","cancelRequest","cancelAll","_create_for_of_iterator_helper_loose","clear","updateFetchOptions","createCorsFetcher","corsMode","customHeaders","cache","Decryptor","config","_keyCache","_pendingKeyFetches","_MAX_KEY_CACHE_SIZE","_config","useWebCrypto","needsDecryption","segment","keyFormat","IDENTITY","NONE","decrypt","rawData","keyInfo","uri","AES_128","_decryptAES128","SAMPLE_AES","_decryptSampleAES","prefetchKey","fetchPromise","keyProvider","has","then","res","size","oldestKey","keys","next","catch","iv","seq","i","cryptoKey","decrypted","k","byteLength","Uint8Array","sequence","Array","from","map","b","toString","padStart","join","crypto","subtle","importKey","name","_segment","setDebug","setLevel","updateConfig","createDecryptor","isComment","line","startsWith","isUri","trim","getTagName","colon","indexOf","substring","getTagAttributes","parseAttributes","attrStr","attrs","len","nameStart","valStart","parseResolution","parts","split","w","h","isNaN","width","height","parseHexBytes","hex","hexStr","test","bytes","parseExtInf","comma","duration","parseFloat","title","parseByteRange","at","parseEncryptionMethod","toUpperCase","parseKeyFormat","format","COMMON_ENCRYPTION","parseKeyAttributes","keyFormatVersions","parseMapAttributes","parseByteRangeText","text","parsePartAttributes","independent","gap","parseServerControl","canSkipUntil","canSkipDateTimes","canBlockReload","holdBack","partHoldBack","parsePreloadHint","type","byteRangeStart","byteRangeLength","parseRenditionReport","lastMSN","lastPart","parseDateRange","id","class","startDate","endDate","plannedDuration","endOnNext","attributes","parseStreamInf","bandwidth","averageBandwidth","codecs","resolution","frameRate","hdcpLevel","audio","video","subtitles","closedCaptions","score","isMasterPlaylist","includes","parseM3U8","playlist","parseMasterPlaylist","parseMediaPlaylist","lines","segments","partialSegments","renditionReports","version","LIVE","targetDuration","mediaSequence","discontinuitySequence","allowCache","independentSegments","startOffset","iFramesOnly","serverControl","preloadHint","currentKey","currentMap","currentDiscontinuity","currentGap","currentByteRange","currentProgramDateTime","currentDateRange","currentBitrate","totalDuration","segmentIndex","tagName","VOD","resolveAgainstBase","dateRange","resolvedUri","programDateTime","discontinuity","bitrate","push","part","hint","report","endTime","raw","variants","mediaRenditions","sessionData","sessionKeys","currentStreamAttrs","rendition","groupId","language","assocLanguage","default","autoselect","forced","instreamId","characteristics","channels","base","URL","protocol","host","baseDir","lastIndexOf","detectPlaylistType","isAbsoluteUri","isHttpUrl","isProtocolRelative","resolveUri","window","location","resolved","href","getBaseUrl","schemeEnd","lastSlash","buildQueryString","params","entries","encodeURIComponent","String","appendQueryParams","qs","separator","parseQueryParams","queryIndex","query","eq","decodeURIComponent","normalizeUrl","result","pop","guessFormatFromUri","clean","endsWith","stripQueryParams","segmentCacheKey","cleanUri","PlaylistManager","fetcher","callbacks","_pollTimer","_isDestroyed","_lastPlaylist","_lastETag","_lastModified","_segmentUpdateSeq","_pollCount","_isPollRequestInFlight","_lastPollHadChanges","_fetcher","_callbacks","_originalUrl","_playlistUrl","playlistUrl","variant","variantUrl","mediaResult","_loadPlaylist","_handleMediaPlaylist","destroy","_stopPolling","refresh","getLastPlaylist","parsed","lastError","TextDecoder","decode","subType","originalError","onError","isFirst","onPlaylistTypeDetected","newSegments","_detectNewSegments","onSegmentsUpdated","onPartialSegments","onPlaylistLoaded","_schedulePoll","onEnded","oldSegments","oldSeqSet","Set","s","filter","interval","Number","isFinite","baseInterval","Math","max","min","SegmentCache","maxSize","policy","_cache","_accessOrder","_maxSize","floor","_policy","_stats","hits","misses","evictions","entry","timestamp","_updateAccess","_evict","deleted","getStats","evictBeforeSequence","removed","getSegmentsInOrder","values","sort","a","firstKey","LookAheadPrefetchStrategy","getNextSegments","currentSequence","allSegments","sorted","startIndex","findIndex","slice","SequentialPrefetchStrategy","SegmentManager","decryptor","_segments","_currentSequence","_fetchQueue","_loadedSegmentKeys","_mapDataCache","_MAX_MAP_CACHE","_pendingPartials","_pendingFetchQueue","_isFetchLoopRunning","normalizedMaxCacheSize","normalizedBufferAheadRaw","normalizedBufferAhead","_decryptor","_prefetchStrategy","updateSegments","oldSegmentCount","onPartSegment","_handleLiveSegments","_handleVODSegments","find","_loadSegment","getNextSegment","getSortedSegments","idx","advanceToNext","getCachedSegment","getCacheStats","getLastSegment","setPlaylistType","td","acc","lastSegments","_enqueueFetch","oldestToKeep","toFetch","buffered","remaining","needed","onBufferStall","add","_processFetchQueue","shift","queueKey","mapData","keyErr","attempt","maxRetries","delay","backoff","decryptErr","lastSeq","_loadMap","segmentUri","Promise","resolve","onDecrypting","onDecrypted","onSegmentReady","onDiscontinuity","mapKey","cached","mapUrl","onMapLoaded","mapUri","reduce","sum","bufferedDuration","HlsIO","_playlistManager","_segmentManager","_currentPlaylist","_isRunning","_duration","_bufferedTime","_durationSegmentKeys","_downloadedSegmentKeys","deepmerge","all","clone","decryptorConfig","mediaPlaylist","_onPlaylistLoaded","_onSegmentsUpdated","_onPartialSegments","emit","PLAYLIST_TYPE_DETECTED","_onEnded","ERROR","_isMediaPlaylist","stop","_setDuration","_setBufferedTime","removeAllListeners","switchUrl","newUrl","advanceByTime","seconds","consumed","maxIterations","iterations","advance","setCorsMode","setKeyProvider","_accumulateDurationFromSegments","addedDuration","nextDuration","DURATION_UPDATED","nextBufferedTime","BUFFERED_TIME_UPDATED","_pruneDedupSets","currentKeys","previousType","isFirstLoad","SEGMENT_READY","LAST_SEGMENT","DISCONTINUITY","PART_SEGMENT","MAP_LOADED","BUFFER_STALL","TIMEOUT","DECRYPTING","DECRYPTED","shouldSyncSegments","PLAYLIST_LOADED","MANIFEST_PARSED","SEGMENTS_UPDATED","ENDED","isArray","bufferedTime","running","EventEmitter","VERSION","EVENTS"],"mappings":"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"}
|