Skip to content

[pull] main from LuanRT:main #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 88 additions & 26 deletions dev-scripts/enum-optimising-transformer.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,47 +59,106 @@ module.exports = (program, pluginConfig, { ts: tsInstance }) => {

if (isNumeric) {
if (hasMinus) {
properties.push(
factory.createPropertyAssignment(
name,
factory.createPrefixUnaryExpression(
tsInstance.SyntaxKind.MinusToken,
factory.createNumericLiteral(value)
)
),
factory.createPropertyAssignment(
factory.createStringLiteral(`-${value}`),
factory.createStringLiteral(name)
const mapping = factory.createPropertyAssignment(
name,
factory.createPrefixUnaryExpression(
tsInstance.SyntaxKind.MinusToken,
factory.createNumericLiteral(value)
)
)

tsInstance.setOriginalNode(mapping, member)
tsInstance.setTextRange(mapping, member)

tsInstance.setOriginalNode(mapping.name, member.name)
tsInstance.setTextRange(mapping.name, member.name)

if (member.initializer) {
tsInstance.setOriginalNode(mapping.initializer, member.initializer)
tsInstance.setTextRange(mapping.initializer, member.initializer)
}

const reverseMapping = factory.createPropertyAssignment(
factory.createStringLiteral(`-${value}`),
factory.createStringLiteral(name)
)

tsInstance.setOriginalNode(reverseMapping, member)
tsInstance.setTextRange(reverseMapping, member)

tsInstance.setOriginalNode(reverseMapping.initializer, member.name)
tsInstance.setTextRange(reverseMapping.initializer, member.name)

if (member.initializer) {
tsInstance.setOriginalNode(reverseMapping.name, member.initializer)
tsInstance.setTextRange(reverseMapping.name, member.initializer)
}

properties.push(mapping, reverseMapping)
} else {
properties.push(
factory.createPropertyAssignment(
name,
factory.createNumericLiteral(value)
),
factory.createPropertyAssignment(
value,
factory.createStringLiteral(name)
)
const mapping = factory.createPropertyAssignment(
name,
factory.createNumericLiteral(value)
)

tsInstance.setOriginalNode(mapping, member)
tsInstance.setTextRange(mapping, member)

tsInstance.setOriginalNode(mapping.name, member.name)
tsInstance.setTextRange(mapping.name, member.name)

if (member.initializer) {
tsInstance.setOriginalNode(mapping.initializer, member.initializer)
tsInstance.setTextRange(mapping.initializer, member.initializer)
}

const reverseMapping = factory.createPropertyAssignment(
value,
factory.createStringLiteral(name)
)

tsInstance.setOriginalNode(reverseMapping, member)
tsInstance.setTextRange(reverseMapping, member)

tsInstance.setOriginalNode(reverseMapping.initializer, member.name)
tsInstance.setTextRange(reverseMapping.initializer, member.name)

if (member.initializer) {
tsInstance.setOriginalNode(reverseMapping.name, member.initializer)
tsInstance.setTextRange(reverseMapping.name, member.initializer)
}

properties.push(mapping, reverseMapping)
}
} else {
properties.push(
factory.createPropertyAssignment(
name,
factory.createStringLiteral(value)
)
const mapping = factory.createPropertyAssignment(
name,
factory.createStringLiteral(value)
)

tsInstance.setOriginalNode(mapping, member)
tsInstance.setTextRange(mapping, member)

tsInstance.setOriginalNode(mapping.name, member.name)
tsInstance.setTextRange(mapping.name, member.name)

tsInstance.setOriginalNode(mapping.initializer, member.initializer)
tsInstance.setTextRange(mapping.initializer, member.initializer)

properties.push(mapping)
}
}

const convertedNameIdentifier = factory.createIdentifier(node.name.text)
tsInstance.setOriginalNode(convertedNameIdentifier, node.name)
tsInstance.setTextRange(convertedNameIdentifier, node.name)

const convertedEnum = factory.createVariableStatement(
variableStatementModifiers,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier(node.name.text),
convertedNameIdentifier,
undefined,
undefined,
factory.createObjectLiteralExpression(
Expand All @@ -112,6 +171,9 @@ module.exports = (program, pluginConfig, { ts: tsInstance }) => {
)
)

tsInstance.setOriginalNode(convertedEnum, node)
tsInstance.setTextRange(convertedEnum, node)

return convertedEnum
}

Expand Down
20 changes: 17 additions & 3 deletions src/core/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export enum ClientType {
ANDROID_MUSIC = 'ANDROID_MUSIC',
ANDROID_CREATOR = 'ANDROID_CREATOR',
TV = 'TVHTML5',
TV_SIMPLY = 'TVHTML5_SIMPLY',
TV_EMBEDDED = 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
WEB_EMBEDDED = 'WEB_EMBEDDED_PLAYER',
WEB_CREATOR = 'WEB_CREATOR'
Expand Down Expand Up @@ -346,9 +347,20 @@ export default class Session extends EventEmitter {
if (session_args.user_agent)
result.context.client.userAgent = session_args.user_agent;

if (session_args.client_name) {
const client = Object.values(Constants.CLIENTS).find((c) => c.NAME === session_args.client_name);
if (client) {
result.context.client.clientName = client.NAME;
result.context.client.clientVersion = client.VERSION;
} else {
Log.warn(TAG, `Unknown client name: ${session_args.client_name}. Using default WEB client.`);
result.context.client.clientName = ClientType.WEB;
result.context.client.clientVersion = Constants.CLIENTS.WEB.VERSION;
}
}

result.context.client.timeZone = session_args.time_zone;
result.context.client.platform = session_args.device_category.toUpperCase();
result.context.client.clientName = session_args.client_name;
result.context.user.enableSafetyMode = session_args.enable_safety_mode;

return result;
Expand Down Expand Up @@ -412,7 +424,7 @@ export default class Session extends EventEmitter {
user_agent: user_agent,
visitor_data: visitor_data || ProtoUtils.encodeVisitorData(generateRandomString(11), Math.floor(Date.now() / 1000)),
client_name: client_name,
client_version: Constants.CLIENTS.WEB.VERSION,
client_version: Object.values(Constants.CLIENTS).filter((v) => v.NAME === client_name)[0]?.VERSION ?? Constants.CLIENTS.WEB.VERSION,
device_category: device_category.toUpperCase(),
os_name: 'Windows',
os_version: '10.0',
Expand Down Expand Up @@ -554,7 +566,9 @@ export default class Session extends EventEmitter {
visitor_data: options.visitor_data || device_info[13],
user_agent: options.user_agent,
client_name: options.client_name,
client_version: device_info[16],
client_version: Object.values(Constants.CLIENTS).filter(
(v) => v.NAME === options.client_name
)[0]?.VERSION ?? device_info[16],
os_name: device_info[17],
os_version: device_info[18],
time_zone: device_info[79] || options.time_zone,
Expand Down
52 changes: 37 additions & 15 deletions src/core/mixins/MediaInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import type PlayerLiveStoryboardSpec from '../../parser/classes/PlayerLiveStoryb
import type PlayerStoryboardSpec from '../../parser/classes/PlayerStoryboardSpec.js';

export default class MediaInfo {
readonly #page: [IPlayerResponse, INextResponse?];
readonly #page: [ IPlayerResponse, INextResponse? ];
readonly #actions: Actions;
readonly #cpn: string;
readonly #playback_tracking?: IPlaybackTracking;
Expand All @@ -46,7 +46,7 @@ export default class MediaInfo {
public playability_status?: IPlayabilityStatus;
public player_config?: IPlayerConfig;

constructor(data: [ApiResponse, ApiResponse?], actions: Actions, cpn: string) {
constructor(data: [ ApiResponse, ApiResponse? ], actions: Actions, cpn: string) {
this.#actions = actions;

const info = Parser.parseResponse<IPlayerResponse>(data[0].data.playerResponse ? data[0].data.playerResponse : data[0].data);
Expand Down Expand Up @@ -98,13 +98,18 @@ export default class MediaInfo {

/**
* Generates a DASH manifest from the streaming data.
* @param url_transformer - Function to transform the URLs.
* @param format_filter - Function to filter the formats.
* @param options - Additional options to customise the manifest generation
* @param options
* @returns DASH manifest
*/
async toDash(url_transformer?: URLTransformer, format_filter?: FormatFilter, options: DashOptions = { include_thumbnails: false }): Promise<string> {
async toDash(options: {
url_transformer?: URLTransformer;
format_filter?: FormatFilter;
include_thumbnails?: boolean;
captions_format?: string;
manifest_options?: DashOptions;
} = {}): Promise<string> {
const player_response = this.#page[0];
const manifest_options = options.manifest_options || {};

if (player_response.video_details && (player_response.video_details.is_live)) {
throw new InnertubeError('Generating DASH manifests for live videos is not supported. Please use the DASH and HLS manifests provided by YouTube in `streaming_data.dash_manifest_url` and `streaming_data.hls_manifest_url` instead.');
Expand All @@ -113,25 +118,25 @@ export default class MediaInfo {
let storyboards;
let captions;

if (options.include_thumbnails && player_response.storyboards) {
if (manifest_options.include_thumbnails && player_response.storyboards) {
storyboards = player_response.storyboards;
}

if (typeof options.captions_format === 'string' && player_response.captions?.caption_tracks) {
if (typeof manifest_options.captions_format === 'string' && player_response.captions?.caption_tracks) {
captions = player_response.captions.caption_tracks;
}

return FormatUtils.toDash(
this.streaming_data,
this.page[0].video_details?.is_post_live_dvr,
url_transformer,
format_filter,
options.url_transformer,
options.format_filter,
this.#cpn,
this.#actions.session.player,
this.#actions,
storyboards,
captions,
options
manifest_options
);
}

Expand Down Expand Up @@ -202,10 +207,7 @@ export default class MediaInfo {
return new TranscriptInfo(this.actions, response);
}

/**
* Adds video to the watch history.
*/
async addToWatchHistory(client_name: string = Constants.CLIENTS.WEB.NAME, client_version: string = Constants.CLIENTS.WEB.VERSION, replacement = 'https://www.'): Promise<Response> {
async addToWatchHistory(client_name?: string, client_version?: string, replacement = 'https://www.'): Promise<Response> {
if (!this.#playback_tracking)
throw new InnertubeError('Playback tracking not available');

Expand All @@ -218,6 +220,26 @@ export default class MediaInfo {

const url = this.#playback_tracking.videostats_playback_url.replace('https://s.', replacement);

return await this.#actions.stats(url, {
client_name: client_name || Constants.CLIENTS.WEB.NAME,
client_version: client_version || Constants.CLIENTS.WEB.VERSION
}, url_params);
}

async updateWatchTime(startTime: number, client_name: string = Constants.CLIENTS.WEB.NAME, client_version: string = Constants.CLIENTS.WEB.VERSION, replacement = 'https://www.'): Promise<Response> {
if (!this.#playback_tracking)
throw new InnertubeError('Playback tracking not available');

const url_params = {
cpn: this.#cpn,
st: startTime.toFixed(3),
et: startTime.toFixed(3),
cmt: startTime.toFixed(3),
final: '1'
};

const url = this.#playback_tracking.videostats_watchtime_url.replace('https://s.', replacement);

return await this.#actions.stats(url, {
client_name,
client_version
Expand Down
6 changes: 3 additions & 3 deletions src/parser/classes/HeatMarker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default class HeatMarker extends YTNode {

constructor(data: RawNode) {
super();
this.time_range_start_millis = data.timeRangeStartMillis;
this.marker_duration_millis = data.markerDurationMillis;
this.heat_marker_intensity_score_normalized = data.heatMarkerIntensityScoreNormalized;
this.time_range_start_millis = Number.parseInt(data.startMillis, 10);
this.marker_duration_millis = Number.parseInt(data.durationMillis, 10);
this.heat_marker_intensity_score_normalized = data.intensityScoreNormalized;
}
}
3 changes: 2 additions & 1 deletion src/parser/classes/Heatmap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Parser, type RawNode } from '../index.js';
import HeatMarker from './HeatMarker.js';
import { type ObservedArray, YTNode } from '../helpers.js';
import TimedMarkerDecoration from './TimedMarkerDecoration.js';

export default class Heatmap extends YTNode {
static type = 'Heatmap';
Expand All @@ -17,6 +18,6 @@ export default class Heatmap extends YTNode {
this.min_height_dp = data.minHeightDp;
this.show_hide_animation_duration_millis = data.showHideAnimationDurationMillis;
this.heat_markers = Parser.parseArray(data.heatMarkers, HeatMarker);
this.heat_markers_decorations = Parser.parseArray(data.heatMarkersDecorations);
this.heat_markers_decorations = Parser.parseArray(data.heatMarkersDecorations, TimedMarkerDecoration);
}
}
Loading