temp: temp save

This commit is contained in:
2025-03-21 23:45:12 +08:00
parent d7b8b57e9c
commit f921819d2a
18 changed files with 5055 additions and 230 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}