@np-dev/youtubei-js
Version:
A wrapper around YouTube's private API. Supports YouTube, YouTube Music, YouTube Kids and YouTube Studio (WIP).
229 lines • 13.6 kB
JavaScript
var _LiveChat_instances, _LiveChat_actions, _LiveChat_video_id, _LiveChat_channel_id, _LiveChat_continuation, _LiveChat_mcontinuation, _LiveChat_retry_count, _LiveChat_pollLivechat, _LiveChat_emitSmoothedActions, _LiveChat_pollMetadata, _LiveChat_wait;
import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import EventEmitter from '../../utils/EventEmitterLike.js';
import { Parser, LiveChatContinuation } from '../index.js';
import SmoothedQueue from './SmoothedQueue.js';
import AddChatItemAction from '../classes/livechat/AddChatItemAction.js';
import UpdateDateTextAction from '../classes/livechat/UpdateDateTextAction.js';
import UpdateDescriptionAction from '../classes/livechat/UpdateDescriptionAction.js';
import UpdateTitleAction from '../classes/livechat/UpdateTitleAction.js';
import UpdateToggleButtonTextAction from '../classes/livechat/UpdateToggleButtonTextAction.js';
import UpdateViewershipAction from '../classes/livechat/UpdateViewershipAction.js';
import * as Proto from '../../proto/index.js';
import { InnertubeError, Platform } from '../../utils/Utils.js';
import ItemMenu from './ItemMenu.js';
import { NavigationEndpoint } from '../nodes.js';
class LiveChat extends EventEmitter {
constructor(video_info) {
var _a, _b;
super();
_LiveChat_instances.add(this);
_LiveChat_actions.set(this, void 0);
_LiveChat_video_id.set(this, void 0);
_LiveChat_channel_id.set(this, void 0);
_LiveChat_continuation.set(this, void 0);
_LiveChat_mcontinuation.set(this, void 0);
_LiveChat_retry_count.set(this, 0);
this.running = false;
this.is_replay = false;
__classPrivateFieldSet(this, _LiveChat_video_id, video_info.basic_info.id, "f");
__classPrivateFieldSet(this, _LiveChat_channel_id, video_info.basic_info.channel_id, "f");
__classPrivateFieldSet(this, _LiveChat_actions, video_info.actions, "f");
__classPrivateFieldSet(this, _LiveChat_continuation, (_a = video_info.livechat) === null || _a === void 0 ? void 0 : _a.continuation, "f");
this.is_replay = ((_b = video_info.livechat) === null || _b === void 0 ? void 0 : _b.is_replay) || false;
this.smoothed_queue = new SmoothedQueue();
this.smoothed_queue.callback = (actions) => __awaiter(this, void 0, void 0, function* () {
if (!actions.length) {
// Wait 2 seconds before requesting an incremental continuation if the action group is empty.
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, 2000);
}
else if (actions.length < 10) {
// If there are less than 10 actions, wait until all of them are emitted.
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_emitSmoothedActions).call(this, actions);
}
else if (this.is_replay) {
/**
* NOTE: Live chat replays require data from the video player for actions to be emitted timely
* and as we don't have that, this ends up being quite innacurate.
*/
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_emitSmoothedActions).call(this, actions);
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, 2000);
}
else {
// There are more than 10 actions, emit them asynchonously so we can request the next incremental continuation.
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_emitSmoothedActions).call(this, actions);
}
if (this.running) {
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollLivechat).call(this);
}
});
}
on(type, listener) {
super.on(type, listener);
}
once(type, listener) {
super.once(type, listener);
}
start() {
if (!this.running) {
this.running = true;
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollLivechat).call(this);
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollMetadata).call(this);
}
}
stop() {
this.smoothed_queue.clear();
this.running = false;
}
/**
* Sends a message.
* @param text - Text to send.
*/
sendMessage(text) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield __classPrivateFieldGet(this, _LiveChat_actions, "f").execute('/live_chat/send_message', {
params: Proto.encodeMessageParams(__classPrivateFieldGet(this, _LiveChat_channel_id, "f"), __classPrivateFieldGet(this, _LiveChat_video_id, "f")),
richMessage: { textSegments: [{ text }] },
clientMessageId: Platform.shim.uuidv4(),
client: 'ANDROID',
parse: true
});
if (!response.actions)
throw new InnertubeError('Unexpected response from send_message', response);
return response.actions.array().as(AddChatItemAction);
});
}
/**
* Applies given filter to the live chat.
* @param filter - Filter to apply.
*/
applyFilter(filter) {
var _a, _b, _c, _d, _e, _f, _g;
if (!this.initial_info)
throw new InnertubeError('Cannot apply filter before initial info is retrieved.');
const menu_items = (_c = (_b = (_a = this.initial_info) === null || _a === void 0 ? void 0 : _a.header) === null || _b === void 0 ? void 0 : _b.view_selector) === null || _c === void 0 ? void 0 : _c.sub_menu_items;
if (filter === 'TOP_CHAT') {
if ((_d = menu_items === null || menu_items === void 0 ? void 0 : menu_items.at(0)) === null || _d === void 0 ? void 0 : _d.selected)
return;
__classPrivateFieldSet(this, _LiveChat_continuation, (_e = menu_items === null || menu_items === void 0 ? void 0 : menu_items.at(0)) === null || _e === void 0 ? void 0 : _e.continuation, "f");
}
else {
if ((_f = menu_items === null || menu_items === void 0 ? void 0 : menu_items.at(1)) === null || _f === void 0 ? void 0 : _f.selected)
return;
__classPrivateFieldSet(this, _LiveChat_continuation, (_g = menu_items === null || menu_items === void 0 ? void 0 : menu_items.at(1)) === null || _g === void 0 ? void 0 : _g.continuation, "f");
}
}
/**
* Retrieves given chat item's menu.
*/
getItemMenu(item) {
return __awaiter(this, void 0, void 0, function* () {
if (!item.hasKey('menu_endpoint') || !item.key('menu_endpoint').isInstanceof(NavigationEndpoint))
throw new InnertubeError('This item does not have a menu.', item);
const response = yield item.key('menu_endpoint').instanceof(NavigationEndpoint).call(__classPrivateFieldGet(this, _LiveChat_actions, "f"), { parse: true });
if (!response)
throw new InnertubeError('Could not retrieve item menu.', item);
return new ItemMenu(response, __classPrivateFieldGet(this, _LiveChat_actions, "f"));
});
}
/**
* Equivalent to "clicking" a button.
*/
selectButton(button) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield button.endpoint.call(__classPrivateFieldGet(this, _LiveChat_actions, "f"), { parse: true });
return response;
});
}
}
_LiveChat_actions = new WeakMap(), _LiveChat_video_id = new WeakMap(), _LiveChat_channel_id = new WeakMap(), _LiveChat_continuation = new WeakMap(), _LiveChat_mcontinuation = new WeakMap(), _LiveChat_retry_count = new WeakMap(), _LiveChat_instances = new WeakSet(), _LiveChat_pollLivechat = function _LiveChat_pollLivechat() {
(() => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
const response = yield __classPrivateFieldGet(this, _LiveChat_actions, "f").execute(this.is_replay ? 'live_chat/get_live_chat_replay' : 'live_chat/get_live_chat', { continuation: __classPrivateFieldGet(this, _LiveChat_continuation, "f"), parse: true });
const contents = response.continuation_contents;
if (!contents) {
this.emit('error', new InnertubeError('Unexpected live chat incremental continuation response', response));
this.emit('end');
this.stop();
}
if (!(contents instanceof LiveChatContinuation)) {
this.stop();
this.emit('end');
return;
}
__classPrivateFieldSet(this, _LiveChat_continuation, contents.continuation.token, "f");
// Header only exists in the first request
if (contents.header) {
this.initial_info = contents;
this.emit('start', contents);
if (this.running)
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollLivechat).call(this);
}
else {
this.smoothed_queue.enqueueActionGroup(contents.actions);
}
__classPrivateFieldSet(this, _LiveChat_retry_count, 0, "f");
}
catch (err) {
this.emit('error', err);
if ((__classPrivateFieldSet(this, _LiveChat_retry_count, (_b = __classPrivateFieldGet(this, _LiveChat_retry_count, "f"), _a = _b++, _b), "f"), _a) < 10) {
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, 2000);
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollLivechat).call(this);
}
else {
this.emit('error', new InnertubeError('Reached retry limit for incremental continuation requests', err));
this.emit('end');
this.stop();
}
}
}))();
}, _LiveChat_emitSmoothedActions = function _LiveChat_emitSmoothedActions(action_queue) {
return __awaiter(this, void 0, void 0, function* () {
const base = 1E4;
let delay = action_queue.length < base / 80 ? 1 : Math.ceil(action_queue.length / (base / 80));
const emit_delay_ms = delay == 1 ? (delay = base / action_queue.length,
delay *= Math.random() + 0.5,
delay = Math.min(1E3, delay),
delay = Math.max(80, delay)) : delay = 80;
for (const action of action_queue) {
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, emit_delay_ms);
this.emit('chat-update', action);
}
});
}, _LiveChat_pollMetadata = function _LiveChat_pollMetadata() {
(() => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
try {
const payload = { videoId: __classPrivateFieldGet(this, _LiveChat_video_id, "f") };
if (__classPrivateFieldGet(this, _LiveChat_mcontinuation, "f")) {
payload.continuation = __classPrivateFieldGet(this, _LiveChat_mcontinuation, "f");
}
const response = yield __classPrivateFieldGet(this, _LiveChat_actions, "f").execute('/updated_metadata', payload);
const data = Parser.parseResponse(response.data);
__classPrivateFieldSet(this, _LiveChat_mcontinuation, (_a = data.continuation) === null || _a === void 0 ? void 0 : _a.token, "f");
this.metadata = {
title: ((_b = data.actions) === null || _b === void 0 ? void 0 : _b.array().firstOfType(UpdateTitleAction)) || ((_c = this.metadata) === null || _c === void 0 ? void 0 : _c.title),
description: ((_d = data.actions) === null || _d === void 0 ? void 0 : _d.array().firstOfType(UpdateDescriptionAction)) || ((_e = this.metadata) === null || _e === void 0 ? void 0 : _e.description),
views: ((_f = data.actions) === null || _f === void 0 ? void 0 : _f.array().firstOfType(UpdateViewershipAction)) || ((_g = this.metadata) === null || _g === void 0 ? void 0 : _g.views),
likes: ((_h = data.actions) === null || _h === void 0 ? void 0 : _h.array().firstOfType(UpdateToggleButtonTextAction)) || ((_j = this.metadata) === null || _j === void 0 ? void 0 : _j.likes),
date: ((_k = data.actions) === null || _k === void 0 ? void 0 : _k.array().firstOfType(UpdateDateTextAction)) || ((_l = this.metadata) === null || _l === void 0 ? void 0 : _l.date)
};
this.emit('metadata-update', this.metadata);
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, 5000);
if (this.running)
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollMetadata).call(this);
}
catch (err) {
yield __classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_wait).call(this, 2000);
if (this.running)
__classPrivateFieldGet(this, _LiveChat_instances, "m", _LiveChat_pollMetadata).call(this);
}
}))();
}, _LiveChat_wait = function _LiveChat_wait(ms) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve) => setTimeout(() => resolve(), ms));
});
};
export default LiveChat;
//# sourceMappingURL=LiveChat.js.map