mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
148 lines
4.0 KiB
JavaScript
148 lines
4.0 KiB
JavaScript
//#region src/media/hls/parse-attributes.ts
|
|
/**
|
|
* Parse HLS attribute list from a tag line.
|
|
* Handles both quoted and unquoted values.
|
|
*/
|
|
function parseAttributeList(line) {
|
|
const attributes = /* @__PURE__ */ new Map();
|
|
for (const match of line.matchAll(/([A-Z0-9-]+)=(?:"([^"]*)"|([^,]*))/g)) {
|
|
const key = match[1];
|
|
const value = match[2] ?? match[3] ?? "";
|
|
if (key) attributes.set(key, value);
|
|
}
|
|
return attributes;
|
|
}
|
|
/**
|
|
* Parse RESOLUTION attribute value (WIDTHxHEIGHT).
|
|
*/
|
|
function parseResolution(value) {
|
|
const match = /^(\d+)x(\d+)$/.exec(value);
|
|
if (!match) return null;
|
|
return {
|
|
width: Number.parseInt(match[1], 10),
|
|
height: Number.parseInt(match[2], 10)
|
|
};
|
|
}
|
|
/**
|
|
* Parse FRAME-RATE attribute to rational frame rate.
|
|
*/
|
|
function parseFrameRate(value) {
|
|
const fps = Number.parseFloat(value);
|
|
if (Number.isNaN(fps) || fps <= 0) return void 0;
|
|
if (Math.abs(fps - 23.976) < .01) return {
|
|
frameRateNumerator: 24e3,
|
|
frameRateDenominator: 1001
|
|
};
|
|
if (Math.abs(fps - 29.97) < .01) return {
|
|
frameRateNumerator: 3e4,
|
|
frameRateDenominator: 1001
|
|
};
|
|
if (Math.abs(fps - 59.94) < .01) return {
|
|
frameRateNumerator: 6e4,
|
|
frameRateDenominator: 1001
|
|
};
|
|
if (fps % 1 === 0) return { frameRateNumerator: Math.round(fps) };
|
|
return { frameRateNumerator: Math.round(fps) };
|
|
}
|
|
const AUDIO_CODEC_PREFIXES = [
|
|
"mp4a.",
|
|
"ac-3",
|
|
"ec-3",
|
|
"ac-4",
|
|
"opus",
|
|
"flac",
|
|
"dts",
|
|
"alac",
|
|
"vorbis"
|
|
];
|
|
/**
|
|
* Parse CODECS attribute into separate video and audio codecs.
|
|
*/
|
|
function parseCodecs(codecs) {
|
|
const parts = codecs.split(",").map((s) => s.trim());
|
|
const result = {};
|
|
for (const codec of parts) {
|
|
const lower = codec.toLowerCase();
|
|
if (codec.startsWith("avc1.") || codec.startsWith("hvc1.") || codec.startsWith("hev1.")) result.video = codec;
|
|
else if (AUDIO_CODEC_PREFIXES.some((prefix) => lower.startsWith(prefix))) result.audio = codec;
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Parse #EXTINF duration value.
|
|
*/
|
|
function parseExtInfDuration(value) {
|
|
const durationPart = value.split(",")[0] ?? value;
|
|
const duration = Number.parseFloat(durationPart);
|
|
return Number.isNaN(duration) ? 0 : duration;
|
|
}
|
|
/**
|
|
* Parse BYTERANGE attribute value.
|
|
* Format: "length[@offset]"
|
|
* If offset is omitted, it continues from the previous byte range end.
|
|
*/
|
|
function parseByteRange(value, previousEnd) {
|
|
const match = /^(\d+)(?:@(\d+))?$/.exec(value);
|
|
if (!match) return null;
|
|
const length = Number.parseInt(match[1], 10);
|
|
if (Number.isNaN(length)) return null;
|
|
let start;
|
|
if (match[2] !== void 0) {
|
|
start = Number.parseInt(match[2], 10);
|
|
if (Number.isNaN(start)) return null;
|
|
} else if (previousEnd !== void 0) start = previousEnd;
|
|
else return null;
|
|
return {
|
|
start,
|
|
end: start + length - 1
|
|
};
|
|
}
|
|
/**
|
|
* Create AttributeList from raw attribute string.
|
|
*/
|
|
function createAttributeList(line) {
|
|
const map = parseAttributeList(line);
|
|
return {
|
|
get(key) {
|
|
return map.get(key);
|
|
},
|
|
getInt(key, defaultValue) {
|
|
const value = map.get(key);
|
|
if (value === void 0) return defaultValue;
|
|
const parsed = Number.parseInt(value, 10);
|
|
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
},
|
|
getFloat(key, defaultValue) {
|
|
const value = map.get(key);
|
|
if (value === void 0) return defaultValue;
|
|
const parsed = Number.parseFloat(value);
|
|
return Number.isNaN(parsed) ? defaultValue : parsed;
|
|
},
|
|
getBool(key) {
|
|
return map.get(key) === "YES";
|
|
},
|
|
getResolution(key) {
|
|
const value = map.get(key);
|
|
if (!value) return void 0;
|
|
return parseResolution(value) ?? void 0;
|
|
},
|
|
getFrameRate(key) {
|
|
const value = map.get(key);
|
|
if (!value) return void 0;
|
|
return parseFrameRate(value);
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Match a tag and extract its attributes.
|
|
* Returns null if the line doesn't match the tag.
|
|
*/
|
|
function matchTag(line, tag) {
|
|
const prefix = `#${tag}:`;
|
|
if (!line.startsWith(prefix)) return null;
|
|
return createAttributeList(line.slice(prefix.length));
|
|
}
|
|
//#endregion
|
|
export { createAttributeList, matchTag, parseAttributeList, parseByteRange, parseCodecs, parseExtInfDuration, parseFrameRate, parseResolution };
|
|
|
|
//# sourceMappingURL=parse-attributes.js.map
|