temp: temp save
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
"preview": "rsbuild preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"konoebml": "0.1.0",
|
||||
"konoebml": "0.1.1",
|
||||
"lit": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -7,15 +7,32 @@ import {
|
||||
type EbmlCuesTagType,
|
||||
type EbmlSeekHeadTagType,
|
||||
type EbmlSegmentTagType,
|
||||
type EbmlCuePointTagType,
|
||||
type EbmlMasterTagType,
|
||||
} from 'konoebml';
|
||||
import { isTagIdPos, simpleMasterExtractor } from './util';
|
||||
import {
|
||||
convertEbmlTagToModelShape,
|
||||
type InferType,
|
||||
isTagIdPos,
|
||||
SEEK_ID_KAX_CUES,
|
||||
SEEK_ID_KAX_INFO,
|
||||
SEEK_ID_KAX_TRACKS,
|
||||
} from './util';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { type } from 'arktype';
|
||||
import { TagWithArktype } from './util';
|
||||
import type { Type } from 'arktype';
|
||||
import { CuePointSchema, type CuePointType } from './schema';
|
||||
|
||||
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 abstract class StandardComponentSystem<
|
||||
E extends EbmlMasterTagType,
|
||||
S extends Type<any>,
|
||||
> {
|
||||
abstract get schema(): S;
|
||||
|
||||
componentFromTag(tag: E): InferType<S> {
|
||||
const extracted = convertEbmlTagToModelShape(tag);
|
||||
return this.schema.assert(extracted) as InferType<S>;
|
||||
}
|
||||
}
|
||||
|
||||
export class EbmlSegment {
|
||||
startNode: EbmlSegmentTagType;
|
||||
@@ -103,109 +120,22 @@ export class EbmlSegment {
|
||||
}
|
||||
}
|
||||
|
||||
export class TrackEntry extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.TrackEntry,
|
||||
schema: type({
|
||||
trackNumber: 'number',
|
||||
trackType: 'number',
|
||||
trackUID: 'number',
|
||||
}),
|
||||
extract: simpleMasterExtractor({
|
||||
[EbmlTagIdEnum.TrackNumber]: {
|
||||
key: 'trackNumber',
|
||||
extract: (t) => t.data as number,
|
||||
},
|
||||
[EbmlTagIdEnum.TrackType]: {
|
||||
key: 'trackType',
|
||||
extract: (t) => t.data as number,
|
||||
},
|
||||
[EbmlTagIdEnum.TrackUID]: {
|
||||
key: 'trackUID',
|
||||
extract: (t) => t.data as number,
|
||||
},
|
||||
}),
|
||||
}) {}
|
||||
|
||||
export class Tracks extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.Tracks,
|
||||
schema: type({
|
||||
tracks: type.instanceOf(TrackEntry).array(),
|
||||
}),
|
||||
extract: simpleMasterExtractor({
|
||||
[EbmlTagIdEnum.TrackEntry]: {
|
||||
key: 'tracks',
|
||||
multi: true,
|
||||
extract: TrackEntry.fromTag.bind(TrackEntry),
|
||||
},
|
||||
}),
|
||||
}) {}
|
||||
|
||||
export interface EbmlSeekEntry {
|
||||
seekId: Uint8Array;
|
||||
seekPosition: number;
|
||||
}
|
||||
|
||||
export class MHead extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.EBML,
|
||||
schema: type({}),
|
||||
extract: () => ({}),
|
||||
}) {}
|
||||
|
||||
export class SimpleBlock extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.SimpleBlock,
|
||||
schema: type({
|
||||
frame: type.instanceOf(Uint8Array),
|
||||
}),
|
||||
extract: (tag) => ({
|
||||
frame: tag.payload,
|
||||
}),
|
||||
}) {}
|
||||
|
||||
export class Block extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.Block,
|
||||
schema: type({}),
|
||||
extract: () => ({}),
|
||||
}) {}
|
||||
|
||||
export class Cluster extends TagWithArktype({
|
||||
id: EbmlTagIdEnum.Cluster,
|
||||
schema: type({
|
||||
timestamp: 'number',
|
||||
position: 'number?',
|
||||
prevSize: 'number?',
|
||||
simpleBlock: type.instanceOf(SimpleBlock).array(),
|
||||
}),
|
||||
extract: simpleMasterExtractor({
|
||||
[EbmlTagIdEnum.Timecode]: {
|
||||
key: 'timestamp',
|
||||
extract: (t) => t.data as number,
|
||||
},
|
||||
[EbmlTagIdEnum.PrevSize]: {
|
||||
key: 'prevSize',
|
||||
extract: (t) => t.data as number,
|
||||
},
|
||||
[EbmlTagIdEnum.SimpleBlock]: {
|
||||
key: 'simpleBlock',
|
||||
multi: true,
|
||||
extract: SimpleBlock.fromTag.bind(SimpleBlock),
|
||||
},
|
||||
}),
|
||||
}) {}
|
||||
|
||||
export type CuePointType = typeof CuePoint.infer;
|
||||
|
||||
export class Cues {
|
||||
export class CuesSystem extends StandardComponentSystem<
|
||||
EbmlCuePointTagType,
|
||||
typeof CuePointSchema
|
||||
> {
|
||||
schema = CuePointSchema;
|
||||
cues: CuePointType[];
|
||||
|
||||
constructor(
|
||||
public readonly tag: EbmlCuesTagType,
|
||||
cues: CuePointType[]
|
||||
) {}
|
||||
constructor(cues: CuePointType[]) {
|
||||
super();
|
||||
this.cues = cues;
|
||||
}
|
||||
|
||||
findClosestCue(seekTime: number): CuePoint | null {
|
||||
findClosestCue(seekTime: number): CuePointType | undefined {
|
||||
const cues = this.cues;
|
||||
if (!cues || cues.length === 0) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let left = 0;
|
||||
@@ -235,8 +165,8 @@ export class Cues {
|
||||
|
||||
const before = cues[right];
|
||||
const after = cues[left];
|
||||
return Math.abs(before.timestamp - seekTime) <
|
||||
Math.abs(after.timestamp - seekTime)
|
||||
return Math.abs(before.CueTime - seekTime) <
|
||||
Math.abs(after.CueTime - seekTime)
|
||||
? before
|
||||
: after;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
withLatestFrom,
|
||||
} from 'rxjs';
|
||||
import { createRangedStream } from '@/fetch';
|
||||
import { EbmlSegment, Cluster, SEEK_ID_KAX_CUES, Cues } from './model';
|
||||
import { EbmlSegment, Cluster, SEEK_ID_KAX_CUES, CuesSystem } from './model';
|
||||
import { isTagIdPos } from './util';
|
||||
|
||||
export function createRangedEbmlStream(
|
||||
@@ -250,7 +250,7 @@ export function createEbmlController(src: string) {
|
||||
};
|
||||
|
||||
const seekWithCues = (
|
||||
cues: Cues,
|
||||
cues: CuesSystem,
|
||||
seekTime: number
|
||||
): Observable<Cluster> => {
|
||||
if (seekTime === 0) {
|
||||
@@ -287,7 +287,7 @@ export function createEbmlController(src: string) {
|
||||
return merge(
|
||||
withCues$.pipe(
|
||||
switchMap((s) =>
|
||||
seekWithCues(Cues.fromTag(s.cuesNode!), seekTime)
|
||||
seekWithCues(CuesSystem.fromTag(s.cuesNode!), seekTime)
|
||||
)
|
||||
),
|
||||
withoutCues$.pipe(switchMap((_) => seekWithoutCues(seekTime)))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +1,12 @@
|
||||
import type { Type } from 'arktype';
|
||||
import type { EbmlMasterTagType, EbmlTagIdEnum, EbmlTagType } from 'konoebml';
|
||||
import { EbmlElementType, EbmlTagIdEnum, type EbmlTagType } from 'konoebml';
|
||||
import { IdMultiSet } from './schema';
|
||||
|
||||
export type InferType<T> = T extends Type<infer U> ? U : never;
|
||||
|
||||
export interface TagWithArktypeOptions<
|
||||
T extends EbmlTagType,
|
||||
S extends Type<any>,
|
||||
> {
|
||||
id: T['id'];
|
||||
schema: S;
|
||||
extract: (tag: T, schema: S) => InferType<S>;
|
||||
}
|
||||
|
||||
export type TagWithArktypeClassInstance<
|
||||
T extends EbmlTagType,
|
||||
S extends Type<any>,
|
||||
> = InferType<S> & {
|
||||
tag: T;
|
||||
};
|
||||
|
||||
export interface TagWithArktypeClass<
|
||||
T extends EbmlTagType,
|
||||
S extends Type<any>,
|
||||
> {
|
||||
new (tag: T, validatedTag: InferType<S>): TagWithArktypeClassInstance<T, S>;
|
||||
|
||||
fromTag<R extends TagWithArktypeClassInstance<T, S>>(
|
||||
this: new (
|
||||
tag: T,
|
||||
validatedTag: InferType<S>
|
||||
) => TagWithArktypeClassInstance<T, S>,
|
||||
tag: T
|
||||
): R;
|
||||
|
||||
id: T['id'];
|
||||
schema: S;
|
||||
}
|
||||
|
||||
export function TagWithArktype<T extends EbmlTagType, S extends Type<any>>({
|
||||
id,
|
||||
schema,
|
||||
extract,
|
||||
}: TagWithArktypeOptions<T, S>): TagWithArktypeClass<T, S> {
|
||||
const tagWithArktypeImpl = class TagWithArktypeImpl {
|
||||
static id = id;
|
||||
static schema = schema;
|
||||
|
||||
tag: T;
|
||||
|
||||
constructor(tag: T, validatedTag: InferType<S>) {
|
||||
Object.assign(this, validatedTag);
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
static fromTag(tag: T) {
|
||||
const extractedData = extract(tag, schema);
|
||||
const validatedExtractedData = schema(extractedData);
|
||||
// biome-ignore lint/complexity/noThisInStatic: <explanation>
|
||||
return new this(tag, validatedExtractedData);
|
||||
}
|
||||
};
|
||||
|
||||
return tagWithArktypeImpl as unknown as TagWithArktypeClass<T, S>;
|
||||
}
|
||||
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 }>;
|
||||
|
||||
@@ -86,56 +30,27 @@ export function isTagPos<
|
||||
return (tag: T): tag is PredicatePositionExtract<T, P> =>
|
||||
pos === '*' || pos === tag.position;
|
||||
}
|
||||
export type MasterChildExtractMap<T, K> = {
|
||||
[id in EbmlTagIdEnum]?: K extends keyof T
|
||||
?
|
||||
| {
|
||||
key: K;
|
||||
multi: true;
|
||||
extract: (
|
||||
tag: Extract<EbmlTagType, { id: id }>
|
||||
) => T[K] extends Array<infer U> ? U : never;
|
||||
}
|
||||
| {
|
||||
key: K;
|
||||
multi?: false;
|
||||
extract: (tag: Extract<EbmlTagType, { id: id }>) => T[K];
|
||||
}
|
||||
: never;
|
||||
};
|
||||
|
||||
export function simpleMasterExtractor<
|
||||
T extends EbmlMasterTagType,
|
||||
S extends Type<any>,
|
||||
EM extends MasterChildExtractMap<InferType<S>, keyof InferType<S>>,
|
||||
>(map: EM) {
|
||||
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
|
||||
return (tag: T, _schema: S): InferType<S> => {
|
||||
if (!tag?.children?.length) {
|
||||
return {} as unknown as InferType<S>;
|
||||
}
|
||||
const value = {} as Record<string, any>;
|
||||
for (const c of tag.children) {
|
||||
const entry = (
|
||||
map as unknown as Record<
|
||||
string,
|
||||
{ id: number; multi: boolean; extract: (tag: any) => any }
|
||||
>
|
||||
)[c.id as number] as any;
|
||||
if (entry?.key) {
|
||||
const key = entry.key;
|
||||
const item = entry.extract ? entry.extract(c) : c.data;
|
||||
if (entry.multi) {
|
||||
if (value[key]) {
|
||||
value[key].push(item);
|
||||
} else {
|
||||
value[key] = [item];
|
||||
}
|
||||
export function convertEbmlTagToModelShape(tag: EbmlTagType) {
|
||||
if (tag.type === EbmlElementType.Master) {
|
||||
const obj: Record<string, any> = {};
|
||||
const children = tag.children;
|
||||
for (const c of children) {
|
||||
const name = EbmlTagIdEnum[c.id];
|
||||
const converted = convertEbmlTagToModelShape(c);
|
||||
if (IdMultiSet.has(c.id)) {
|
||||
if (obj[name]) {
|
||||
obj[name].push(converted);
|
||||
} else {
|
||||
value[key] = item;
|
||||
obj[name] = [converted];
|
||||
}
|
||||
} else {
|
||||
obj[name] = converted;
|
||||
}
|
||||
}
|
||||
return value as unknown as InferType<S>;
|
||||
};
|
||||
}
|
||||
if (tag.id === EbmlTagIdEnum.SimpleBlock || tag.id === EbmlTagIdEnum.Block) {
|
||||
return tag;
|
||||
}
|
||||
return tag.data;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user