mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-06-19 04:21:35 +00:00
feat: add method to change notification preferences and get unseen notifications count
This commit is contained in:
237
lib/Actions.js
237
lib/Actions.js
@@ -4,168 +4,89 @@ const Axios = require('axios');
|
||||
const Utils = require('./Utils');
|
||||
const Constants = require('./Constants');
|
||||
|
||||
async function subscribe(session, video_id, channel_id) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to subscribe to a channel');
|
||||
let data = {
|
||||
context: session.context,
|
||||
channelIds: [channel_id]
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/subscription/subscribe${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
async function engage(session, engagement_type, args = {}) {
|
||||
if (!session.logged_in) throw new Error('You must be signed-in to interact with a video/channel');
|
||||
let data = {};
|
||||
switch (engagement_type) {
|
||||
case 'like/like':
|
||||
case 'like/dislike':
|
||||
case 'like/removelike':
|
||||
data = {
|
||||
context: session.context,
|
||||
target: {
|
||||
videoId: args.video_id
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'subscription/subscribe':
|
||||
case 'subscription/unsubscribe':
|
||||
data = {
|
||||
context: session.context,
|
||||
channelIds: [args.channel_id]
|
||||
};
|
||||
break;
|
||||
case 'comment/create_comment':
|
||||
data = {
|
||||
context: session.context,
|
||||
commentText: args.text,
|
||||
createCommentParams: Utils.encodeVideoId(args.video_id)
|
||||
};
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/${engagement_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: args.video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status
|
||||
};
|
||||
}
|
||||
|
||||
async function unsubscribe(session, video_id, channel_id) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to unsubscribe from a channel');
|
||||
let data = {
|
||||
context: session.context,
|
||||
channelIds: [channel_id]
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/subscription/unsubscribe${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function likeVideo(session, video_id) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to like a video');
|
||||
let data = {
|
||||
context: session.context,
|
||||
target: {
|
||||
videoId: video_id
|
||||
}
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/like${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function dislikeVideo(session, video_id) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to like a video');
|
||||
let data = {
|
||||
context: session.context,
|
||||
target: {
|
||||
videoId: video_id
|
||||
}
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/dislike${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function removeLike(session, video_id) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to remove a like/dislike.');
|
||||
let data = {
|
||||
context: session.context,
|
||||
target: {
|
||||
videoId: video_id
|
||||
}
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/like/removelike${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function commentVideo(session, video_id, text) {
|
||||
if (!text) throw new Error('No text was provided');
|
||||
if (!session.logged_in) throw new Error('You must be logged in to post a comment.');
|
||||
|
||||
let data = {
|
||||
context: session.context,
|
||||
commentText: text,
|
||||
createCommentParams: Utils.encodeId(video_id)
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/comment/create_comment${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else if (response.data.responseContext) {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function getNotifications(session) {
|
||||
async function notifications(session, action_type, args = {}) {
|
||||
if (!session.logged_in) throw new Error('You must be logged in to fetch notifications');
|
||||
let data = {
|
||||
context: session.context,
|
||||
notificationsMenuRequestType: 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'
|
||||
};
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/notification/get_notification_menu${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
|
||||
if (response instanceof Error) {
|
||||
return {
|
||||
success: false,
|
||||
status_code: response.response.status,
|
||||
message: response.message
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
data: response.data
|
||||
};
|
||||
let data;
|
||||
switch (action_type) {
|
||||
case 'modify_channel_preference':
|
||||
let pref_types = { ALL: 0, NONE: 1, PERSONALIZED: 2 };
|
||||
data = {
|
||||
context: session.context,
|
||||
params: Utils.encodeChannelId(args.channel_id, pref_types[args.pref.toUpperCase()])
|
||||
};
|
||||
break;
|
||||
case 'get_notification_menu':
|
||||
data = {
|
||||
context: session.context,
|
||||
notificationsMenuRequestType: 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'
|
||||
};
|
||||
break;
|
||||
case 'get_unseen_count':
|
||||
data = {
|
||||
context: session.context
|
||||
};
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/notification/${action_type}${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
|
||||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
|
||||
if (action_type === 'modify_channel_preference') return { success: true, status_code: response.status };
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
data: response.data
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { subscribe, unsubscribe, likeVideo, dislikeVideo, removeLike, commentVideo, getNotifications };
|
||||
async function getContinuation(session, continuation_token) {
|
||||
let data = { context: session.context };
|
||||
const response = await Axios.post(`${Constants.urls.YT_BASE_URL}/youtubei/v1/next/${session.logged_in && session.cookie.length < 1 ? '' : `?key=${session.key}`}`, JSON.stringify(data), Constants.innertube_request_opts({ session, data, desktop: true })).catch((error) => error);
|
||||
if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message };
|
||||
return {
|
||||
success: true,
|
||||
status_code: response.status,
|
||||
data: response.data
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { engage, notifications, getContinuation };
|
||||
@@ -161,15 +161,16 @@ const formatVideoData = (data, context, desktop) => {
|
||||
video_details.description = data[2].playerResponse.videoDetails.shortDescription;
|
||||
video_details.thumbnail = data[2].playerResponse.videoDetails.thumbnail.thumbnails.slice(-1)[0];
|
||||
|
||||
// actions
|
||||
video_details.like = like => {};
|
||||
video_details.dislike = dislike => {};
|
||||
video_details.removeLike = remove_like => {};
|
||||
video_details.subscribe = subscribe => {};
|
||||
video_details.unsubscribe = unsubscribe => {};
|
||||
video_details.comment = comment => {};
|
||||
// Actions
|
||||
video_details.like = () => {};
|
||||
video_details.dislike = () => {};
|
||||
video_details.removeLike = () => {};
|
||||
video_details.subscribe = () => {};
|
||||
video_details.unsubscribe = () => {};
|
||||
video_details.comment = () => {};
|
||||
video_details.setNotificationPref = () => {};
|
||||
|
||||
// additional metadata
|
||||
// Additional metadata
|
||||
video_details.metadata = metadata;
|
||||
}
|
||||
return video_details;
|
||||
|
||||
@@ -137,20 +137,22 @@ class Innertube extends EventEmitter {
|
||||
const data = await this.requestVideoInfo(id, false);
|
||||
const video_data = Constants.formatVideoData(data, this, false);
|
||||
|
||||
video_data.like = like => Actions.likeVideo(this, id);
|
||||
video_data.dislike = dislike => Actions.dislikeVideo(this, id);
|
||||
video_data.removeLike = remove_like => Actions.removeLike(this, id);
|
||||
video_data.subscribe = subscribe => Actions.subscribe(this, id, video_data.metadata.channel_id);
|
||||
video_data.unsubscribe = unsubscribe => Actions.unsubscribe(this, id, video_data.metadata.channel_id);
|
||||
video_data.comment = comment => Actions.commentVideo(this, id, comment);
|
||||
video_data.like = () => Actions.engage(this, 'like/like', { video_id: id });
|
||||
video_data.dislike = () => Actions.engage(this, 'like/dislike', { video_id: id });
|
||||
video_data.removeLike = () => Actions.engage(this, 'like/removelike', { video_id: id });
|
||||
video_data.subscribe = () => Actions.engage(this, 'subscription/subscribe', { video_id: id, channel_id: video_data.metadata.channel_id });
|
||||
video_data.unsubscribe = () => Actions.engage(this, 'subscription/unsubscribe', { video_id: id, channel_id: video_data.metadata.channel_id });
|
||||
video_data.comment = text => Actions.engage(this, 'comment/create_comment', { video_id: id, text });
|
||||
video_data.setNotificationPref = pref => Actions.notifications(this, 'modify_channel_preference', { channel_id: video_data.metadata.channel_id, pref: pref || 'NONE' });
|
||||
|
||||
return video_data;
|
||||
}
|
||||
|
||||
async getNotifications() {
|
||||
const response = await Actions.getNotifications(this);
|
||||
const contents = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[0].multiPageMenuNotificationSectionRenderer.items;
|
||||
return contents.map((notification) => {
|
||||
const response = await Actions.notifications(this, 'get_notification_menu');
|
||||
const contents = response.data.actions[0].openPopupAction.popup.multiPageMenuRenderer.sections[0];
|
||||
if (!contents.multiPageMenuNotificationSectionRenderer) return { error: 'You don\'t have any notification.' };
|
||||
return contents.multiPageMenuNotificationSectionRenderer.items.map((notification) => {
|
||||
if (!notification.notificationRenderer) return;
|
||||
notification = notification.notificationRenderer;
|
||||
return {
|
||||
@@ -159,13 +161,18 @@ class Innertube extends EventEmitter {
|
||||
channel_name: notification.contextualMenu.menuRenderer.items[1].menuServiceItemRenderer.text.runs[1].text,
|
||||
channel_thumbnail: notification.thumbnail.thumbnails[0],
|
||||
video_thumbnail: notification.videoThumbnail.thumbnails[0],
|
||||
video_url: 'https://youtu.be/' + notification.navigationEndpoint.watchEndpoint.videoId,
|
||||
video_url: `https://youtu.be/${notification.navigationEndpoint.watchEndpoint.videoId}`,
|
||||
read: notification.read,
|
||||
notification_id: notification.notificationId,
|
||||
};
|
||||
}).filter((notification_block) => notification_block);
|
||||
}
|
||||
|
||||
async getUnseenNotificationsCount() {
|
||||
const response = await Actions.notifications(this, 'get_unseen_count');
|
||||
return response.data.unseenCount;
|
||||
}
|
||||
|
||||
async requestVideoInfo(id, desktop) {
|
||||
let response;
|
||||
if (!desktop) {
|
||||
|
||||
19
lib/Utils.js
19
lib/Utils.js
@@ -39,8 +39,21 @@ function createFunction(input, raw_code) { // I hate this
|
||||
return new Function(input, raw_code);
|
||||
}
|
||||
|
||||
function encodeId(id) {
|
||||
return encodeURI(new Buffer.from(`` + id + `*`).toString('base64').replace('==', '') + 'BQBw==');
|
||||
function encodeChannelId(id, notification_pref) {
|
||||
const buff_start = `
|
||||
`;
|
||||
const buff_end = [
|
||||
``, // all
|
||||
``, // none
|
||||
``, // personalized
|
||||
];
|
||||
|
||||
let buff = Buffer.from(`${buff_start}${id}${buff_end[notification_pref]}`);
|
||||
return encodeURIComponent(`${buff.toString('base64')}GAAgBA==`);
|
||||
}
|
||||
|
||||
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, createFunction, encodeId };
|
||||
function encodeVideoId(id) {
|
||||
return encodeURIComponent(`${Buffer.from(`` + id + `*`).toString('base64').slice(0, -1)}BQBw==`);
|
||||
}
|
||||
|
||||
module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, createFunction, encodeVideoId, encodeChannelId };
|
||||
Reference in New Issue
Block a user