better-youtube-api
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -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[] |
400
out/index.js
@@ -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) |
395
src/index.ts
@@ -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) | ||
}) | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
76182
2199