mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-19 04:21:35 +00:00
* deps: update linkedom
* refactor!: remove YTNodeGenerator in favour of namespaced pure functions
BREAKING CHANGES:
- Removes `YTNodeGenerator` from `import('youtubei.js').Generator` and exposes its functions directly in `import('youtubei.js').Generator`
* refactor!: replace Parser class with pure functions
- Remove Parser class in favour of pure functions
- Merge duplicate classes `AppendContinuationItemsAction` into a single class
- Move continuation parsers into a seperate file
- Add better custom logging support to parser methods as per issue #460
* refactor!: replace Proto class with pure functions
* chore: update package-lock.json
* refactor!: replace FormatUtils with pure functions and JSX components
- Replace linkedom DASH manifest generation with a dependency free JSX implementation
- Remove FormatUtils class in favour of pure functions
- Remove DOMParser requirement
- Remove duplicate types
* refactor: implement changes from #462
* chore: lint
* fix: deno support
* fix: render valid xml document
* fix: wrong function call in DashUtils
* fix: typo in parser
Co-authored-by: LuanRT <luan.lrt4@gmail.com>
* refactor!: move streaming info logic into seperate function
This allows users to access the same data available in the dash manifest while also simplifying the manifest generation
* chore: lint
* refactor: readability improvements & fixes
Remove redundant getAudioTrackGroups
General readability improvements in StreamingInfo.ts
Share response object between `getBitrate` and `getMimeType` as to not make duplicate requests
* build: remove unnecessary step in deno build
Co-authored-by: absidue <48293849+absidue@users.noreply.github.com>
* refactor: move types to `types` directory
* docs: add back comments lost during refactor
* chore: lint
---------
Co-authored-by: LuanRT <luan.lrt4@gmail.com>
Co-authored-by: absidue <48293849+absidue@users.noreply.github.com>
105 lines
2.9 KiB
TypeScript
105 lines
2.9 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-namespace */
|
|
declare global {
|
|
namespace JSX {
|
|
interface IntrinsicElements {
|
|
[key: string]: DashProps;
|
|
}
|
|
}
|
|
}
|
|
|
|
export type DashChild = (DashNode | (DashNode | Promise<DashNode | DashNode[]>) | Promise<DashNode | DashNode[]>);
|
|
export interface DashProps {
|
|
[key: string]: unknown,
|
|
children?: DashChild[]
|
|
}
|
|
|
|
export interface DashNode {
|
|
type: string,
|
|
props: DashProps,
|
|
}
|
|
|
|
const XML_CHARACTER_MAP = {
|
|
'&': '&',
|
|
'"': '"',
|
|
'\'': ''',
|
|
'<': '<',
|
|
'>': '>'
|
|
} as const;
|
|
|
|
function escapeXMLString(str: string) {
|
|
return str.replace(/([&"<>'])/g, (_, item: keyof typeof XML_CHARACTER_MAP) => {
|
|
return XML_CHARACTER_MAP[item];
|
|
});
|
|
}
|
|
|
|
function normalizeTag(tag: string) {
|
|
if (tag === 'mpd') return 'MPD';
|
|
if (tag === 'base-url') return 'BaseURL';
|
|
|
|
const sections = tag.split('-');
|
|
return sections.map((section) => section.charAt(0).toUpperCase() + section.slice(1)).join('');
|
|
}
|
|
|
|
export function createElement(
|
|
tagNameOrFunction: string | ((props: DashProps) => DashNode | Promise<DashNode>),
|
|
props: { [key: string] : unknown } | null | undefined,
|
|
...children: DashChild[]
|
|
): DashNode | Promise<DashNode> {
|
|
const normalizedChildren = children.flat().map((child) => typeof child === 'string' ? createTextElement(child) : child);
|
|
|
|
if (typeof tagNameOrFunction === 'function') {
|
|
return tagNameOrFunction({ ...props, children: normalizedChildren });
|
|
}
|
|
|
|
return {
|
|
type: normalizeTag(tagNameOrFunction),
|
|
props: {
|
|
...props,
|
|
children: normalizedChildren
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createTextElement(text: string): DashNode {
|
|
return {
|
|
type: 'TEXT_ELEMENT',
|
|
props: { nodeValue: text }
|
|
};
|
|
}
|
|
|
|
export async function renderElementToString(element: DashNode): Promise<string> {
|
|
if (element.type === 'TEXT_ELEMENT')
|
|
return escapeXMLString(typeof element.props.nodeValue === 'string' ? element.props.nodeValue : '');
|
|
|
|
let dom = `<${element.type}`;
|
|
|
|
if (element.props) {
|
|
const properties = Object.keys(element.props)
|
|
.filter((key) => ![ 'children', 'nodeValue' ].includes(key) && element.props[key] !== undefined)
|
|
.map((name) => `${name}="${escapeXMLString(`${element.props[name]}`)}"`);
|
|
|
|
if (properties.length > 0)
|
|
dom += ` ${properties.join(' ')}`;
|
|
}
|
|
|
|
if (element.props.children) {
|
|
const children = await Promise.all((await Promise.all(element.props.children.flat())).flat().filter((child) => !!child).map((child) => renderElementToString(child)));
|
|
if (children.length > 0) {
|
|
dom += `>${children.join('')}</${element.type}>`;
|
|
return dom;
|
|
}
|
|
}
|
|
|
|
return `${dom}/>`;
|
|
}
|
|
|
|
export async function renderToString(root: DashNode | Promise<DashNode>) {
|
|
const dom = await renderElementToString(await root);
|
|
|
|
return `<?xml version="1.0" encoding="utf-8"?>${dom}`;
|
|
}
|
|
|
|
export function Fragment(props: DashProps) {
|
|
return props.children;
|
|
}
|