download
raw
5.68 kB
import { AttrList } from '../utils/attr-list';
import { logger } from '../utils/logger';
import type { MediaFragmentRef } from './fragment';
// Avoid exporting const enum so that these values can be inlined
const enum DateRangeAttribute {
ID = 'ID',
CLASS = 'CLASS',
CUE = 'CUE',
START_DATE = 'START-DATE',
DURATION = 'DURATION',
END_DATE = 'END-DATE',
END_ON_NEXT = 'END-ON-NEXT',
PLANNED_DURATION = 'PLANNED-DURATION',
SCTE35_OUT = 'SCTE35-OUT',
SCTE35_IN = 'SCTE35-IN',
SCTE35_CMD = 'SCTE35-CMD',
}
export type DateRangeCue = {
pre: boolean;
post: boolean;
once: boolean;
};
const CLASS_INTERSTITIAL = 'com.apple.hls.interstitial';
export function isDateRangeCueAttribute(attrName: string): boolean {
return (
attrName !== DateRangeAttribute.ID &&
attrName !== DateRangeAttribute.CLASS &&
attrName !== DateRangeAttribute.CUE &&
attrName !== DateRangeAttribute.START_DATE &&
attrName !== DateRangeAttribute.DURATION &&
attrName !== DateRangeAttribute.END_DATE &&
attrName !== DateRangeAttribute.END_ON_NEXT
);
}
export function isSCTE35Attribute(attrName: string): boolean {
return (
attrName === DateRangeAttribute.SCTE35_OUT ||
attrName === DateRangeAttribute.SCTE35_IN ||
attrName === DateRangeAttribute.SCTE35_CMD
);
}
export class DateRange {
public attr: AttrList;
public tagAnchor: MediaFragmentRef | null;
public tagOrder: number;
private _startDate: Date;
private _endDate?: Date;
private _dateAtEnd?: Date;
private _cue?: DateRangeCue;
private _badValueForSameId?: string;
constructor(
dateRangeAttr: AttrList,
dateRangeWithSameId?: DateRange | undefined,
tagCount: number = 0,
) {
this.tagAnchor = dateRangeWithSameId?.tagAnchor || null;
this.tagOrder = dateRangeWithSameId?.tagOrder ?? tagCount;
if (dateRangeWithSameId) {
const previousAttr = dateRangeWithSameId.attr;
for (const key in previousAttr) {
if (
Object.prototype.hasOwnProperty.call(dateRangeAttr, key) &&
dateRangeAttr[key] !== previousAttr[key]
) {
logger.warn(
`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`,
);
this._badValueForSameId = key;
break;
}
}
// Merge DateRange tags with the same ID
dateRangeAttr = Object.assign(
new AttrList({}),
previousAttr,
dateRangeAttr,
);
}
this.attr = dateRangeAttr;
if (dateRangeWithSameId) {
this._startDate = dateRangeWithSameId._startDate;
this._cue = dateRangeWithSameId._cue;
this._endDate = dateRangeWithSameId._endDate;
this._dateAtEnd = dateRangeWithSameId._dateAtEnd;
} else {
this._startDate = new Date(dateRangeAttr[DateRangeAttribute.START_DATE]);
}
if (DateRangeAttribute.END_DATE in this.attr) {
const endDate =
dateRangeWithSameId?.endDate ||
new Date(this.attr[DateRangeAttribute.END_DATE]);
if (Number.isFinite(endDate.getTime())) {
this._endDate = endDate;
}
}
}
get id(): string {
return this.attr.ID;
}
get class(): string {
return this.attr.CLASS;
}
get cue(): DateRangeCue {
const _cue = this._cue;
if (_cue === undefined) {
return (this._cue = this.attr.enumeratedStringList(
this.attr.CUE ? 'CUE' : 'X-CUE',
{
pre: false,
post: false,
once: false,
},
));
}
return _cue;
}
get startTime(): number {
const { tagAnchor } = this;
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (tagAnchor === null || tagAnchor.programDateTime === null) {
logger.warn(
`Expected tagAnchor Fragment with PDT set for DateRange "${this.id}": ${tagAnchor}`,
);
return NaN;
}
return (
tagAnchor.start +
(this.startDate.getTime() - tagAnchor.programDateTime) / 1000
);
}
get startDate(): Date {
return this._startDate;
}
get endDate(): Date | null {
const dateAtEnd = this._endDate || this._dateAtEnd;
if (dateAtEnd) {
return dateAtEnd;
}
const duration = this.duration;
if (duration !== null) {
return (this._dateAtEnd = new Date(
this._startDate.getTime() + duration * 1000,
));
}
return null;
}
get duration(): number | null {
if (DateRangeAttribute.DURATION in this.attr) {
const duration = this.attr.decimalFloatingPoint(
DateRangeAttribute.DURATION,
);
if (Number.isFinite(duration)) {
return duration;
}
} else if (this._endDate) {
return (this._endDate.getTime() - this._startDate.getTime()) / 1000;
}
return null;
}
get plannedDuration(): number | null {
if (DateRangeAttribute.PLANNED_DURATION in this.attr) {
return this.attr.decimalFloatingPoint(
DateRangeAttribute.PLANNED_DURATION,
);
}
return null;
}
get endOnNext(): boolean {
return this.attr.bool(DateRangeAttribute.END_ON_NEXT);
}
get isInterstitial(): boolean {
return this.class === CLASS_INTERSTITIAL;
}
get isValid(): boolean {
return (
!!this.id &&
!this._badValueForSameId &&
Number.isFinite(this.startDate.getTime()) &&
(this.duration === null || this.duration >= 0) &&
(!this.endOnNext || !!this.class) &&
(!this.attr.CUE ||
(!this.cue.pre && !this.cue.post) ||
this.cue.pre !== this.cue.post) &&
(!this.isInterstitial ||
'X-ASSET-URI' in this.attr ||
'X-ASSET-LIST' in this.attr)
);
}
}

Xet Storage Details

Size:
5.68 kB
·
Xet hash:
24e90a5e7ba652b2bcd10ff1197e69628621f21ef06b32faf5cf353e2590b205

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.