mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
build(spf): build26 from 45504a2b
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
||||
//#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
|
||||
Reference in New Issue
Block a user