feat: add codegen and refactor mkv models
This commit is contained in:
		
							parent
							
								
									f921819d2a
								
							
						
					
					
						commit
						0b681d4fd1
					
				| @ -1,135 +1,229 @@ | |||||||
| import { | import { | ||||||
|   type EbmlTagType, |   type EbmlClusterTagType, | ||||||
|   EbmlTagIdEnum, |   type EbmlCuePointTagType, | ||||||
|   EbmlTagPosition, |  | ||||||
|   type EbmlTracksTagType, |  | ||||||
|   type EbmlInfoTagType, |  | ||||||
|   type EbmlCuesTagType, |   type EbmlCuesTagType, | ||||||
|  |   type EbmlInfoTagType, | ||||||
|  |   type EbmlMasterTagType, | ||||||
|   type EbmlSeekHeadTagType, |   type EbmlSeekHeadTagType, | ||||||
|   type EbmlSegmentTagType, |   type EbmlSegmentTagType, | ||||||
|   type EbmlCuePointTagType, |   EbmlTagIdEnum, | ||||||
|   type EbmlMasterTagType, |   EbmlTagPosition, | ||||||
|  |   type EbmlTagType, | ||||||
|  |   type EbmlTrackEntryTagType, | ||||||
|  |   type EbmlTracksTagType, | ||||||
| } from 'konoebml'; | } from 'konoebml'; | ||||||
|  | import {convertEbmlTagToComponent, type InferType,} from './util'; | ||||||
|  | import {isEqual, maxBy} from 'lodash-es'; | ||||||
|  | import {ArkErrors, type Type} from 'arktype'; | ||||||
| import { | import { | ||||||
|   convertEbmlTagToModelShape, |   ClusterSchema, | ||||||
|   type InferType, |   type ClusterType, | ||||||
|   isTagIdPos, |   CuePointSchema, | ||||||
|   SEEK_ID_KAX_CUES, |   type CuePointType, | ||||||
|   SEEK_ID_KAX_INFO, |   type CueTrackPositionsType, | ||||||
|   SEEK_ID_KAX_TRACKS, |   InfoSchema, | ||||||
| } from './util'; |   type InfoType, | ||||||
| import { isEqual } from 'lodash-es'; |   SeekHeadSchema, | ||||||
| import type { Type } from 'arktype'; |   type SeekHeadType, | ||||||
| import { CuePointSchema, type CuePointType } from './schema'; |   TrackEntrySchema, | ||||||
|  |   type TrackEntryType | ||||||
|  | } from './schema'; | ||||||
| 
 | 
 | ||||||
| export abstract class StandardComponentSystem< | export const SEEK_ID_KAX_INFO = new Uint8Array([0x15, 0x49, 0xa9, 0x66]); | ||||||
|   E extends EbmlMasterTagType, | export const SEEK_ID_KAX_TRACKS = new Uint8Array([0x16, 0x54, 0xae, 0x6b]); | ||||||
|   S extends Type<any>, | export const SEEK_ID_KAX_CUES = new Uint8Array([0x1c, 0x53, 0xbb, 0x6b]); | ||||||
| > { |  | ||||||
|   abstract get schema(): S; |  | ||||||
| 
 | 
 | ||||||
|   componentFromTag(tag: E): InferType<S> { | export class SegmentSystem { | ||||||
|     const extracted = convertEbmlTagToModelShape(tag); |   startTag: EbmlSegmentTagType; | ||||||
|     return this.schema.assert(extracted) as InferType<S>; |   headTags: EbmlTagType[] = []; | ||||||
|   } | 
 | ||||||
| } |   cue: CueSystem; | ||||||
|  |   cluster: ClusterSystem; | ||||||
|  |   seek: SeekSystem; | ||||||
|  |   info: InfoSystem; | ||||||
|  |   track: TrackSystem; | ||||||
| 
 | 
 | ||||||
| export class EbmlSegment { |  | ||||||
|   startNode: EbmlSegmentTagType; |  | ||||||
|   seekHeadNode?: EbmlSeekHeadTagType; |  | ||||||
|   seekEntries: EbmlSeekEntry[]; |  | ||||||
|   tracksNode?: EbmlTracksTagType; |  | ||||||
|   infoNode?: EbmlInfoTagType; |  | ||||||
|   cuesNode?: EbmlCuesTagType; |  | ||||||
|   metaBuffer: EbmlTagType[] = []; |  | ||||||
|   metaOffsets: Map<number, EbmlTagType> = new Map(); |  | ||||||
| 
 | 
 | ||||||
|   constructor(startNode: EbmlSegmentTagType) { |   constructor(startNode: EbmlSegmentTagType) { | ||||||
|     this.startNode = startNode; |     this.startTag = startNode; | ||||||
|     this.seekEntries = []; |     this.cue = new CueSystem(this); | ||||||
|     this.metaBuffer = []; |     this.cluster = new ClusterSystem(this); | ||||||
|  |     this.seek = new SeekSystem(this); | ||||||
|  |     this.info = new InfoSystem(this); | ||||||
|  |     this.track = new TrackSystem(this); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get dataOffset() { |   get dataStartOffset() { | ||||||
|     return this.startNode.startOffset + this.startNode.headerLength; |     return this.startTag.startOffset + this.startTag.headerLength; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private addSeekHead(node: EbmlSeekHeadTagType) { |   get startOffset () { | ||||||
|     this.seekHeadNode = node; |     return this.startTag.startOffset; | ||||||
|     this.seekEntries = this.seekHeadNode.children |  | ||||||
|       .filter(isTagIdPos(EbmlTagIdEnum.Seek, EbmlTagPosition.End)) |  | ||||||
|       .map((c) => { |  | ||||||
|         const seekId = c.children.find( |  | ||||||
|           (item) => item.id === EbmlTagIdEnum.SeekID |  | ||||||
|         )?.data; |  | ||||||
|         const seekPosition = c.children.find( |  | ||||||
|           (item) => item.id === EbmlTagIdEnum.SeekPosition |  | ||||||
|         )?.data as number; |  | ||||||
|         if (seekId && seekPosition) { |  | ||||||
|           return { |  | ||||||
|             seekId, |  | ||||||
|             seekPosition, |  | ||||||
|           }; |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|       }) |  | ||||||
|       .filter((c): c is EbmlSeekEntry => !!c); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   findSeekPositionBySeekId(seekId: Uint8Array): number | undefined { |   completeHeads () { | ||||||
|     return this.seekEntries.find((c) => isEqual(c.seekId, seekId)) |     const infoTag = this.seek.seekTagBySeekId(SEEK_ID_KAX_INFO); | ||||||
|       ?.seekPosition; |     const tracksTag = this.seek.seekTagBySeekId(SEEK_ID_KAX_TRACKS); | ||||||
|  |     const cuesTag = this.seek.seekTagBySeekId(SEEK_ID_KAX_CUES); | ||||||
|  | 
 | ||||||
|  |     if (cuesTag?.id === EbmlTagIdEnum.Cues) { | ||||||
|  |       this.cue.prepareCuesWithTag(cuesTag) | ||||||
|  |     } | ||||||
|  |     if (infoTag?.id === EbmlTagIdEnum.Info) { | ||||||
|  |       this.info.prepareWithInfoTag(infoTag); | ||||||
|  |     } | ||||||
|  |     if (tracksTag?.id === EbmlTagIdEnum.Tracks) { | ||||||
|  |       this.track.prepareTracksWithTag(tracksTag); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|   findLocalNodeBySeekId(seekId: Uint8Array): EbmlTagType | undefined { |     return this; | ||||||
|     return this.findLocalNodeBySeekPosition( |  | ||||||
|       this.findSeekPositionBySeekId(seekId) |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   findLocalNodeBySeekPosition( |   scanHead (tag: EbmlTagType) { | ||||||
|     seekPosition: number | undefined |     if ( | ||||||
|  |       tag.id === EbmlTagIdEnum.SeekHead && | ||||||
|  |       tag.position === EbmlTagPosition.End | ||||||
|  |     ) { | ||||||
|  |       this.seek.addSeekHeadTag(tag); | ||||||
|  |     } | ||||||
|  |     this.headTags.push(tag); | ||||||
|  |     this.seek.memoTag(tag); | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class SegmentComponentSystemTrait<E extends EbmlMasterTagType, S extends Type<any>> { | ||||||
|  |   segment: SegmentSystem; | ||||||
|  | 
 | ||||||
|  |   get schema(): S { | ||||||
|  |     throw new Error("unimplemented!") | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   constructor(segment: SegmentSystem) { | ||||||
|  |     this.segment = segment; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   componentFromTag(tag: E): InferType<S> { | ||||||
|  |     const extracted = convertEbmlTagToComponent(tag); | ||||||
|  |     const result = this.schema(extracted); | ||||||
|  |     if (result instanceof ArkErrors) { | ||||||
|  |       const errors = result; | ||||||
|  |       console.error('Parse component from tag error:', tag.toDebugRecord(), errors.flatProblemsByPath) | ||||||
|  |       throw errors; | ||||||
|  |     } | ||||||
|  |     return result as InferType<S> | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class SeekSystem extends SegmentComponentSystemTrait<EbmlSeekHeadTagType, typeof SeekHeadSchema> { | ||||||
|  |   override get schema() { | ||||||
|  |     return SeekHeadSchema; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   seekHeads: SeekHeadType[] = []; | ||||||
|  |   offsetToTagMemo: Map<number, EbmlTagType> = new Map(); | ||||||
|  | 
 | ||||||
|  |   memoTag (tag: EbmlTagType) { | ||||||
|  |     this.offsetToTagMemo.set(tag.startOffset, tag); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   addSeekHeadTag (tag: EbmlSeekHeadTagType) { | ||||||
|  |     const seekHead = this.componentFromTag(tag); | ||||||
|  |     this.seekHeads.push(seekHead); | ||||||
|  |     return seekHead; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   offsetFromSeekPosition (position: number): number { | ||||||
|  |     return position + this.segment.startOffset; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   offsetFromSeekDataPosition (position: number) : number { | ||||||
|  |     return position + this.segment.dataStartOffset; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   seekTagByStartOffset ( | ||||||
|  |     startOffset: number | undefined | ||||||
|   ): EbmlTagType | undefined { |   ): EbmlTagType | undefined { | ||||||
|     return seekPosition! >= 0 |     return startOffset! >= 0 | ||||||
|       ? this.metaOffsets.get(seekPosition as number) |       ? this.offsetToTagMemo.get(startOffset!) | ||||||
|       : undefined; |       : undefined; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   markMetaEnd() { |   seekOffsetBySeekId(seekId: Uint8Array): number | undefined { | ||||||
|     this.infoNode = this.findLocalNodeBySeekId( |     const seekPosition = this.seekHeads[0]?.Seek?.find((c) => isEqual(c.SeekID, seekId)) | ||||||
|       SEEK_ID_KAX_INFO |       ?.SeekPosition; | ||||||
|     ) as EbmlInfoTagType; |     return seekPosition! >= 0 ? this.offsetFromSeekPosition(seekPosition!) : undefined; | ||||||
|     this.tracksNode = this.findLocalNodeBySeekId( |  | ||||||
|       SEEK_ID_KAX_TRACKS |  | ||||||
|     ) as EbmlTracksTagType; |  | ||||||
|     this.cuesNode = this.findLocalNodeBySeekId( |  | ||||||
|       SEEK_ID_KAX_CUES |  | ||||||
|     ) as EbmlCuesTagType; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   scanMeta(node: EbmlTagType): boolean { |   seekTagBySeekId(seekId: Uint8Array): EbmlTagType | undefined { | ||||||
|     if ( |     return this.seekTagByStartOffset( | ||||||
|       node.id === EbmlTagIdEnum.SeekHead && |       this.seekOffsetBySeekId(seekId) | ||||||
|       node.position === EbmlTagPosition.End |     ); | ||||||
|     ) { |  | ||||||
|       this.addSeekHead(node); |  | ||||||
|     } |  | ||||||
|     this.metaBuffer.push(node); |  | ||||||
|     this.metaOffsets.set(node.startOffset - this.dataOffset, node); |  | ||||||
|     return true; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class CuesSystem extends StandardComponentSystem< | export class InfoSystem extends SegmentComponentSystemTrait<EbmlInfoTagType, typeof InfoSchema> { | ||||||
|  |   override get schema() { | ||||||
|  |     return InfoSchema; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   info!: InfoType; | ||||||
|  | 
 | ||||||
|  |   prepareWithInfoTag (tag: EbmlInfoTagType) { | ||||||
|  |     this.info = this.componentFromTag(tag); | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class ClusterSystem extends SegmentComponentSystemTrait<EbmlClusterTagType, typeof ClusterSchema> { | ||||||
|  |   override get schema() { | ||||||
|  |     return ClusterSchema | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   clustersBuffer: ClusterType[] = []; | ||||||
|  | 
 | ||||||
|  |   addClusterWithTag (tag: EbmlClusterTagType): ClusterType { | ||||||
|  |     const cluster = this.componentFromTag(tag); | ||||||
|  |     this.clustersBuffer.push(cluster); | ||||||
|  |     return cluster; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class TrackSystem extends SegmentComponentSystemTrait<EbmlTrackEntryTagType, typeof TrackEntrySchema> { | ||||||
|  |   override get schema() { | ||||||
|  |     return TrackEntrySchema; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tracks = new Map<number, TrackEntryType>(); | ||||||
|  | 
 | ||||||
|  |   prepareTracksWithTag (tag: EbmlTracksTagType) { | ||||||
|  |     this.tracks.clear(); | ||||||
|  |     for (const c of tag.children) { | ||||||
|  |       if (c.id === EbmlTagIdEnum.TrackEntry) { | ||||||
|  |         const trackEntry = this.componentFromTag(c); | ||||||
|  |         this.tracks.set(trackEntry.TrackNumber, trackEntry); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class CueSystem extends SegmentComponentSystemTrait< | ||||||
|   EbmlCuePointTagType, |   EbmlCuePointTagType, | ||||||
|   typeof CuePointSchema |   typeof CuePointSchema | ||||||
| > { | > { | ||||||
|   schema = CuePointSchema; |   override get schema () { | ||||||
|   cues: CuePointType[]; |     return CuePointSchema | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|   constructor(cues: CuePointType[]) { |   cues: CuePointType[] = []; | ||||||
|     super(); | 
 | ||||||
|     this.cues = cues; | 
 | ||||||
|  |   prepareCuesWithTag (tag: EbmlCuesTagType) { | ||||||
|  |     this.cues = tag.children | ||||||
|  |       .filter(c => c.id === EbmlTagIdEnum.CuePoint) | ||||||
|  |       .map(this.componentFromTag.bind(this)); | ||||||
|  |     return this; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   findClosestCue(seekTime: number): CuePointType | undefined { |   findClosestCue(seekTime: number): CuePointType | undefined { | ||||||
| @ -170,4 +264,19 @@ export class CuesSystem extends StandardComponentSystem< | |||||||
|       ? before |       ? before | ||||||
|       : after; |       : after; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   getCueTrackPositions (cuePoint: CuePointType, track?: number): CueTrackPositionsType { | ||||||
|  |     let cueTrackPositions: CueTrackPositionsType | undefined; | ||||||
|  |     if (track! >= 0) { | ||||||
|  |       cueTrackPositions = cuePoint.CueTrackPositions.find(c => c.CueTrack === track); | ||||||
|  |     } | ||||||
|  |    if (!cueTrackPositions) { | ||||||
|  |      cueTrackPositions =  maxBy(cuePoint.CueTrackPositions, c => c.CueClusterPosition)!; | ||||||
|  |    } | ||||||
|  |    return cueTrackPositions; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get prepared (): boolean { | ||||||
|  |     return this.cues.length > 0; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -26,8 +26,9 @@ import { | |||||||
|   withLatestFrom, |   withLatestFrom, | ||||||
| } from 'rxjs'; | } from 'rxjs'; | ||||||
| import { createRangedStream } from '@/fetch'; | import { createRangedStream } from '@/fetch'; | ||||||
| import { EbmlSegment, Cluster, SEEK_ID_KAX_CUES, CuesSystem } from './model'; | import { SegmentSystem, SEEK_ID_KAX_CUES, type CueSystem } from './model'; | ||||||
| import { isTagIdPos } from './util'; | import { isTagIdPos } from './util'; | ||||||
|  | import type { ClusterType } from "./schema"; | ||||||
| 
 | 
 | ||||||
| export function createRangedEbmlStream( | export function createRangedEbmlStream( | ||||||
|   url: string, |   url: string, | ||||||
| @ -124,12 +125,14 @@ export function createEbmlController(src: string) { | |||||||
| 
 | 
 | ||||||
|       const segments$ = segmentStart$.pipe( |       const segments$ = segmentStart$.pipe( | ||||||
|         map((startTag) => { |         map((startTag) => { | ||||||
|           const segment = new EbmlSegment(startTag); |           const segment = new SegmentSystem(startTag); | ||||||
|  |           const clusterSystem = segment.cluster; | ||||||
|  |           const seekSystem = segment.seek; | ||||||
| 
 | 
 | ||||||
|           const continuousReusedCluster$ = ebml$.pipe( |           const continuousReusedCluster$ = ebml$.pipe( | ||||||
|             filter(isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End)), |             filter(isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End)), | ||||||
|             filter((s) => s.id === EbmlTagIdEnum.Cluster), |             filter((s) => s.id === EbmlTagIdEnum.Cluster), | ||||||
|             map(Cluster.fromTag.bind(Cluster)) |             map(clusterSystem.addClusterWithTag.bind(clusterSystem)) | ||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
|           const segmentEnd$ = ebml$.pipe( |           const segmentEnd$ = ebml$.pipe( | ||||||
| @ -154,33 +157,27 @@ export function createEbmlController(src: string) { | |||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
|           const withMeta$ = meta$.pipe( |           const withMeta$ = meta$.pipe( | ||||||
|             reduce((segment, meta) => { |             reduce((segment, meta) => segment.scanHead(meta), segment), | ||||||
|               segment.scanMeta(meta); |             map(segment.completeHeads.bind(segment)), | ||||||
|               return segment; |  | ||||||
|             }, segment), |  | ||||||
|             map((segment) => { |  | ||||||
|               segment.markMetaEnd(); |  | ||||||
|               return segment; |  | ||||||
|             }), |  | ||||||
|             take(1), |             take(1), | ||||||
|             shareReplay(1) |             shareReplay(1) | ||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
|           const withRemoteCues$ = withMeta$.pipe( |           const withRemoteCues$ = withMeta$.pipe( | ||||||
|             switchMap((s) => { |             switchMap((s) => { | ||||||
|               if (s.cuesNode) { |               const cueSystem = s.cue; | ||||||
|  |               const seekSystem = s.seek; | ||||||
|  |               if (cueSystem.prepared) { | ||||||
|                 return EMPTY; |                 return EMPTY; | ||||||
|               } |               } | ||||||
|               const cuesStartOffset = |               const remoteCuesTagStartOffset = seekSystem.seekOffsetBySeekId(SEEK_ID_KAX_CUES); | ||||||
|                 s.dataOffset + |               if (remoteCuesTagStartOffset! >= 0) { | ||||||
|                 (s.findSeekPositionBySeekId(SEEK_ID_KAX_CUES) ?? Number.NaN); |                 return createRangedEbmlStream(src, remoteCuesTagStartOffset).pipe( | ||||||
|               if (cuesStartOffset >= 0) { |  | ||||||
|                 return createRangedEbmlStream(src, cuesStartOffset).pipe( |  | ||||||
|                   switchMap((req) => req.ebml$), |                   switchMap((req) => req.ebml$), | ||||||
|                   filter(isTagIdPos(EbmlTagIdEnum.Cues, EbmlTagPosition.End)), |                   filter(isTagIdPos(EbmlTagIdEnum.Cues, EbmlTagPosition.End)), | ||||||
|                   withLatestFrom(withMeta$), |                   withLatestFrom(withMeta$), | ||||||
|                   map(([cues, withMeta]) => { |                   map(([cues, withMeta]) => { | ||||||
|                     withMeta.cuesNode = cues; |                     withMeta.cue.prepareCuesWithTag(cues); | ||||||
|                     return withMeta; |                     return withMeta; | ||||||
|                   }) |                   }) | ||||||
|                 ); |                 ); | ||||||
| @ -192,12 +189,7 @@ export function createEbmlController(src: string) { | |||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
|           const withLocalCues$ = withMeta$.pipe( |           const withLocalCues$ = withMeta$.pipe( | ||||||
|             switchMap((s) => { |             switchMap((s) => s.cue.prepared ? of(s) : EMPTY), | ||||||
|               if (s.cuesNode) { |  | ||||||
|                 return of(s); |  | ||||||
|               } |  | ||||||
|               return EMPTY; |  | ||||||
|             }), |  | ||||||
|             shareReplay(1) |             shareReplay(1) | ||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
| @ -210,7 +202,7 @@ export function createEbmlController(src: string) { | |||||||
|             switchMap((empty) => (empty ? withMeta$ : EMPTY)) |             switchMap((empty) => (empty ? withMeta$ : EMPTY)) | ||||||
|           ); |           ); | ||||||
| 
 | 
 | ||||||
|           const seekWithoutCues = (seekTime: number): Observable<Cluster> => { |           const seekWithoutCues = (seekTime: number): Observable<ClusterType> => { | ||||||
|             const cluster$ = continuousReusedCluster$.pipe( |             const cluster$ = continuousReusedCluster$.pipe( | ||||||
|               isEmpty(), |               isEmpty(), | ||||||
|               switchMap((empty) => { |               switchMap((empty) => { | ||||||
| @ -223,7 +215,7 @@ export function createEbmlController(src: string) { | |||||||
|                       filter( |                       filter( | ||||||
|                         isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End) |                         isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End) | ||||||
|                       ), |                       ), | ||||||
|                       map(Cluster.fromTag.bind(Cluster)) |                       map((tag) => clusterSystem.addClusterWithTag(tag)) | ||||||
|                     ) |                     ) | ||||||
|                   : continuousReusedCluster$; |                   : continuousReusedCluster$; | ||||||
|               }) |               }) | ||||||
| @ -236,23 +228,23 @@ export function createEbmlController(src: string) { | |||||||
|               scan( |               scan( | ||||||
|                 (prev, curr) => |                 (prev, curr) => | ||||||
|                   [prev?.[1], curr] as [ |                   [prev?.[1], curr] as [ | ||||||
|                     Cluster | undefined, |                     ClusterType | undefined, | ||||||
|                     Cluster | undefined, |                     ClusterType | undefined, | ||||||
|                   ], |                   ], | ||||||
|                 [undefined, undefined] as [ |                 [undefined, undefined] as [ | ||||||
|                   Cluster | undefined, |                   ClusterType | undefined, | ||||||
|                   Cluster | undefined, |                   ClusterType | undefined, | ||||||
|                 ] |                 ] | ||||||
|               ), |               ), | ||||||
|               filter((c) => c[1]?.timestamp! > seekTime), |               filter((c) => c[1]?.Timestamp! > seekTime), | ||||||
|               map((c) => c[0] ?? c[1]!) |               map((c) => c[0] ?? c[1]!) | ||||||
|             ); |             ); | ||||||
|           }; |           }; | ||||||
| 
 | 
 | ||||||
|           const seekWithCues = ( |           const seekWithCues = ( | ||||||
|             cues: CuesSystem, |             cues: CueSystem, | ||||||
|             seekTime: number |             seekTime: number | ||||||
|           ): Observable<Cluster> => { |           ): Observable<ClusterType> => { | ||||||
|             if (seekTime === 0) { |             if (seekTime === 0) { | ||||||
|               return seekWithoutCues(seekTime); |               return seekWithoutCues(seekTime); | ||||||
|             } |             } | ||||||
| @ -265,29 +257,29 @@ export function createEbmlController(src: string) { | |||||||
| 
 | 
 | ||||||
|             return createRangedEbmlStream( |             return createRangedEbmlStream( | ||||||
|               src, |               src, | ||||||
|               cuePoint.position + segment.dataOffset |               seekSystem.offsetFromSeekDataPosition(cues.getCueTrackPositions(cuePoint).CueClusterPosition) | ||||||
|             ).pipe( |             ).pipe( | ||||||
|               switchMap((req) => req.ebml$), |               switchMap((req) => req.ebml$), | ||||||
|               filter(isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End)), |               filter(isTagIdPos(EbmlTagIdEnum.Cluster, EbmlTagPosition.End)), | ||||||
|               map(Cluster.fromTag.bind(Cluster)) |               map(clusterSystem.addClusterWithTag.bind(clusterSystem)) | ||||||
|             ); |             ); | ||||||
|           }; |           }; | ||||||
| 
 | 
 | ||||||
|           const seek = (seekTime: number): Observable<Cluster> => { |           const seek = (seekTime: number): Observable<ClusterType> => { | ||||||
|             if (seekTime === 0) { |             if (seekTime === 0) { | ||||||
|               const subscripton = merge(withCues$, withoutCues$).subscribe(); |               const subscription = merge(withCues$, withoutCues$).subscribe(); | ||||||
| 
 | 
 | ||||||
|               // if seekTime equals to 0 at start, reuse the initialize stream
 |               // if seekTime equals to 0 at start, reuse the initialize stream
 | ||||||
|               return seekWithoutCues(seekTime).pipe( |               return seekWithoutCues(seekTime).pipe( | ||||||
|                 finalize(() => { |                 finalize(() => { | ||||||
|                   subscripton.unsubscribe(); |                   subscription.unsubscribe(); | ||||||
|                 }) |                 }) | ||||||
|               ); |               ); | ||||||
|             } |             } | ||||||
|             return merge( |             return merge( | ||||||
|               withCues$.pipe( |               withCues$.pipe( | ||||||
|                 switchMap((s) => |                 switchMap((s) => | ||||||
|                   seekWithCues(CuesSystem.fromTag(s.cuesNode!), seekTime) |                   seekWithCues(s.cue, seekTime) | ||||||
|                 ) |                 ) | ||||||
|               ), |               ), | ||||||
|               withoutCues$.pipe(switchMap((_) => seekWithoutCues(seekTime))) |               withoutCues$.pipe(switchMap((_) => seekWithoutCues(seekTime))) | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ export const SeekSchema = type({ | |||||||
| export type SeekType = typeof SeekSchema.infer; | export type SeekType = typeof SeekSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const SeekHeadSchema = type({ | export const SeekHeadSchema = type({ | ||||||
|   Seek: SeekSchema.array(), |   Seek: SeekSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type SeekHeadType = typeof SeekHeadSchema.infer; | export type SeekHeadType = typeof SeekHeadSchema.infer; | ||||||
| @ -79,7 +79,7 @@ export const BlockMoreSchema = type({ | |||||||
| export type BlockMoreType = typeof BlockMoreSchema.infer; | export type BlockMoreType = typeof BlockMoreSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const BlockAdditionsSchema = type({ | export const BlockAdditionsSchema = type({ | ||||||
|   BlockMore: BlockMoreSchema.array(), |   BlockMore: BlockMoreSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type BlockAdditionsType = typeof BlockAdditionsSchema.infer; | export type BlockAdditionsType = typeof BlockAdditionsSchema.infer; | ||||||
| @ -198,12 +198,9 @@ export enum MatrixCoefficientsRestrictionEnum { | |||||||
|   CHROMA_DERIVED_CONSTANT_LUMINANCE = 13, |   CHROMA_DERIVED_CONSTANT_LUMINANCE = 13, | ||||||
|   // ITU-R BT.2100-0
 |   // ITU-R BT.2100-0
 | ||||||
|   ITU_R_BT_2100_0 = 14, |   ITU_R_BT_2100_0 = 14, | ||||||
| } | }; | ||||||
| export const MatrixCoefficientsRestriction = type( | export const MatrixCoefficientsRestriction = type('0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14'); | ||||||
|   '0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14' | export type MatrixCoefficientsRestrictionType = typeof MatrixCoefficientsRestriction.infer; | ||||||
| ); |  | ||||||
| export type MatrixCoefficientsRestrictionType = |  | ||||||
|   typeof MatrixCoefficientsRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ChromaSitingHorzRestrictionEnum { | export enum ChromaSitingHorzRestrictionEnum { | ||||||
|   // unspecified
 |   // unspecified
 | ||||||
| @ -212,10 +209,9 @@ export enum ChromaSitingHorzRestrictionEnum { | |||||||
|   LEFT_COLLOCATED = 1, |   LEFT_COLLOCATED = 1, | ||||||
|   // half
 |   // half
 | ||||||
|   HALF = 2, |   HALF = 2, | ||||||
| } | }; | ||||||
| export const ChromaSitingHorzRestriction = type('0 | 1 | 2'); | export const ChromaSitingHorzRestriction = type('0 | 1 | 2'); | ||||||
| export type ChromaSitingHorzRestrictionType = | export type ChromaSitingHorzRestrictionType = typeof ChromaSitingHorzRestriction.infer; | ||||||
|   typeof ChromaSitingHorzRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ChromaSitingVertRestrictionEnum { | export enum ChromaSitingVertRestrictionEnum { | ||||||
|   // unspecified
 |   // unspecified
 | ||||||
| @ -224,10 +220,9 @@ export enum ChromaSitingVertRestrictionEnum { | |||||||
|   TOP_COLLOCATED = 1, |   TOP_COLLOCATED = 1, | ||||||
|   // half
 |   // half
 | ||||||
|   HALF = 2, |   HALF = 2, | ||||||
| } | }; | ||||||
| export const ChromaSitingVertRestriction = type('0 | 1 | 2'); | export const ChromaSitingVertRestriction = type('0 | 1 | 2'); | ||||||
| export type ChromaSitingVertRestrictionType = | export type ChromaSitingVertRestrictionType = typeof ChromaSitingVertRestriction.infer; | ||||||
|   typeof ChromaSitingVertRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum RangeRestrictionEnum { | export enum RangeRestrictionEnum { | ||||||
|   // unspecified
 |   // unspecified
 | ||||||
| @ -238,7 +233,7 @@ export enum RangeRestrictionEnum { | |||||||
|   FULL_RANGE_NO_CLIPPING = 2, |   FULL_RANGE_NO_CLIPPING = 2, | ||||||
|   // defined by MatrixCoefficients / TransferCharacteristics
 |   // defined by MatrixCoefficients / TransferCharacteristics
 | ||||||
|   DEFINED_BY_MATRIX_COEFFICIENTS_TRANSFER_CHARACTERISTICS = 3, |   DEFINED_BY_MATRIX_COEFFICIENTS_TRANSFER_CHARACTERISTICS = 3, | ||||||
| } | }; | ||||||
| export const RangeRestriction = type('0 | 1 | 2 | 3'); | export const RangeRestriction = type('0 | 1 | 2 | 3'); | ||||||
| export type RangeRestrictionType = typeof RangeRestriction.infer; | export type RangeRestrictionType = typeof RangeRestriction.infer; | ||||||
| 
 | 
 | ||||||
| @ -281,12 +276,9 @@ export enum TransferCharacteristicsRestrictionEnum { | |||||||
|   SMPTE_ST_428_1 = 17, |   SMPTE_ST_428_1 = 17, | ||||||
|   // ARIB STD-B67 (HLG)
 |   // ARIB STD-B67 (HLG)
 | ||||||
|   ARIB_STD_B67_HLG = 18, |   ARIB_STD_B67_HLG = 18, | ||||||
| } | }; | ||||||
| export const TransferCharacteristicsRestriction = type( | export const TransferCharacteristicsRestriction = type('0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18'); | ||||||
|   '0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18' | export type TransferCharacteristicsRestrictionType = typeof TransferCharacteristicsRestriction.infer; | ||||||
| ); |  | ||||||
| export type TransferCharacteristicsRestrictionType = |  | ||||||
|   typeof TransferCharacteristicsRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum PrimariesRestrictionEnum { | export enum PrimariesRestrictionEnum { | ||||||
|   // reserved
 |   // reserved
 | ||||||
| @ -317,10 +309,8 @@ export enum PrimariesRestrictionEnum { | |||||||
|   SMPTE_EG_432_2 = 12, |   SMPTE_EG_432_2 = 12, | ||||||
|   // EBU Tech. 3213-E - JEDEC P22 phosphors
 |   // EBU Tech. 3213-E - JEDEC P22 phosphors
 | ||||||
|   EBU_TECH_3213_E_JEDEC_P22_PHOSPHORS = 22, |   EBU_TECH_3213_E_JEDEC_P22_PHOSPHORS = 22, | ||||||
| } | }; | ||||||
| export const PrimariesRestriction = type( | export const PrimariesRestriction = type('0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 22'); | ||||||
|   '0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 22' |  | ||||||
| ); |  | ||||||
| export type PrimariesRestrictionType = typeof PrimariesRestriction.infer; | export type PrimariesRestrictionType = typeof PrimariesRestriction.infer; | ||||||
| 
 | 
 | ||||||
| export const ColourSchema = type({ | export const ColourSchema = type({ | ||||||
| @ -351,10 +341,9 @@ export enum ProjectionTypeRestrictionEnum { | |||||||
|   CUBEMAP = 2, |   CUBEMAP = 2, | ||||||
|   // mesh
 |   // mesh
 | ||||||
|   MESH = 3, |   MESH = 3, | ||||||
| } | }; | ||||||
| export const ProjectionTypeRestriction = type('0 | 1 | 2 | 3'); | export const ProjectionTypeRestriction = type('0 | 1 | 2 | 3'); | ||||||
| export type ProjectionTypeRestrictionType = | export type ProjectionTypeRestrictionType = typeof ProjectionTypeRestriction.infer; | ||||||
|   typeof ProjectionTypeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const ProjectionSchema = type({ | export const ProjectionSchema = type({ | ||||||
|   ProjectionType: ProjectionTypeRestriction.default(0), |   ProjectionType: ProjectionTypeRestriction.default(0), | ||||||
| @ -373,10 +362,9 @@ export enum FlagInterlacedRestrictionEnum { | |||||||
|   INTERLACED = 1, |   INTERLACED = 1, | ||||||
|   // progressive
 |   // progressive
 | ||||||
|   PROGRESSIVE = 2, |   PROGRESSIVE = 2, | ||||||
| } | }; | ||||||
| export const FlagInterlacedRestriction = type('0 | 1 | 2'); | export const FlagInterlacedRestriction = type('0 | 1 | 2'); | ||||||
| export type FlagInterlacedRestrictionType = | export type FlagInterlacedRestrictionType = typeof FlagInterlacedRestriction.infer; | ||||||
|   typeof FlagInterlacedRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum FieldOrderRestrictionEnum { | export enum FieldOrderRestrictionEnum { | ||||||
|   // progressive
 |   // progressive
 | ||||||
| @ -391,7 +379,7 @@ export enum FieldOrderRestrictionEnum { | |||||||
|   TFF_INTERLEAVED = 9, |   TFF_INTERLEAVED = 9, | ||||||
|   // bff (interleaved)
 |   // bff (interleaved)
 | ||||||
|   BFF_INTERLEAVED = 14, |   BFF_INTERLEAVED = 14, | ||||||
| } | }; | ||||||
| export const FieldOrderRestriction = type('0 | 1 | 2 | 6 | 9 | 14'); | export const FieldOrderRestriction = type('0 | 1 | 2 | 6 | 9 | 14'); | ||||||
| export type FieldOrderRestrictionType = typeof FieldOrderRestriction.infer; | export type FieldOrderRestrictionType = typeof FieldOrderRestriction.infer; | ||||||
| 
 | 
 | ||||||
| @ -426,10 +414,8 @@ export enum StereoModeRestrictionEnum { | |||||||
|   BOTH_EYES_LACED_IN_ONE_BLOCK_LEFT_EYE_IS_FIRST = 13, |   BOTH_EYES_LACED_IN_ONE_BLOCK_LEFT_EYE_IS_FIRST = 13, | ||||||
|   // both eyes laced in one Block (right eye is first)
 |   // both eyes laced in one Block (right eye is first)
 | ||||||
|   BOTH_EYES_LACED_IN_ONE_BLOCK_RIGHT_EYE_IS_FIRST = 14, |   BOTH_EYES_LACED_IN_ONE_BLOCK_RIGHT_EYE_IS_FIRST = 14, | ||||||
| } | }; | ||||||
| export const StereoModeRestriction = type( | export const StereoModeRestriction = type('0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14'); | ||||||
|   '0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14' |  | ||||||
| ); |  | ||||||
| export type StereoModeRestrictionType = typeof StereoModeRestriction.infer; | export type StereoModeRestrictionType = typeof StereoModeRestriction.infer; | ||||||
| 
 | 
 | ||||||
| export enum AlphaModeRestrictionEnum { | export enum AlphaModeRestrictionEnum { | ||||||
| @ -437,7 +423,7 @@ export enum AlphaModeRestrictionEnum { | |||||||
|   NONE = 0, |   NONE = 0, | ||||||
|   // present
 |   // present
 | ||||||
|   PRESENT = 1, |   PRESENT = 1, | ||||||
| } | }; | ||||||
| export const AlphaModeRestriction = type('0 | 1'); | export const AlphaModeRestriction = type('0 | 1'); | ||||||
| export type AlphaModeRestrictionType = typeof AlphaModeRestriction.infer; | export type AlphaModeRestrictionType = typeof AlphaModeRestriction.infer; | ||||||
| 
 | 
 | ||||||
| @ -450,10 +436,9 @@ export enum OldStereoModeRestrictionEnum { | |||||||
|   LEFT_EYE = 2, |   LEFT_EYE = 2, | ||||||
|   // both eyes
 |   // both eyes
 | ||||||
|   BOTH_EYES = 3, |   BOTH_EYES = 3, | ||||||
| } | }; | ||||||
| export const OldStereoModeRestriction = type('0 | 1 | 2 | 3'); | export const OldStereoModeRestriction = type('0 | 1 | 2 | 3'); | ||||||
| export type OldStereoModeRestrictionType = | export type OldStereoModeRestrictionType = typeof OldStereoModeRestriction.infer; | ||||||
|   typeof OldStereoModeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum DisplayUnitRestrictionEnum { | export enum DisplayUnitRestrictionEnum { | ||||||
|   // pixels
 |   // pixels
 | ||||||
| @ -466,7 +451,7 @@ export enum DisplayUnitRestrictionEnum { | |||||||
|   DISPLAY_ASPECT_RATIO = 3, |   DISPLAY_ASPECT_RATIO = 3, | ||||||
|   // unknown
 |   // unknown
 | ||||||
|   UNKNOWN = 4, |   UNKNOWN = 4, | ||||||
| } | }; | ||||||
| export const DisplayUnitRestriction = type('0 | 1 | 2 | 3 | 4'); | export const DisplayUnitRestriction = type('0 | 1 | 2 | 3 | 4'); | ||||||
| export type DisplayUnitRestrictionType = typeof DisplayUnitRestriction.infer; | export type DisplayUnitRestrictionType = typeof DisplayUnitRestriction.infer; | ||||||
| 
 | 
 | ||||||
| @ -477,10 +462,9 @@ export enum AspectRatioTypeRestrictionEnum { | |||||||
|   KEEP_ASPECT_RATIO = 1, |   KEEP_ASPECT_RATIO = 1, | ||||||
|   // fixed
 |   // fixed
 | ||||||
|   FIXED = 2, |   FIXED = 2, | ||||||
| } | }; | ||||||
| export const AspectRatioTypeRestriction = type('0 | 1 | 2'); | export const AspectRatioTypeRestriction = type('0 | 1 | 2'); | ||||||
| export type AspectRatioTypeRestrictionType = | export type AspectRatioTypeRestrictionType = typeof AspectRatioTypeRestriction.infer; | ||||||
|   typeof AspectRatioTypeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const VideoSchema = type({ | export const VideoSchema = type({ | ||||||
|   FlagInterlaced: FlagInterlacedRestriction.default(0), |   FlagInterlaced: FlagInterlacedRestriction.default(0), | ||||||
| @ -534,10 +518,8 @@ export enum EmphasisRestrictionEnum { | |||||||
|   PHONO_LONDON = 15, |   PHONO_LONDON = 15, | ||||||
|   // Phono NARTB
 |   // Phono NARTB
 | ||||||
|   PHONO_NARTB = 16, |   PHONO_NARTB = 16, | ||||||
| } | }; | ||||||
| export const EmphasisRestriction = type( | export const EmphasisRestriction = type('0 | 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16'); | ||||||
|   '0 | 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16' |  | ||||||
| ); |  | ||||||
| export type EmphasisRestrictionType = typeof EmphasisRestriction.infer; | export type EmphasisRestrictionType = typeof EmphasisRestriction.infer; | ||||||
| 
 | 
 | ||||||
| export const AudioSchema = type({ | export const AudioSchema = type({ | ||||||
| @ -558,10 +540,9 @@ export enum TrackPlaneTypeRestrictionEnum { | |||||||
|   RIGHT_EYE = 1, |   RIGHT_EYE = 1, | ||||||
|   // background
 |   // background
 | ||||||
|   BACKGROUND = 2, |   BACKGROUND = 2, | ||||||
| } | }; | ||||||
| export const TrackPlaneTypeRestriction = type('0 | 1 | 2'); | export const TrackPlaneTypeRestriction = type('0 | 1 | 2'); | ||||||
| export type TrackPlaneTypeRestrictionType = | export type TrackPlaneTypeRestrictionType = typeof TrackPlaneTypeRestriction.infer; | ||||||
|   typeof TrackPlaneTypeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const TrackPlaneSchema = type({ | export const TrackPlaneSchema = type({ | ||||||
|   TrackPlaneUID: type.number, |   TrackPlaneUID: type.number, | ||||||
| @ -571,13 +552,13 @@ export const TrackPlaneSchema = type({ | |||||||
| export type TrackPlaneType = typeof TrackPlaneSchema.infer; | export type TrackPlaneType = typeof TrackPlaneSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const TrackCombinePlanesSchema = type({ | export const TrackCombinePlanesSchema = type({ | ||||||
|   TrackPlane: TrackPlaneSchema.array(), |   TrackPlane: TrackPlaneSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type TrackCombinePlanesType = typeof TrackCombinePlanesSchema.infer; | export type TrackCombinePlanesType = typeof TrackCombinePlanesSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const TrackJoinBlocksSchema = type({ | export const TrackJoinBlocksSchema = type({ | ||||||
|   TrackJoinUID: type.number.array(), |   TrackJoinUID: type.number.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type TrackJoinBlocksType = typeof TrackJoinBlocksSchema.infer; | export type TrackJoinBlocksType = typeof TrackJoinBlocksSchema.infer; | ||||||
| @ -598,10 +579,9 @@ export enum ContentCompAlgoRestrictionEnum { | |||||||
|   LZO1X = 2, |   LZO1X = 2, | ||||||
|   // Header Stripping
 |   // Header Stripping
 | ||||||
|   HEADER_STRIPPING = 3, |   HEADER_STRIPPING = 3, | ||||||
| } | }; | ||||||
| export const ContentCompAlgoRestriction = type('0 | 1 | 2 | 3'); | export const ContentCompAlgoRestriction = type('0 | 1 | 2 | 3'); | ||||||
| export type ContentCompAlgoRestrictionType = | export type ContentCompAlgoRestrictionType = typeof ContentCompAlgoRestriction.infer; | ||||||
|   typeof ContentCompAlgoRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const ContentCompressionSchema = type({ | export const ContentCompressionSchema = type({ | ||||||
|   ContentCompAlgo: ContentCompAlgoRestriction.default(0), |   ContentCompAlgo: ContentCompAlgoRestriction.default(0), | ||||||
| @ -615,17 +595,15 @@ export enum AESSettingsCipherModeRestrictionEnum { | |||||||
|   AES_CTR = 1, |   AES_CTR = 1, | ||||||
|   // AES-CBC
 |   // AES-CBC
 | ||||||
|   AES_CBC = 2, |   AES_CBC = 2, | ||||||
| } | }; | ||||||
| export const AESSettingsCipherModeRestriction = type('1 | 2'); | export const AESSettingsCipherModeRestriction = type('1 | 2'); | ||||||
| export type AESSettingsCipherModeRestrictionType = | export type AESSettingsCipherModeRestrictionType = typeof AESSettingsCipherModeRestriction.infer; | ||||||
|   typeof AESSettingsCipherModeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const ContentEncAESSettingsSchema = type({ | export const ContentEncAESSettingsSchema = type({ | ||||||
|   AESSettingsCipherMode: AESSettingsCipherModeRestriction, |   AESSettingsCipherMode: AESSettingsCipherModeRestriction, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type ContentEncAESSettingsType = | export type ContentEncAESSettingsType = typeof ContentEncAESSettingsSchema.infer; | ||||||
|   typeof ContentEncAESSettingsSchema.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ContentEncAlgoRestrictionEnum { | export enum ContentEncAlgoRestrictionEnum { | ||||||
|   // Not encrypted
 |   // Not encrypted
 | ||||||
| @ -640,20 +618,18 @@ export enum ContentEncAlgoRestrictionEnum { | |||||||
|   BLOWFISH = 4, |   BLOWFISH = 4, | ||||||
|   // AES
 |   // AES
 | ||||||
|   AES = 5, |   AES = 5, | ||||||
| } | }; | ||||||
| export const ContentEncAlgoRestriction = type('0 | 1 | 2 | 3 | 4 | 5'); | export const ContentEncAlgoRestriction = type('0 | 1 | 2 | 3 | 4 | 5'); | ||||||
| export type ContentEncAlgoRestrictionType = | export type ContentEncAlgoRestrictionType = typeof ContentEncAlgoRestriction.infer; | ||||||
|   typeof ContentEncAlgoRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ContentSigAlgoRestrictionEnum { | export enum ContentSigAlgoRestrictionEnum { | ||||||
|   // Not signed
 |   // Not signed
 | ||||||
|   NOT_SIGNED = 0, |   NOT_SIGNED = 0, | ||||||
|   // RSA
 |   // RSA
 | ||||||
|   RSA = 1, |   RSA = 1, | ||||||
| } | }; | ||||||
| export const ContentSigAlgoRestriction = type('0 | 1'); | export const ContentSigAlgoRestriction = type('0 | 1'); | ||||||
| export type ContentSigAlgoRestrictionType = | export type ContentSigAlgoRestrictionType = typeof ContentSigAlgoRestriction.infer; | ||||||
|   typeof ContentSigAlgoRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ContentSigHashAlgoRestrictionEnum { | export enum ContentSigHashAlgoRestrictionEnum { | ||||||
|   // Not signed
 |   // Not signed
 | ||||||
| @ -662,10 +638,9 @@ export enum ContentSigHashAlgoRestrictionEnum { | |||||||
|   SHA1_160 = 1, |   SHA1_160 = 1, | ||||||
|   // MD5
 |   // MD5
 | ||||||
|   MD5 = 2, |   MD5 = 2, | ||||||
| } | }; | ||||||
| export const ContentSigHashAlgoRestriction = type('0 | 1 | 2'); | export const ContentSigHashAlgoRestriction = type('0 | 1 | 2'); | ||||||
| export type ContentSigHashAlgoRestrictionType = | export type ContentSigHashAlgoRestrictionType = typeof ContentSigHashAlgoRestriction.infer; | ||||||
|   typeof ContentSigHashAlgoRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const ContentEncryptionSchema = type({ | export const ContentEncryptionSchema = type({ | ||||||
|   ContentEncAlgo: ContentEncAlgoRestriction.default(0), |   ContentEncAlgo: ContentEncAlgoRestriction.default(0), | ||||||
| @ -686,20 +661,18 @@ export enum ContentEncodingScopeRestrictionEnum { | |||||||
|   PRIVATE = 2, |   PRIVATE = 2, | ||||||
|   // Next
 |   // Next
 | ||||||
|   NEXT = 4, |   NEXT = 4, | ||||||
| } | }; | ||||||
| export const ContentEncodingScopeRestriction = type('1 | 2 | 4'); | export const ContentEncodingScopeRestriction = type('1 | 2 | 4'); | ||||||
| export type ContentEncodingScopeRestrictionType = | export type ContentEncodingScopeRestrictionType = typeof ContentEncodingScopeRestriction.infer; | ||||||
|   typeof ContentEncodingScopeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum ContentEncodingTypeRestrictionEnum { | export enum ContentEncodingTypeRestrictionEnum { | ||||||
|   // Compression
 |   // Compression
 | ||||||
|   COMPRESSION = 0, |   COMPRESSION = 0, | ||||||
|   // Encryption
 |   // Encryption
 | ||||||
|   ENCRYPTION = 1, |   ENCRYPTION = 1, | ||||||
| } | }; | ||||||
| export const ContentEncodingTypeRestriction = type('0 | 1'); | export const ContentEncodingTypeRestriction = type('0 | 1'); | ||||||
| export type ContentEncodingTypeRestrictionType = | export type ContentEncodingTypeRestrictionType = typeof ContentEncodingTypeRestriction.infer; | ||||||
|   typeof ContentEncodingTypeRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export const ContentEncodingSchema = type({ | export const ContentEncodingSchema = type({ | ||||||
|   ContentEncodingOrder: type.number.default(0), |   ContentEncodingOrder: type.number.default(0), | ||||||
| @ -712,7 +685,7 @@ export const ContentEncodingSchema = type({ | |||||||
| export type ContentEncodingType = typeof ContentEncodingSchema.infer; | export type ContentEncodingType = typeof ContentEncodingSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const ContentEncodingsSchema = type({ | export const ContentEncodingsSchema = type({ | ||||||
|   ContentEncoding: ContentEncodingSchema.array(), |   ContentEncoding: ContentEncodingSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type ContentEncodingsType = typeof ContentEncodingsSchema.infer; | export type ContentEncodingsType = typeof ContentEncodingsSchema.infer; | ||||||
| @ -734,7 +707,7 @@ export enum TrackTypeRestrictionEnum { | |||||||
|   CONTROL = 32, |   CONTROL = 32, | ||||||
|   // metadata
 |   // metadata
 | ||||||
|   METADATA = 33, |   METADATA = 33, | ||||||
| } | }; | ||||||
| export const TrackTypeRestriction = type('1 | 2 | 3 | 16 | 17 | 18 | 32 | 33'); | export const TrackTypeRestriction = type('1 | 2 | 3 | 16 | 17 | 18 | 32 | 33'); | ||||||
| export type TrackTypeRestrictionType = typeof TrackTypeRestriction.infer; | export type TrackTypeRestrictionType = typeof TrackTypeRestriction.infer; | ||||||
| 
 | 
 | ||||||
| @ -760,7 +733,7 @@ export const TrackEntrySchema = type({ | |||||||
|   MaxBlockAdditionID: type.number.default(0), |   MaxBlockAdditionID: type.number.default(0), | ||||||
|   BlockAdditionMapping: BlockAdditionMappingSchema.array().optional(), |   BlockAdditionMapping: BlockAdditionMappingSchema.array().optional(), | ||||||
|   Name: type.string.optional(), |   Name: type.string.optional(), | ||||||
|   Language: type.string.default('eng'), |   Language: type.string.default("eng"), | ||||||
|   LanguageBCP47: type.string.optional(), |   LanguageBCP47: type.string.optional(), | ||||||
|   CodecID: type.string, |   CodecID: type.string, | ||||||
|   CodecPrivate: BinarySchema.optional(), |   CodecPrivate: BinarySchema.optional(), | ||||||
| @ -788,7 +761,7 @@ export const TrackEntrySchema = type({ | |||||||
| export type TrackEntryType = typeof TrackEntrySchema.infer; | export type TrackEntryType = typeof TrackEntrySchema.infer; | ||||||
| 
 | 
 | ||||||
| export const TracksSchema = type({ | export const TracksSchema = type({ | ||||||
|   TrackEntry: TrackEntrySchema.array(), |   TrackEntry: TrackEntrySchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type TracksType = typeof TracksSchema.infer; | export type TracksType = typeof TracksSchema.infer; | ||||||
| @ -816,13 +789,13 @@ export type CueTrackPositionsType = typeof CueTrackPositionsSchema.infer; | |||||||
| 
 | 
 | ||||||
| export const CuePointSchema = type({ | export const CuePointSchema = type({ | ||||||
|   CueTime: type.number, |   CueTime: type.number, | ||||||
|   CueTrackPositions: CueTrackPositionsSchema.array(), |   CueTrackPositions: CueTrackPositionsSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type CuePointType = typeof CuePointSchema.infer; | export type CuePointType = typeof CuePointSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const CuesSchema = type({ | export const CuesSchema = type({ | ||||||
|   CuePoint: CuePointSchema.array(), |   CuePoint: CuePointSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type CuesType = typeof CuesSchema.infer; | export type CuesType = typeof CuesSchema.infer; | ||||||
| @ -841,7 +814,7 @@ export const AttachedFileSchema = type({ | |||||||
| export type AttachedFileType = typeof AttachedFileSchema.infer; | export type AttachedFileType = typeof AttachedFileSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const AttachmentsSchema = type({ | export const AttachmentsSchema = type({ | ||||||
|   AttachedFile: AttachedFileSchema.array(), |   AttachedFile: AttachedFileSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type AttachmentsType = typeof AttachmentsSchema.infer; | export type AttachmentsType = typeof AttachmentsSchema.infer; | ||||||
| @ -859,38 +832,39 @@ export const EditionEntrySchema = type({ | |||||||
|   EditionFlagDefault: type.number.default(0), |   EditionFlagDefault: type.number.default(0), | ||||||
|   EditionFlagOrdered: type.number.default(0), |   EditionFlagOrdered: type.number.default(0), | ||||||
|   EditionDisplay: EditionDisplaySchema.array().optional(), |   EditionDisplay: EditionDisplaySchema.array().optional(), | ||||||
|  | 
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type EditionEntryType = typeof EditionEntrySchema.infer; | export type EditionEntryType = typeof EditionEntrySchema.infer; | ||||||
| 
 | 
 | ||||||
| export const ChaptersSchema = type({ | export const ChaptersSchema = type({ | ||||||
|   EditionEntry: EditionEntrySchema.array(), |   EditionEntry: EditionEntrySchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type ChaptersType = typeof ChaptersSchema.infer; | export type ChaptersType = typeof ChaptersSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const TagTrackUIDSchema = match({ | export const TagTrackUIDSchema = match({ | ||||||
|   'number[]': (v) => (v.length > 0 ? v : [0]), |   "number[]": v => v.length > 0 ? v : [0], | ||||||
|   undefined: () => [0], |   "undefined": () => [0], | ||||||
|   default: 'assert', |   default: "assert" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const TagEditionUIDSchema = match({ | export const TagEditionUIDSchema = match({ | ||||||
|   'number[]': (v) => (v.length > 0 ? v : [0]), |   "number[]": v => v.length > 0 ? v : [0], | ||||||
|   undefined: () => [0], |   "undefined": () => [0], | ||||||
|   default: 'assert', |   default: "assert" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const TagChapterUIDSchema = match({ | export const TagChapterUIDSchema = match({ | ||||||
|   'number[]': (v) => (v.length > 0 ? v : [0]), |   "number[]": v => v.length > 0 ? v : [0], | ||||||
|   undefined: () => [0], |   "undefined": () => [0], | ||||||
|   default: 'assert', |   default: "assert" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export const TagAttachmentUIDSchema = match({ | export const TagAttachmentUIDSchema = match({ | ||||||
|   'number[]': (v) => (v.length > 0 ? v : [0]), |   "number[]": v => v.length > 0 ? v : [0], | ||||||
|   undefined: () => [0], |   "undefined": () => [0], | ||||||
|   default: 'assert', |   default: "assert" | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export enum TargetTypeValueRestrictionEnum { | export enum TargetTypeValueRestrictionEnum { | ||||||
| @ -908,60 +882,55 @@ export enum TargetTypeValueRestrictionEnum { | |||||||
|   EDITION_ISSUE_VOLUME_OPUS_SEASON_SEQUEL = 60, |   EDITION_ISSUE_VOLUME_OPUS_SEASON_SEQUEL = 60, | ||||||
|   // COLLECTION
 |   // COLLECTION
 | ||||||
|   COLLECTION = 70, |   COLLECTION = 70, | ||||||
| } | }; | ||||||
| export const TargetTypeValueRestriction = type( | export const TargetTypeValueRestriction = type('10 | 20 | 30 | 40 | 50 | 60 | 70'); | ||||||
|   '10 | 20 | 30 | 40 | 50 | 60 | 70' | export type TargetTypeValueRestrictionType = typeof TargetTypeValueRestriction.infer; | ||||||
| ); |  | ||||||
| export type TargetTypeValueRestrictionType = |  | ||||||
|   typeof TargetTypeValueRestriction.infer; |  | ||||||
| 
 | 
 | ||||||
| export enum TargetTypeRestrictionEnum { | export enum TargetTypeRestrictionEnum { | ||||||
|   // TargetTypeValue 70
 |   // TargetTypeValue 70
 | ||||||
|   COLLECTION = 'COLLECTION', |   COLLECTION = "COLLECTION", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   EDITION = 'EDITION', |   EDITION = "EDITION", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   ISSUE = 'ISSUE', |   ISSUE = "ISSUE", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   VOLUME = 'VOLUME', |   VOLUME = "VOLUME", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   OPUS = 'OPUS', |   OPUS = "OPUS", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   SEASON = 'SEASON', |   SEASON = "SEASON", | ||||||
|   // TargetTypeValue 60
 |   // TargetTypeValue 60
 | ||||||
|   SEQUEL = 'SEQUEL', |   SEQUEL = "SEQUEL", | ||||||
|   // TargetTypeValue 50
 |   // TargetTypeValue 50
 | ||||||
|   ALBUM = 'ALBUM', |   ALBUM = "ALBUM", | ||||||
|   // TargetTypeValue 50
 |   // TargetTypeValue 50
 | ||||||
|   OPERA = 'OPERA', |   OPERA = "OPERA", | ||||||
|   // TargetTypeValue 50
 |   // TargetTypeValue 50
 | ||||||
|   CONCERT = 'CONCERT', |   CONCERT = "CONCERT", | ||||||
|   // TargetTypeValue 50
 |   // TargetTypeValue 50
 | ||||||
|   MOVIE = 'MOVIE', |   MOVIE = "MOVIE", | ||||||
|   // TargetTypeValue 50
 |   // TargetTypeValue 50
 | ||||||
|   EPISODE = 'EPISODE', |   EPISODE = "EPISODE", | ||||||
|   // TargetTypeValue 40
 |   // TargetTypeValue 40
 | ||||||
|   PART = 'PART', |   PART = "PART", | ||||||
|   // TargetTypeValue 40
 |   // TargetTypeValue 40
 | ||||||
|   SESSION = 'SESSION', |   SESSION = "SESSION", | ||||||
|   // TargetTypeValue 30
 |   // TargetTypeValue 30
 | ||||||
|   TRACK = 'TRACK', |   TRACK = "TRACK", | ||||||
|   // TargetTypeValue 30
 |   // TargetTypeValue 30
 | ||||||
|   SONG = 'SONG', |   SONG = "SONG", | ||||||
|   // TargetTypeValue 30
 |   // TargetTypeValue 30
 | ||||||
|   CHAPTER = 'CHAPTER', |   CHAPTER = "CHAPTER", | ||||||
|   // TargetTypeValue 20
 |   // TargetTypeValue 20
 | ||||||
|   SUBTRACK = 'SUBTRACK', |   SUBTRACK = "SUBTRACK", | ||||||
|   // TargetTypeValue 20
 |   // TargetTypeValue 20
 | ||||||
|   MOVEMENT = 'MOVEMENT', |   MOVEMENT = "MOVEMENT", | ||||||
|   // TargetTypeValue 20
 |   // TargetTypeValue 20
 | ||||||
|   SCENE = 'SCENE', |   SCENE = "SCENE", | ||||||
|   // TargetTypeValue 10
 |   // TargetTypeValue 10
 | ||||||
|   SHOT = 'SHOT', |   SHOT = "SHOT", | ||||||
| } | }; | ||||||
| export const TargetTypeRestriction = type( | export const TargetTypeRestriction = type('"COLLECTION" | "EDITION" | "ISSUE" | "VOLUME" | "OPUS" | "SEASON" | "SEQUEL" | "ALBUM" | "OPERA" | "CONCERT" | "MOVIE" | "EPISODE" | "PART" | "SESSION" | "TRACK" | "SONG" | "CHAPTER" | "SUBTRACK" | "MOVEMENT" | "SCENE" | "SHOT"'); | ||||||
|   '"COLLECTION" | "EDITION" | "ISSUE" | "VOLUME" | "OPUS" | "SEASON" | "SEQUEL" | "ALBUM" | "OPERA" | "CONCERT" | "MOVIE" | "EPISODE" | "PART" | "SESSION" | "TRACK" | "SONG" | "CHAPTER" | "SUBTRACK" | "MOVEMENT" | "SCENE" | "SHOT"' |  | ||||||
| ); |  | ||||||
| export type TargetTypeRestrictionType = typeof TargetTypeRestriction.infer; | export type TargetTypeRestrictionType = typeof TargetTypeRestriction.infer; | ||||||
| 
 | 
 | ||||||
| export const TargetsSchema = type({ | export const TargetsSchema = type({ | ||||||
| @ -977,12 +946,13 @@ export type TargetsType = typeof TargetsSchema.infer; | |||||||
| 
 | 
 | ||||||
| export const TagSchema = type({ | export const TagSchema = type({ | ||||||
|   Targets: TargetsSchema, |   Targets: TargetsSchema, | ||||||
|  | 
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type TagType = typeof TagSchema.infer; | export type TagType = typeof TagSchema.infer; | ||||||
| 
 | 
 | ||||||
| export const TagsSchema = type({ | export const TagsSchema = type({ | ||||||
|   Tag: TagSchema.array(), |   Tag: TagSchema.array().atLeastLength(1), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type TagsType = typeof TagsSchema.infer; | export type TagsType = typeof TagsSchema.infer; | ||||||
| @ -1037,5 +1007,5 @@ export const IdMultiSet = new Set([ | |||||||
|   EbmlTagIdEnum.Tag, |   EbmlTagIdEnum.Tag, | ||||||
|   EbmlTagIdEnum.SeekHead, |   EbmlTagIdEnum.SeekHead, | ||||||
|   EbmlTagIdEnum.Cluster, |   EbmlTagIdEnum.Cluster, | ||||||
|   EbmlTagIdEnum.Tags, |   EbmlTagIdEnum.Tags | ||||||
| ]); | ]) | ||||||
| @ -2,11 +2,7 @@ import type { Type } from 'arktype'; | |||||||
| import { EbmlElementType, EbmlTagIdEnum, type EbmlTagType } from 'konoebml'; | import { EbmlElementType, EbmlTagIdEnum, type EbmlTagType } from 'konoebml'; | ||||||
| import { IdMultiSet } from './schema'; | import { IdMultiSet } from './schema'; | ||||||
| 
 | 
 | ||||||
| export type InferType<T> = T extends Type<infer U> ? U : never; | export type InferType<T extends Type<any>> = T['infer']; | ||||||
| 
 |  | ||||||
| export const SEEK_ID_KAX_INFO = new Uint8Array([0x15, 0x49, 0xa9, 0x66]); |  | ||||||
| export const SEEK_ID_KAX_TRACKS = new Uint8Array([0x16, 0x54, 0xae, 0x6b]); |  | ||||||
| export const SEEK_ID_KAX_CUES = new Uint8Array([0x1c, 0x53, 0xbb, 0x6b]); |  | ||||||
| 
 | 
 | ||||||
| export type PredicateIdExtract<T, K> = Extract<T, { id: K }>; | export type PredicateIdExtract<T, K> = Extract<T, { id: K }>; | ||||||
| 
 | 
 | ||||||
| @ -31,13 +27,13 @@ export function isTagPos< | |||||||
|     pos === '*' || pos === tag.position; |     pos === '*' || pos === tag.position; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function convertEbmlTagToModelShape(tag: EbmlTagType) { | export function convertEbmlTagToComponent (tag: EbmlTagType) { | ||||||
|   if (tag.type === EbmlElementType.Master) { |   if (tag.type === EbmlElementType.Master) { | ||||||
|     const obj: Record<string, any> = {}; |     const obj: Record<string, any> = {}; | ||||||
|     const children = tag.children; |     const children = tag.children; | ||||||
|     for (const c of children) { |     for (const c of children) { | ||||||
|       const name = EbmlTagIdEnum[c.id]; |       const name = EbmlTagIdEnum[c.id]; | ||||||
|       const converted = convertEbmlTagToModelShape(c); |       const converted = convertEbmlTagToComponent(c); | ||||||
|       if (IdMultiSet.has(c.id)) { |       if (IdMultiSet.has(c.id)) { | ||||||
|         if (obj[name]) { |         if (obj[name]) { | ||||||
|           obj[name].push(converted); |           obj[name].push(converted); | ||||||
| @ -48,6 +44,7 @@ export function convertEbmlTagToModelShape(tag: EbmlTagType) { | |||||||
|         obj[name] = converted; |         obj[name] = converted; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     return obj; | ||||||
|   } |   } | ||||||
|   if (tag.id === EbmlTagIdEnum.SimpleBlock || tag.id === EbmlTagIdEnum.Block) { |   if (tag.id === EbmlTagIdEnum.SimpleBlock || tag.id === EbmlTagIdEnum.Block) { | ||||||
|     return tag; |     return tag; | ||||||
|  | |||||||
| @ -390,6 +390,9 @@ function generateMkvSchemaHierarchy(elements_: EbmlElementType[]) { | |||||||
|           : meta.primitive(v.name); |           : meta.primitive(v.name); | ||||||
|         if (v.maxOccurs !== 1) { |         if (v.maxOccurs !== 1) { | ||||||
|           expr = `${expr}.array()`; |           expr = `${expr}.array()`; | ||||||
|  |           if (v.maxOccurs !== 1 && v.minOccurs === 1 && !v.default) { | ||||||
|  |             expr = `${expr}.atLeastLength(1)` | ||||||
|  |           } | ||||||
|           idMulti.add(v.name); |           idMulti.add(v.name); | ||||||
|         } |         } | ||||||
|         if (v.default) { |         if (v.default) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user