From c5eea2b4ff92834ccca075f037063a5fa465dedc Mon Sep 17 00:00:00 2001 From: "luan.lrt4@gmail.com" Date: Mon, 4 Apr 2022 13:52:59 -0300 Subject: [PATCH] feat: implement pagination for all endpoints --- lib/Actions.js | 77 ++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/lib/Actions.js b/lib/Actions.js index a3a7d0b6..cc265b7d 100644 --- a/lib/Actions.js +++ b/lib/Actions.js @@ -14,7 +14,7 @@ const Constants = require('./Constants'); * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ async function engage(session, engagement_type, args = {}) { - if (!session.logged_in) throw new Error('You are not signed-in'); + if (!session.logged_in) throw new Error('You are not signed in'); const data = { context: session.context }; switch (engagement_type) { @@ -46,6 +46,7 @@ async function engage(session, engagement_type, args = {}) { data.actions = [ action ]; break; default: + throw new Utils.InnertubeError('Invalid action', action); } const response = await session.YTRequester.post(`/${engagement_type}`, JSON.stringify(data)).catch((error) => error); @@ -61,18 +62,18 @@ async function engage(session, engagement_type, args = {}) { * Accesses YouTube's various sections. * * @param {Innertube} session - A valid Innertube session. - * @param {string} action_type - Type of action. + * @param {string} action - Type of action. * @param {object} args - Action argumenets. * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ -async function browse(session, action_type, args = {}) { - if (!session.logged_in && action_type != 'home_feed' - && action_type !== 'lyrics' && action_type !== 'music_playlist' - && action_type !== 'playlist') - throw new Error('You are not signed-in'); +async function browse(session, action, args = {}) { + if (!session.logged_in && action != 'home_feed' + && action !== 'lyrics' && action !== 'music_playlist' + && action !== 'playlist') + throw new Error('You are not signed in'); const data = { context: session.context }; - switch (action_type) { + switch (action) { case 'account_notifications': data.browseId = 'SPaccount_notifications'; break; @@ -103,7 +104,11 @@ async function browse(session, action_type, args = {}) { case 'playlist': data.browseId = args.browse_id; break; + case 'continuation': + data.continuation = args.ctoken; + break; default: + throw new Utils.InnertubeError('Invalid action', action); } const requester = args.ytmusic && session.YTMRequester || session.YTRequester; @@ -122,15 +127,15 @@ async function browse(session, action_type, args = {}) { * Account settings endpoints. * * @param {Innertube} session - A valid Innertube session. - * @param {string} action_type - Type of action. + * @param {string} action - Type of action. * @param {object} args - Action argumenets. * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ -async function account(session, action_type, args = {}) { - if (!session.logged_in) throw new Error('You are not signed-in'); +async function account(session, action, args = {}) { + if (!session.logged_in) throw new Error('You are not signed in'); const data = {}; - switch (action_type) { + switch (action) { case 'account/account_menu': data.context = session.context; break; @@ -140,9 +145,10 @@ async function account(session, action_type, args = {}) { data.settingItemId = arts.setting_item_id; break; default: + throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/${action_type}`, JSON.stringify(data)).catch((error) => error); + const response = await session.YTRequester.post(`/${action}`, JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; return { @@ -156,12 +162,12 @@ async function account(session, action_type, args = {}) { * Accesses YouTube Music endpoints (/youtubei/v1/music/). * * @param {Innertube} session - A valid Innertube session. - * @param {string} action_type - Type of action. + * @param {string} action - Type of action. * @param {object} args - Action arguments. * @todo Implement more actions. * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ -async function music(session, action_type, args) { +async function music(session, action, args) { const context = JSON.parse(JSON.stringify(session.context)); // deep copy the context obj so we don't accidentally change it context.client.originalUrl = Constants.URLS.YT_MUSIC; @@ -170,16 +176,16 @@ async function music(session, action_type, args) { let data; - switch (action_type) { + switch (action) { case 'get_search_suggestions': data.context = context; data.input = args.input || ''; break; default: - break; + throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTMRequester.post(`/music/${action_type}`, JSON.stringify(data)).catch((error) => error); + const response = await session.YTMRequester.post(`/music/${action}`, JSON.stringify(data)).catch((error) => error); if (response instanceof Error) return { success: false, status_code: response.response.status, message: response.message }; return { @@ -198,13 +204,15 @@ async function music(session, action_type, args) { * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ async function search(session, client, args = {}) { - if (!args.query) throw new Error('No query was provided'); - const data = { context: session.context }; switch (client) { case 'YOUTUBE': - data.params = Proto.encodeSearchFilter(args.options.period, args.options.duration, args.options.order); - data.query = args.query; + if (args.query) { + data.params = Proto.encodeSearchFilter(args.options.period, args.options.duration, args.options.order); + data.query = args.query; + } else { + data.continuation = args.ctoken; + } break; case 'YTMUSIC': const context = JSON.parse(JSON.stringify(session.context)); // deep copy the context obj so we don't accidentally change it @@ -217,7 +225,7 @@ async function search(session, client, args = {}) { data.query = args.query; break; default: - break; + throw new Utils.InnertubeError('Invalid client', action); } const requester = client == 'YOUTUBE' && session.YTRequester || session.YTMRequester; @@ -235,15 +243,15 @@ async function search(session, client, args = {}) { * Interacts with YouTube's notification system. * * @param {Innertube} session - A valid Innertube session. - * @param {string} action_type - Type of action. + * @param {string} action - Type of action. * @param {object} args - Action arguments. * @returns {Promise.<{ success: boolean; status_code: number; data: object; message?: string }>} */ -async function notifications(session, action_type, args = {}) { - if (!session.logged_in) throw new Error('You are not signed-in'); +async function notifications(session, action, args = {}) { + if (!session.logged_in) throw new Error('You are not signed in'); const data = {}; - switch (action_type) { + switch (action) { case 'modify_channel_preference': const pref_types = { PERSONALIZED: 1, ALL: 2, NONE: 3 }; data.context = session.context; @@ -252,16 +260,18 @@ async function notifications(session, action_type, args = {}) { case 'get_notification_menu': data.context = session.context; data.notificationsMenuRequestType = 'NOTIFICATIONS_MENU_REQUEST_TYPE_INBOX'; + args.ctoken && (data.ctoken = args.ctoken); break; case 'get_unseen_count': data.context = session.context; break; default: + throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/notification/${action_type}`, JSON.stringify(data)).catch((err) => err); + const response = await session.YTRequester.post(`/notification/${action}`, JSON.stringify(data)).catch((err) => err); 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 }; + if (action === 'modify_channel_preference') return { success: true, status_code: response.status }; return { success: true, @@ -274,13 +284,13 @@ async function notifications(session, action_type, args = {}) { * Interacts with YouTube's livechat system. * * @param {Innertube} session - A valid Innertube session. - * @param {string} action_type - Type of action. + * @param {string} action - Type of action. * @param {object} args - Action arguments. * @returns {Promise.<{ success: boolean; data: object; message?: string }>} */ -async function livechat(session, action_type, args = {}) { +async function livechat(session, action, args = {}) { const data = {}; - switch (action_type) { + switch (action) { case 'live_chat/get_live_chat': data.context = session.context; data.continuation = args.ctoken; @@ -306,9 +316,10 @@ async function livechat(session, action_type, args = {}) { args.continuation && (data.continuation = args.continuation); break; default: + throw new Utils.InnertubeError('Invalid action', action); } - const response = await session.YTRequester.post(`/${action_type}`, JSON.stringify(data)).catch((err) => err); + const response = await session.YTRequester.post(`/${action}`, JSON.stringify(data)).catch((err) => err); if (response instanceof Error) return { success: false, message: response.message }; return { success: true, data: response.data };