Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

better-youtube-api

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

better-youtube-api - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0

2

out/entities/Channel.js

@@ -43,3 +43,3 @@ "use strict";

this.profilePictures = data.snippet.thumbnails;
this.datePublished = new Date(data.snippet.publishedAt);
this.dateCreated = new Date(data.snippet.publishedAt);
this.name = data.snippet.title;

@@ -46,0 +46,0 @@ this.about = data.snippet.description;

@@ -25,3 +25,3 @@ "use strict";

this.tags = playlist.snippet.tags;
this.itemCount = playlist.contentDetails.itemCount;
this.length = playlist.contentDetails.itemCount;
this.embedHtml = playlist.player.embedHtml;

@@ -37,3 +37,3 @@ }

this.creatorId = data.snippet.channelId;
this.datePublished = new Date(data.snippet.publishedAt);
this.dateCreated = new Date(data.snippet.publishedAt);
this.thumbnails = data.snippet.thumbnails;

@@ -52,9 +52,2 @@ this.full = data.kind === 'youtube#playlist';

/**
* Deprecated, use Playlist#fetchVideos instead.
* @param maxResults Maximum number of videos to fetch.
*/
getVideos(maxResults = -1) {
return this.fetchVideos(maxResults);
}
/**
* Fetches this playlist and reassigns this object to the new playlist object.

@@ -61,0 +54,0 @@ * Only useful if `this.full` is false, or if you want updated playlist info.

@@ -61,2 +61,6 @@ "use strict";

}
/**
* Fetches the video's comments and assigns them to Video#comments.
* @param maxResults The maximum amount of comments to fetch
*/
fetchComments(maxResults = -1) {

@@ -63,0 +67,0 @@ return __awaiter(this, void 0, void 0, function* () {

@@ -52,2 +52,8 @@ /**

/**
* Get a comment object from the ID of a comment.
* @param id The ID of the comment.
*/
public getComment (id: string): Promise<YTComment>
/**
* Get a video object from the url of a video.

@@ -79,6 +85,7 @@ * @param url The url of the video.

* Get `maxResults` comments on a video. Used mostly internally with `Video#fetchComments`.
* Can only get the last 100 comments on a video, due to a bug with the YouTube API.
* @param videoId The ID of the video.
* @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video.
*/
public getVideoComments (videoId: string, maxResults: number): Promise<YTComment[]>
public getVideoComments (videoId: string, maxResults?: number): Promise<YTComment[]>

@@ -90,3 +97,3 @@ /**

*/
public getCommentReplies (commentId: string, maxResults: number): Promise<YTComment[]>
public getCommentReplies (commentId: string, maxResults?: number): Promise<YTComment[]>
}

@@ -206,2 +213,8 @@

public fetch(): Promise<this & Video>
/**
* Fetches the video's comments and assigns them to Video#comments.
* @param maxResults The maximum amount of comments to fetch
*/
public fetchComments (maxResults?: number): Promise<YTComment[]>
}

@@ -272,3 +285,3 @@

*/
public datePublished: Date
public dateCreated: Date

@@ -286,3 +299,3 @@ /**

/**
* The channel's uploads. Only available after calling `Channel#getVideos()`
* The channel's uploads. Only available after calling `Channel#fetchVideos()`
*/

@@ -312,3 +325,3 @@ public videos: Playlist

*/
public getVideos(): Promise<Playlist>
public fetchVideos(): Promise<Playlist>
}

@@ -351,3 +364,3 @@

/**
* The videos in the playlist. Only available after calling `Playlist#getVideos()`.
* The videos in the playlist. Only available after calling `Playlist#fetchVideos()`.
*/

@@ -364,3 +377,3 @@ public videos: Video[]

*/
public datePublished: Date
public dateCreated: Date

@@ -381,3 +394,3 @@ /**

*/
public itemCount: number
public length: number

@@ -403,8 +416,2 @@ /**

/**
* Deprecated, use Playlist#fetchVideos instead.
* @param maxResults Maximum number of videos to fetch.
*/
public getVideos (maxResults?: number): Promise<Video[]>
/**
* Fetches this playlist and reassigns this object to the new playlist object.

@@ -482,3 +489,3 @@ * Only useful if `this.full` is false, or if you want updated playlist info.

/**
* The number of likes the comment has gotten.
* The number of likes the comment has received.
*/

@@ -499,3 +506,3 @@ public likes: number

/**
* Either the ID of the video that it commented on, or the ID of the
* Either the ID of the video that it is commenting on, or the ID of the
* comment it is replying to.

@@ -506,3 +513,5 @@ */

/**
* Replies to the comment.
* Replies directed to the comment. If the comment was fetched from a video,
* then this will be partially filled. You'll need to use Comment#fetchReplies
* to get all of the replies, though.
*/

@@ -509,0 +518,0 @@ public replies: YTComment[]

@@ -28,70 +28,2 @@ "use strict";

}
search(type, searchTerm, maxResults = 10) {
return __awaiter(this, void 0, void 0, function* () {
if (maxResults < 1 || maxResults > 50) {
return Promise.reject('Max results must be greater than 0 and less than or equal to 50');
}
const results = yield util_1.request.api('search', {
q: searchTerm,
maxResults,
key: this.token,
part: 'snippet',
type
});
const items = [];
results.items.forEach(item => {
switch (type) {
case 'video':
items.push(new entities_1.Video(this, item));
break;
case 'channel':
items.push(new entities_1.Channel(this, item));
break;
case 'playlist':
items.push(new entities_1.Playlist(this, item));
break;
default:
throw new Error('Type must be a video, channel, or playlist');
}
});
return items;
});
}
getItemById(type, id) {
return __awaiter(this, void 0, void 0, function* () {
let result;
if (type === 'video') {
result = yield util_1.request.api('videos', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
});
}
else if (type === 'channel') {
result = yield util_1.request.api('channels', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
});
}
else if (type === 'playlist') {
result = yield util_1.request.api('playlists', {
id,
part: 'snippet,contentDetails,player',
key: this.token
});
}
else if (type === 'comment') {
result = yield util_1.request.api('comments', {
id,
part: 'snippet',
key: this.token
});
}
if (result.items.length === 0) {
return Promise.reject('Item not found');
}
return result.items[0];
});
}
/**

@@ -103,5 +35,3 @@ * Search videos on YouTube.

searchVideos(searchTerm, maxResults = 10) {
return __awaiter(this, void 0, void 0, function* () {
return this.search('video', searchTerm, maxResults);
});
return this.search('video', searchTerm, maxResults);
}

@@ -114,5 +44,3 @@ /**

searchChannels(searchTerm, maxResults = 10) {
return __awaiter(this, void 0, void 0, function* () {
return this.search('channel', searchTerm, maxResults);
});
return this.search('channel', searchTerm, maxResults);
}

@@ -125,5 +53,3 @@ /**

searchPlaylists(searchTerm, maxResults = 10) {
return __awaiter(this, void 0, void 0, function* () {
return this.search('playlist', searchTerm, maxResults);
});
return this.search('playlist', searchTerm, maxResults);
}

@@ -135,5 +61,3 @@ /**

getVideo(id) {
return __awaiter(this, void 0, void 0, function* () {
return new entities_1.Video(this, yield this.getItemById('video', id));
});
return this.getItemById('video', id);
}

@@ -145,5 +69,3 @@ /**

getChannel(id) {
return __awaiter(this, void 0, void 0, function* () {
return new entities_1.Channel(this, yield this.getItemById('channel', id));
});
return this.getItemById('channel', id);
}

@@ -155,5 +77,3 @@ /**

getPlaylist(id) {
return __awaiter(this, void 0, void 0, function* () {
return new entities_1.Playlist(this, yield this.getItemById('playlist', id));
});
return this.getItemById('playlist', id);
}

@@ -165,5 +85,3 @@ /**

getComment(id) {
return __awaiter(this, void 0, void 0, function* () {
return new entities_1.YTComment(this, yield this.getItemById('comment', id));
});
return this.getItemById('comment', id);
}

@@ -175,9 +93,7 @@ /**

getVideoByUrl(url) {
return __awaiter(this, void 0, void 0, function* () {
const id = util_1.parseUrl(url);
if (!id.video) {
return Promise.reject('Not a valid video url');
}
return new entities_1.Video(this, yield this.getItemById('video', id.video));
});
const id = util_1.parseUrl(url);
if (!id.video) {
return Promise.reject('Not a valid video url');
}
return this.getItemById('video', id.video);
}

@@ -189,9 +105,7 @@ /**

getChannelByUrl(url) {
return __awaiter(this, void 0, void 0, function* () {
const id = util_1.parseUrl(url);
if (!id.channel) {
return Promise.reject('Not a valid channel url');
}
return new entities_1.Channel(this, yield this.getItemById('channel', id.channel));
});
const id = util_1.parseUrl(url);
if (!id.channel) {
return Promise.reject('Not a valid channel url');
}
return this.getItemById('channel', id.channel);
}

@@ -203,9 +117,7 @@ /**

getPlaylistByUrl(url) {
return __awaiter(this, void 0, void 0, function* () {
const id = util_1.parseUrl(url);
if (!id.playlist) {
return Promise.reject('Not a valid playlist url');
}
return new entities_1.Playlist(this, yield this.getItemById('playlist', id.playlist));
});
const id = util_1.parseUrl(url);
if (!id.playlist) {
return Promise.reject('Not a valid playlist url');
}
return this.getItemById('playlist', id.playlist);
}

@@ -218,57 +130,104 @@ /**

getPlaylistItems(playlistId, maxResults = -1) {
return this.getPaginatedItems('playlistItems', playlistId, maxResults);
}
/**
* Get `maxResults` comments on a video. Used mostly internally with `Video#fetchComments`.
* Can only get the last 100 comments on a video, due to a bug with the YouTube API.
* @param videoId The ID of the video.
* @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video.
*/
getVideoComments(videoId, maxResults = -1) {
return this.getPaginatedItems('commentThreads', videoId, maxResults);
}
/**
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`.
* @param commentId The ID of the comment to get replies from.
* @param maxResults The maximum amount of replies to get. Gets all replies if <= 0 or not included.
*/
getCommentReplies(commentId, maxResults = -1) {
return this.getPaginatedItems('comments', commentId, maxResults);
}
search(type, searchTerm, maxResults = 10) {
return __awaiter(this, void 0, void 0, function* () {
let full;
let videos = [];
if (maxResults <= 0) {
full = true;
if (maxResults < 1 || maxResults > 50) {
return Promise.reject('Max results must be greater than 0 and less than or equal to 50');
}
else {
full = false;
}
if (maxResults > 50) {
return Promise.reject('Max results must be 50 or below');
}
const results = yield util_1.request.api('playlistItems', {
playlistId,
const results = yield util_1.request.api('search', {
q: searchTerm,
maxResults,
key: this.token,
part: 'snippet',
key: this.token,
maxResults: full ? 50 : maxResults
}).catch(() => {
return Promise.reject('Playlist not found');
type
});
const totalResults = results.pageInfo.totalResults;
const perPage = results.pageInfo.resultsPerPage;
const pages = Math.floor(totalResults / perPage);
const items = [];
results.items.forEach(item => {
videos.push(new entities_1.Video(this, item));
switch (type) {
case 'video':
items.push(new entities_1.Video(this, item));
break;
case 'channel':
items.push(new entities_1.Channel(this, item));
break;
case 'playlist':
items.push(new entities_1.Playlist(this, item));
break;
default:
throw new Error('Type must be a video, channel, or playlist');
}
});
if (!full || pages === 0) {
return videos;
return items;
});
}
getItemById(type, id) {
return __awaiter(this, void 0, void 0, function* () {
let result;
if (type === 'video') {
result = yield util_1.request.api('videos', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
});
}
let oldRes = results;
for (let i = 1; i < pages; i++) {
const newResults = yield util_1.request.api('playlistItems', {
playlistId,
part: 'snippet',
key: this.token,
maxResults: 50,
pageToken: oldRes.nextPageToken
else if (type === 'channel') {
result = yield util_1.request.api('channels', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
});
oldRes = newResults;
newResults.items.forEach(item => {
videos.push(new entities_1.Video(this, item));
}
else if (type === 'playlist') {
result = yield util_1.request.api('playlists', {
id,
part: 'snippet,contentDetails,player',
key: this.token
});
}
return videos;
else if (type === 'comment') {
result = yield util_1.request.api('comments', {
id,
part: 'snippet',
key: this.token
});
}
if (result.items.length === 0) {
return Promise.reject('Item not found');
}
switch (type) {
case 'video':
return new entities_1.Video(this, result.items[0]);
case 'playlist':
return new entities_1.Playlist(this, result.items[0]);
case 'channel':
return new entities_1.Channel(this, result.items[0]);
case 'comment':
return new entities_1.YTComment(this, result.items[0]);
default:
throw new Error('Type must be a video, channel, or playlist');
}
});
}
/**
* Get `maxResults` comments on a video. Used mostly internally with `Video#fetchComments`.
* @param videoId The ID of the video.
* @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video.
*/
getVideoComments(videoId, maxResults = -1) {
getPaginatedItems(type, id, maxResults = -1) {
return __awaiter(this, void 0, void 0, function* () {
let full;
let comments = [];
let items = [];
if (maxResults <= 0) {

@@ -280,14 +239,39 @@ full = true;

}
if (maxResults > 100) {
return Promise.reject('Max results must be 100 or below');
let max;
if (type === 'playlistItems') {
max = 50;
}
const results = yield util_1.request.api('commentThreads', {
videoId,
part: 'snippet,replies',
else if (type === 'commentThreads' || type === 'comments') {
max = 100;
}
else {
return Promise.reject('Unknown item type ' + type);
}
if (maxResults > max) {
return Promise.reject(`Max results must be ${max} or below`);
}
const options = {
part: 'snippet',
key: this.token,
maxResults: full ? 100 : maxResults,
textFormat: 'plainText'
}).catch(() => {
return Promise.reject('Comment thread not found');
maxResults: full ? max : maxResults
};
switch (type) {
case 'playlistItems':
options.playlistId = id;
break;
case 'commentThreads':
options.videoId = id;
options.part += ',replies';
options.textFormat = 'plainText';
break;
case 'comments':
options.parentId = id;
break;
}
const results = yield util_1.request.api(type, options).catch(error => {
return Promise.reject('Items not found');
});
if (results.items.length === 0) {
return Promise.reject('Items not found');
}
const totalResults = results.pageInfo.totalResults;

@@ -297,4 +281,15 @@ const perPage = results.pageInfo.resultsPerPage;

results.items.forEach(item => {
const comment = new entities_1.YTComment(this, item.snippet.topLevelComment);
comments.push(comment);
let comment;
switch (type) {
case 'playlistItems':
items.push(new entities_1.Video(this, item));
break;
case 'commentThreads':
comment = new entities_1.YTComment(this, item.snippet.topLevelComment);
items.push(comment);
break;
case 'comments':
items.push(new entities_1.YTComment(this, item));
break;
}
if (item.replies) {

@@ -308,18 +303,23 @@ item.replies.comments.forEach(reply => {

if (!full || pages === 0) {
return comments;
return items;
}
let oldRes = results;
options.pageToken = oldRes.nextPageToken;
for (let i = 1; i < pages; i++) {
const newResults = yield util_1.request.api('commentThreads', {
videoId,
part: 'snippet',
key: this.token,
maxResults: 50,
pageToken: oldRes.nextPageToken,
textFormat: 'plainText'
});
const newResults = yield util_1.request.api(type, options);
oldRes = newResults;
newResults.items.forEach(item => {
const comment = new entities_1.YTComment(this, item.snippet.topLevelComment);
comments.push(comment);
let comment;
switch (type) {
case 'playlistItems':
items.push(new entities_1.Video(this, item));
break;
case 'commentThreads':
comment = new entities_1.YTComment(this, item.snippet.topLevelComment);
items.push(comment);
break;
case 'comments':
items.push(new entities_1.YTComment(this, item));
break;
}
if (item.replies) {

@@ -333,58 +333,6 @@ item.replies.comments.forEach(reply => {

}
return comments;
return items;
});
}
/**
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`.
* @param commentId The ID of the comment to get replies from.
* @param maxResults The maximum amount of replies to get. Gets all replies if <= 0 or not included.
*/
getCommentReplies(commentId, maxResults = -1) {
return __awaiter(this, void 0, void 0, function* () {
let full;
let replies = [];
if (maxResults <= 0) {
full = true;
}
else {
full = false;
}
if (maxResults > 100) {
return Promise.reject('Max results must be 50 or below');
}
const results = yield util_1.request.api('comments', {
parentId: commentId,
part: 'snippet',
key: this.token,
maxResults: full ? 100 : maxResults
}).catch(() => {
return Promise.reject('Playlist not found');
});
const totalResults = results.pageInfo.totalResults;
const perPage = results.pageInfo.resultsPerPage;
const pages = Math.floor(totalResults / perPage);
results.items.forEach(item => {
replies.push(new entities_1.YTComment(this, item));
});
if (!full || pages === 0) {
return replies;
}
let oldRes = results;
for (let i = 1; i < pages; i++) {
const newResults = yield util_1.request.api('comments', {
parentId: commentId,
part: 'snippet',
key: this.token,
maxResults: 100,
pageToken: oldRes.nextPageToken
});
oldRes = newResults;
newResults.items.forEach(item => {
replies.push(new entities_1.YTComment(this, item));
});
}
return replies;
});
}
}
exports.YouTube = YouTube;
{
"name": "better-youtube-api",
"version": "0.2.1",
"version": "0.3.0",
"description": "A very easy to use promise-based Youtube Data v3 API.",

@@ -5,0 +5,0 @@ "main": "out/index.js",

@@ -1,4 +0,2 @@

import { YouTube } from '..'
import { Playlist } from '.'
import { Thumbnail } from '../types'
import { YouTube, Playlist, Thumbnail } from '..'

@@ -68,3 +66,3 @@ /**

*/
public datePublished: Date
public dateCreated: Date

@@ -82,3 +80,3 @@ /**

/**
* The channel's uploads. Only available after calling `Channel#getVideos()`
* The channel's uploads. Only available after calling `Channel#fetchVideos()`
*/

@@ -127,3 +125,3 @@ public videos: Playlist

this.profilePictures = data.snippet.thumbnails
this.datePublished = new Date(data.snippet.publishedAt)
this.dateCreated = new Date(data.snippet.publishedAt)
this.name = data.snippet.title

@@ -130,0 +128,0 @@ this.about = data.snippet.description

@@ -69,3 +69,3 @@ import { YouTube } from '..'

/**
* The number of likes the comment has gotten.
* The number of likes the comment has received.
*/

@@ -86,3 +86,3 @@ public likes: number

/**
* Either the ID of the video that it commented on, or the ID of the
* Either the ID of the video that it is commenting on, or the ID of the
* comment it is replying to.

@@ -93,4 +93,4 @@ */

/**
* Replies to the comment. If the comment was fetched from a video, then
* this will be partially filled. You'll need to use Comment#fetchReplies
* Replies directed to the comment. If the comment was fetched from a video,
* then this will be partially filled. You'll need to use Comment#fetchReplies
* to get all of the replies, though.

@@ -97,0 +97,0 @@ */

@@ -1,4 +0,2 @@

import { YouTube } from '..'
import { Video } from '.'
import { Thumbnail } from '../types'
import { YouTube, Video, Thumbnail } from '..'

@@ -40,3 +38,3 @@ /**

/**
* The videos in the playlist. Only available after calling `Playlist#getVideos()`.
* The videos in the playlist. Only available after calling `Playlist#fetchVideos()`.
*/

@@ -53,3 +51,3 @@ public videos: Video[]

*/
public datePublished: Date
public dateCreated: Date

@@ -70,3 +68,3 @@ /**

*/
public itemCount: number
public length: number

@@ -96,3 +94,3 @@ /**

this.tags = playlist.snippet.tags
this.itemCount = playlist.contentDetails.itemCount
this.length = playlist.contentDetails.itemCount
this.embedHtml = playlist.player.embedHtml

@@ -107,3 +105,3 @@ } else if (data.kind === 'youtube#searchResult') {

this.creatorId = data.snippet.channelId
this.datePublished = new Date(data.snippet.publishedAt)
this.dateCreated = new Date(data.snippet.publishedAt)
this.thumbnails = data.snippet.thumbnails

@@ -122,10 +120,2 @@ this.full = data.kind === 'youtube#playlist'

/**
* Deprecated, use Playlist#fetchVideos instead.
* @param maxResults Maximum number of videos to fetch.
*/
public getVideos (maxResults: number = -1) {
return this.fetchVideos(maxResults)
}
/**
* Fetches this playlist and reassigns this object to the new playlist object.

@@ -132,0 +122,0 @@ * Only useful if `this.full` is false, or if you want updated playlist info.

@@ -159,2 +159,6 @@ import { YouTube } from '..'

/**
* Fetches the video's comments and assigns them to Video#comments.
* @param maxResults The maximum amount of comments to fetch
*/
public async fetchComments (maxResults: number = -1) {

@@ -161,0 +165,0 @@ this.comments = await this.youtube.getVideoComments(this.id, maxResults)

@@ -20,72 +20,2 @@ import { Video, Channel, Playlist, YTComment } from './entities'

private async search (type: 'video' | 'channel' | 'playlist', searchTerm: string, maxResults: number = 10) {
if (maxResults < 1 || maxResults > 50) {
return Promise.reject('Max results must be greater than 0 and less than or equal to 50')
}
const results = await request.api('search', {
q: searchTerm,
maxResults,
key: this.token,
part: 'snippet',
type
})
const items = []
results.items.forEach(item => {
switch (type) {
case 'video':
items.push(new Video(this, item))
break
case 'channel':
items.push(new Channel(this, item))
break
case 'playlist':
items.push(new Playlist(this, item))
break
default:
throw new Error('Type must be a video, channel, or playlist')
}
})
return items as Video[] | Channel[] | Playlist[]
}
private async getItemById (type: 'video' | 'channel' | 'playlist' | 'comment', id: string) {
let result
if (type === 'video') {
result = await request.api('videos', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
})
} else if (type === 'channel') {
result = await request.api('channels', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
})
} else if (type === 'playlist') {
result = await request.api('playlists', {
id,
part: 'snippet,contentDetails,player',
key: this.token
})
} else if (type === 'comment') {
result = await request.api('comments', {
id,
part: 'snippet',
key: this.token
})
}
if (result.items.length === 0) {
return Promise.reject('Item not found')
}
return result.items[0]
}
/**

@@ -96,3 +26,3 @@ * Search videos on YouTube.

*/
public async searchVideos (searchTerm: string, maxResults: number = 10) {
public searchVideos (searchTerm: string, maxResults: number = 10) {
return this.search('video', searchTerm, maxResults) as Promise<Video[]>

@@ -106,3 +36,3 @@ }

*/
public async searchChannels (searchTerm: string, maxResults: number = 10) {
public searchChannels (searchTerm: string, maxResults: number = 10) {
return this.search('channel', searchTerm, maxResults) as Promise<Channel[]>

@@ -116,3 +46,3 @@ }

*/
public async searchPlaylists (searchTerm: string, maxResults: number = 10) {
public searchPlaylists (searchTerm: string, maxResults: number = 10) {
return this.search('playlist', searchTerm, maxResults) as Promise<Playlist[]>

@@ -125,4 +55,4 @@ }

*/
public async getVideo (id: string) {
return new Video(this, await this.getItemById('video', id))
public getVideo (id: string) {
return this.getItemById('video', id) as Promise<Video>
}

@@ -134,4 +64,4 @@

*/
public async getChannel (id: string) {
return new Channel(this, await this.getItemById('channel', id))
public getChannel (id: string) {
return this.getItemById('channel', id) as Promise<Channel>
}

@@ -143,4 +73,4 @@

*/
public async getPlaylist (id: string) {
return new Playlist(this, await this.getItemById('playlist', id))
public getPlaylist (id: string) {
return this.getItemById('playlist', id) as Promise<Playlist>
}

@@ -152,4 +82,4 @@

*/
public async getComment (id: string) {
return new YTComment(this, await this.getItemById('comment', id))
public getComment (id: string) {
return this.getItemById('comment', id) as Promise<YTComment>
}

@@ -161,3 +91,3 @@

*/
public async getVideoByUrl (url: string) {
public getVideoByUrl (url: string) {
const id = parseUrl(url)

@@ -169,3 +99,3 @@

return new Video(this, await this.getItemById('video', id.video))
return this.getItemById('video', id.video) as Promise<Video>
}

@@ -177,3 +107,3 @@

*/
public async getChannelByUrl (url: string) {
public getChannelByUrl (url: string) {
const id = parseUrl(url)

@@ -185,3 +115,3 @@

return new Channel(this, await this.getItemById('channel', id.channel))
return this.getItemById('channel', id.channel) as Promise<Channel>
}

@@ -193,3 +123,3 @@

*/
public async getPlaylistByUrl (url: string) {
public getPlaylistByUrl (url: string) {
const id = parseUrl(url)

@@ -201,3 +131,3 @@

return new Playlist(this, await this.getItemById('playlist', id.playlist))
return this.getItemById('playlist', id.playlist) as Promise<Playlist>
}

@@ -210,65 +140,109 @@

*/
public async getPlaylistItems (playlistId: string, maxResults: number = -1) {
let full
let videos: Video[] = []
public getPlaylistItems (playlistId: string, maxResults: number = -1) {
return this.getPaginatedItems('playlistItems', playlistId, maxResults) as Promise<Video[]>
}
if (maxResults <= 0) {
full = true
} else {
full = false
}
/**
* Get `maxResults` comments on a video. Used mostly internally with `Video#fetchComments`.
* Can only get the last 100 comments on a video, due to a bug with the YouTube API.
* @param videoId The ID of the video.
* @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video.
*/
public getVideoComments (videoId: string, maxResults: number = -1) {
return this.getPaginatedItems('commentThreads', videoId, maxResults) as Promise<YTComment[]>
}
if (maxResults > 50) {
return Promise.reject('Max results must be 50 or below')
/**
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`.
* @param commentId The ID of the comment to get replies from.
* @param maxResults The maximum amount of replies to get. Gets all replies if <= 0 or not included.
*/
public getCommentReplies (commentId: string, maxResults: number = -1) {
return this.getPaginatedItems('comments', commentId, maxResults) as Promise<YTComment[]>
}
private async search (type: 'video' | 'channel' | 'playlist', searchTerm: string, maxResults: number = 10): Promise<Video[] | Channel[] | Playlist[]> {
if (maxResults < 1 || maxResults > 50) {
return Promise.reject('Max results must be greater than 0 and less than or equal to 50')
}
const results = await request.api('playlistItems', {
playlistId,
const results = await request.api('search', {
q: searchTerm,
maxResults,
key: this.token,
part: 'snippet',
key: this.token,
maxResults: full ? 50 : maxResults
}).catch(() => {
return Promise.reject('Playlist not found')
type
})
const totalResults = results.pageInfo.totalResults
const perPage = results.pageInfo.resultsPerPage
const pages = Math.floor(totalResults / perPage)
const items = []
results.items.forEach(item => {
videos.push(new Video(this, item))
switch (type) {
case 'video':
items.push(new Video(this, item))
break
case 'channel':
items.push(new Channel(this, item))
break
case 'playlist':
items.push(new Playlist(this, item))
break
default:
throw new Error('Type must be a video, channel, or playlist')
}
})
if (!full || pages === 0) {
return videos
}
return items
}
let oldRes = results
private async getItemById (type: 'video' | 'channel' | 'playlist' | 'comment', id: string): Promise<Video | Channel | Playlist | YTComment> {
let result
for (let i = 1; i < pages; i++) {
const newResults = await request.api('playlistItems', {
playlistId,
if (type === 'video') {
result = await request.api('videos', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
})
} else if (type === 'channel') {
result = await request.api('channels', {
id,
part: 'snippet,contentDetails,statistics,status',
key: this.token
})
} else if (type === 'playlist') {
result = await request.api('playlists', {
id,
part: 'snippet,contentDetails,player',
key: this.token
})
} else if (type === 'comment') {
result = await request.api('comments', {
id,
part: 'snippet',
key: this.token,
maxResults: 50,
pageToken: oldRes.nextPageToken
key: this.token
})
}
oldRes = newResults
newResults.items.forEach(item => {
videos.push(new Video(this, item))
})
if (result.items.length === 0) {
return Promise.reject('Item not found')
}
return videos
switch (type) {
case 'video':
return new Video(this, result.items[0])
case 'playlist':
return new Playlist(this, result.items[0])
case 'channel':
return new Channel(this, result.items[0])
case 'comment':
return new YTComment(this, result.items[0])
default:
throw new Error('Type must be a video, channel, or playlist')
}
}
/**
* Get `maxResults` comments on a video. Used mostly internally with `Video#fetchComments`.
* @param videoId The ID of the video.
* @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video.
*/
public async getVideoComments (videoId: string, maxResults: number = -1) {
let full
let comments: YTComment[] = []
private async getPaginatedItems (type: 'playlistItems' | 'commentThreads' | 'comments', id: string, maxResults: number = -1): Promise<Video[] | YTComment[]> {
let full: boolean
let items = []

@@ -281,16 +255,53 @@ if (maxResults <= 0) {

if (maxResults > 100) {
return Promise.reject('Max results must be 100 or below')
let max: number
if (type === 'playlistItems') {
max = 50
} else if (type === 'commentThreads' || type === 'comments') {
max = 100
} else {
return Promise.reject('Unknown item type ' + type)
}
const results = await request.api('commentThreads', {
videoId,
part: 'snippet,replies',
if (maxResults > max) {
return Promise.reject(`Max results must be ${max} or below`)
}
const options: {
part: string,
key: string,
maxResults: number,
videoId?: string,
parentId?: string,
textFormat?: string,
playlistId?: string,
pageToken?: string
} = {
part: 'snippet',
key: this.token,
maxResults: full ? 100 : maxResults,
textFormat: 'plainText'
}).catch(() => {
return Promise.reject('Comment thread not found')
maxResults: full ? max : maxResults
}
switch (type) {
case 'playlistItems':
options.playlistId = id
break
case 'commentThreads':
options.videoId = id
options.part += ',replies'
options.textFormat = 'plainText'
break
case 'comments':
options.parentId = id
break
}
const results = await request.api(type, options).catch(error => {
return Promise.reject('Items not found')
})
if (results.items.length === 0) {
return Promise.reject('Items not found')
}
const totalResults = results.pageInfo.totalResults

@@ -301,5 +312,17 @@ const perPage = results.pageInfo.resultsPerPage

results.items.forEach(item => {
const comment = new YTComment(this, item.snippet.topLevelComment)
comments.push(comment)
let comment: YTComment
switch (type) {
case 'playlistItems':
items.push(new Video(this, item))
break
case 'commentThreads':
comment = new YTComment(this, item.snippet.topLevelComment)
items.push(comment)
break
case 'comments':
items.push(new YTComment(this, item))
break
}
if (item.replies) {

@@ -314,22 +337,28 @@ item.replies.comments.forEach(reply => {

if (!full || pages === 0) {
return comments
return items
}
let oldRes = results
options.pageToken = oldRes.nextPageToken
for (let i = 1; i < pages; i++) {
const newResults = await request.api('commentThreads', {
videoId,
part: 'snippet',
key: this.token,
maxResults: 50,
pageToken: oldRes.nextPageToken,
textFormat: 'plainText'
})
const newResults = await request.api(type, options)
oldRes = newResults
newResults.items.forEach(item => {
const comment = new YTComment(this, item.snippet.topLevelComment)
comments.push(comment)
let comment: YTComment
switch (type) {
case 'playlistItems':
items.push(new Video(this, item))
break
case 'commentThreads':
comment = new YTComment(this, item.snippet.topLevelComment)
items.push(comment)
break
case 'comments':
items.push(new YTComment(this, item))
break
}
if (item.replies) {

@@ -344,64 +373,4 @@ item.replies.comments.forEach(reply => {

return comments
return items
}
/**
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`.
* @param commentId The ID of the comment to get replies from.
* @param maxResults The maximum amount of replies to get. Gets all replies if <= 0 or not included.
*/
public async getCommentReplies (commentId: string, maxResults: number = -1) {
let full
let replies: YTComment[] = []
if (maxResults <= 0) {
full = true
} else {
full = false
}
if (maxResults > 100) {
return Promise.reject('Max results must be 50 or below')
}
const results = await request.api('comments', {
parentId: commentId,
part: 'snippet',
key: this.token,
maxResults: full ? 100 : maxResults
}).catch(() => {
return Promise.reject('Playlist not found')
})
const totalResults = results.pageInfo.totalResults
const perPage = results.pageInfo.resultsPerPage
const pages = Math.floor(totalResults / perPage)
results.items.forEach(item => {
replies.push(new YTComment(this, item))
})
if (!full || pages === 0) {
return replies
}
let oldRes = results
for (let i = 1; i < pages; i++) {
const newResults = await request.api('comments', {
parentId: commentId,
part: 'snippet',
key: this.token,
maxResults: 100,
pageToken: oldRes.nextPageToken
})
oldRes = newResults
newResults.items.forEach(item => {
replies.push(new YTComment(this, item))
})
}
return replies
}
}

@@ -52,26 +52,6 @@ import 'mocha'

it('should reject if the item isn\'t found', async () => {
const youtube = new YouTube(apiKey)
expect(await youtube.getChannel('dQw4w9WgXcQ').catch(error => { return error })).to.equal('Item not found')
})
it('should work with comments', async () => {
const youtube = new YouTube(apiKey)
expect(await youtube.getComment('UgyNb5InfceN2n5WhG94AaABAg')).to.be.instanceOf(YTComment)
})
it('should work with replies', async () => {
const youtube = new YouTube(apiKey)
expect((await (await youtube.getComment('UgyNb5InfceN2n5WhG94AaABAg')).fetchReplies()).length).to.be.greaterThan(0)
})
it('should work with playlists', async () => {
const youtube = new YouTube(apiKey)
expect(await youtube.getPlaylist('PLMC9KNkIncKvYin_USF1qoJQnIyMAfRxl')).to.be.instanceOf(Playlist)
})
describe('Playlist items', () => {
it('should reject if the playlist isn\'t found', async () => {
const youtube = new YouTube(apiKey)
expect(await youtube.getPlaylistItems('').catch(error => { return error })).to.equal('Playlist not found')
expect(await youtube.getPlaylistItems('').catch(error => { return error })).to.equal('Items not found')
})

@@ -106,3 +86,3 @@

const youtube = new YouTube(apiKey)
expect(await youtube.getVideoComments('24EWkH5ipdw').catch(error => { return error })).to.equal('Comment thread not found')
expect(await youtube.getVideoComments('24EWkH5ipdw').catch(error => { return error })).to.equal('Items not found')
})

@@ -112,3 +92,3 @@

const youtube = new YouTube(apiKey)
expect(await youtube.getVideoComments('0').catch(error => { return error })).to.equal('Comment thread not found')
expect(await youtube.getVideoComments('0').catch(error => { return error })).to.equal('Items not found')
})

@@ -121,2 +101,19 @@

})
describe('Comment replies', () => {
it('should work with valid comments with replies', async () => {
const youtube = new YouTube(apiKey)
expect((await youtube.getCommentReplies('Uggw2qPdnUEfcHgCoAEC'))[0]).to.be.instanceOf(YTComment)
})
it('should not work with invalid comments/comments with no replies', async () => {
const youtube = new YouTube(apiKey)
expect(await youtube.getCommentReplies('0').catch(error => { return error })).to.equal('Items not found')
})
it('should return an array with a length of <= maxResults', async () => {
const youtube = new YouTube(apiKey)
expect((await youtube.getCommentReplies('Uggw2qPdnUEfcHgCoAEC', 1)).length).to.be.lessThan(2)
})
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc