better-youtube-api
Advanced tools
Comparing version 0.1.2 to 0.2.0
@@ -9,1 +9,2 @@ "use strict"; | ||
__export(require("./Playlist")); | ||
__export(require("./Comment")); |
@@ -43,5 +43,5 @@ "use strict"; | ||
*/ | ||
getVideos() { | ||
fetchVideos(maxResults = -1) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this.videos = yield this.youtube.getPlaylistItems(this.id); | ||
this.videos = yield this.youtube.getPlaylistItems(this.id, maxResults); | ||
return this.videos; | ||
@@ -51,2 +51,9 @@ }); | ||
/** | ||
* 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. | ||
@@ -53,0 +60,0 @@ * Only useful if `this.full` is false, or if you want updated playlist info. |
@@ -33,3 +33,3 @@ "use strict"; | ||
this.id = data.snippet.resourceId.videoId; | ||
this.private = data.snippet.title === 'Private video' ? true : false; | ||
this.private = data.snippet.title === 'Private video'; | ||
} | ||
@@ -62,3 +62,9 @@ else if (data.kind === 'youtube#searchResult') { | ||
} | ||
fetchComments(maxResults = -1) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this.comments = yield this.youtube.getVideoComments(this.id, maxResults); | ||
return this.comments; | ||
}); | ||
} | ||
} | ||
exports.Video = Video; |
@@ -1,3 +0,1 @@ | ||
import { youtube_v3 } from "googleapis" | ||
/** | ||
@@ -77,2 +75,16 @@ * The main class used to interact with the YouTube API. Use this. | ||
public getPlaylistItems(playlistId: string, maxResults?: number): Promise<Video[]> | ||
/** | ||
* 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 getVideoComments (videoId: string, maxResults: number): Promise<YTComment[]> | ||
/** | ||
* 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): Promise<YTComment[]> | ||
} | ||
@@ -85,5 +97,5 @@ | ||
/** | ||
* YouTube object that created the video. | ||
*/ | ||
public youtube | ||
* YouTube object that created the video. | ||
*/ | ||
public youtube: YouTube | ||
@@ -93,3 +105,3 @@ /** | ||
*/ | ||
public data: youtube_v3.Schema$Video | youtube_v3.Schema$PlaylistItem | youtube_v3.Schema$SearchResult | ||
public data | ||
@@ -119,3 +131,9 @@ /** | ||
*/ | ||
public thumbnails: youtube_v3.Schema$ThumbnailDetails | ||
public thumbnails: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -132,2 +150,4 @@ /** | ||
private _length: string | ||
/** | ||
@@ -176,4 +196,9 @@ * The minutes of the video. | ||
constructor(youtube, data: youtube_v3.Schema$Video | youtube_v3.Schema$PlaylistItem | youtube_v3.Schema$SearchResult) | ||
/** | ||
* The video's comments. Only defined when Video#fetchComments is called. | ||
*/ | ||
public comments: YTComment[] | ||
constructor(youtube: YouTube, data) | ||
/** | ||
@@ -203,3 +228,3 @@ * Fetches this video and reassigns this object to the new video object. | ||
*/ | ||
public data: youtube_v3.Schema$Channel | youtube_v3.Schema$SearchResult | ||
public data: any | ||
@@ -239,3 +264,9 @@ /** | ||
*/ | ||
public profilePictures: youtube_v3.Schema$ThumbnailDetails | ||
public profilePictures: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -253,12 +284,2 @@ /** | ||
/** | ||
* This channel's statistics. | ||
*/ | ||
public statistics: youtube_v3.Schema$ChannelStatistics | ||
/** | ||
* The status of this channel. | ||
*/ | ||
public status: youtube_v3.Schema$ChannelStatus | ||
/** | ||
* This channel's view count. | ||
@@ -283,3 +304,3 @@ */ | ||
constructor (youtube: YouTube, data: youtube_v3.Schema$Channel | youtube_v3.Schema$SearchResult) | ||
constructor (youtube: YouTube, data) | ||
@@ -310,3 +331,3 @@ /** | ||
*/ | ||
public data: youtube_v3.Schema$Playlist | youtube_v3.Schema$SearchResult | ||
public data: any | ||
@@ -351,3 +372,9 @@ /** | ||
*/ | ||
public thumbnails: youtube_v3.Schema$ThumbnailDetails | ||
public thumbnails: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -369,3 +396,3 @@ /** | ||
constructor (youtube: YouTube, data: youtube_v3.Schema$Playlist | youtube_v3.Schema$SearchResult) | ||
constructor (youtube: YouTube, data) | ||
@@ -383,1 +410,110 @@ /** | ||
} | ||
export class YTComment { | ||
/** | ||
* The YouTube object used to create the comment. | ||
*/ | ||
public youtube: YouTube | ||
/** | ||
* The raw data from the YouTube API of the comment. | ||
*/ | ||
public data: any | ||
/** | ||
* The comment's unique YouTube ID. | ||
*/ | ||
public id: string | ||
/** | ||
* The comment's author. | ||
*/ | ||
public author: { | ||
/** | ||
* The author's YouTube username. May not be unique. | ||
*/ | ||
username: string, | ||
/** | ||
* The author's avatar URL. | ||
*/ | ||
avatar: string, | ||
/** | ||
* The author's channel ID. | ||
*/ | ||
channelId: string, | ||
/** | ||
* The author's channel URL. | ||
*/ | ||
channelUrl: string | ||
} | ||
/** | ||
* The comment's content. | ||
*/ | ||
public text: { | ||
/** | ||
* What YouTube displays to the user viewing the comment. | ||
*/ | ||
displayed: string, | ||
/** | ||
* The comment's plain text. | ||
*/ | ||
original: string | ||
} | ||
/** | ||
* Whether or not you can like/dislike the comment. | ||
*/ | ||
public rateable: boolean | ||
/** | ||
* Either YouTube thinks it's popular, or it has at least 100 likes. | ||
*/ | ||
public popular: boolean | ||
/** | ||
* The number of likes the comment has gotten. | ||
*/ | ||
public likes: number | ||
/** | ||
* The date the comment was published. | ||
*/ | ||
public datePublished: Date | ||
/** | ||
* Either the date the comment was last edited, or the date it was | ||
* posted. | ||
*/ | ||
public dateEdited: Date | ||
/** | ||
* Either the ID of the video that it commented on, or the ID of the | ||
* comment it is replying to. | ||
*/ | ||
public parentId: string | ||
/** | ||
* Replies to the comment. | ||
*/ | ||
public replies: YTComment[] | ||
constructor (youtube: YouTube, data) | ||
/** | ||
* Fetches replies to the comment. | ||
* @param maxResults The maximum amount of replies to fetch. Fetches all comments if not included | ||
* or less than 0. | ||
*/ | ||
public fetchReplies (maxResults?: number): Promise<YTComment[]> | ||
} | ||
export type Thumbnail = { | ||
height?: number, | ||
width?: number, | ||
url?: string | ||
} |
140
out/index.js
@@ -83,2 +83,9 @@ "use strict"; | ||
} | ||
else if (type === 'comment') { | ||
result = yield util_1.request.api('comments', { | ||
id, | ||
part: 'snippet', | ||
key: this.token | ||
}); | ||
} | ||
if (result.items.length === 0) { | ||
@@ -148,2 +155,11 @@ return Promise.reject('Item not found'); | ||
/** | ||
* Get a comment object from the ID of a comment. | ||
* @param id The ID of the comment. | ||
*/ | ||
getComment(id) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return new entities_1.YTComment(this, yield this.getItemById('comment', id)); | ||
}); | ||
} | ||
/** | ||
* Get a video object from the url of a video. | ||
@@ -188,3 +204,3 @@ * @param url The url of the video. | ||
/** | ||
* Get `maxResults` videos in a playlist. Used mostly internally with `Playlist#getVideos`. | ||
* Get `maxResults` videos in a playlist. Used mostly internally with `Playlist#fetchVideos`. | ||
* @param playlistId The ID of the playlist. | ||
@@ -215,3 +231,3 @@ * @param maxResults The maximum amount of videos to get from the playlist. If <= 0 or not included, returns all videos in the playlist. | ||
const totalResults = results.pageInfo.totalResults; | ||
const perPage = 50; | ||
const perPage = results.pageInfo.resultsPerPage; | ||
const pages = Math.floor(totalResults / perPage); | ||
@@ -241,3 +257,123 @@ results.items.forEach(item => { | ||
} | ||
/** | ||
* 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) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let full; | ||
let comments = []; | ||
if (maxResults <= 0) { | ||
full = true; | ||
} | ||
else { | ||
full = false; | ||
} | ||
if (maxResults > 100) { | ||
return Promise.reject('Max results must be 100 or below'); | ||
} | ||
const results = yield util_1.request.api('commentThreads', { | ||
videoId, | ||
part: 'snippet,replies', | ||
key: this.token, | ||
maxResults: full ? 100 : maxResults, | ||
textFormat: 'plainText' | ||
}).catch(() => { | ||
return Promise.reject('Comment thread not found'); | ||
}); | ||
const totalResults = results.pageInfo.totalResults; | ||
const perPage = results.pageInfo.resultsPerPage; | ||
const pages = Math.floor(totalResults / perPage); | ||
results.items.forEach(item => { | ||
const comment = new entities_1.YTComment(this, item.snippet.topLevelComment); | ||
comments.push(comment); | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new entities_1.YTComment(this, reply); | ||
comment.replies.push(created); | ||
}); | ||
} | ||
}); | ||
if (!full || pages === 0) { | ||
return comments; | ||
} | ||
let oldRes = results; | ||
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' | ||
}); | ||
oldRes = newResults; | ||
newResults.items.forEach(item => { | ||
const comment = new entities_1.YTComment(this, item.snippet.topLevelComment); | ||
comments.push(comment); | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new entities_1.YTComment(this, reply); | ||
comment.replies.push(created); | ||
}); | ||
} | ||
}); | ||
} | ||
return comments; | ||
}); | ||
} | ||
/** | ||
* 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; |
@@ -7,6 +7,3 @@ "use strict"; | ||
api: (subUrl, params) => { | ||
let url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? subUrl : '/' + subUrl); | ||
for (let param in params) { | ||
url += (!url.includes('?') ? '?' : '&') + param + '=' + params[param]; | ||
} | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? subUrl : '/' + subUrl) + parseParams(params); | ||
return get(url); | ||
@@ -78,1 +75,8 @@ } | ||
} | ||
function parseParams(params) { | ||
let url = ''; | ||
for (let param in params) { | ||
url += (!url.includes('?') ? '?' : '&') + param + '=' + params[param]; | ||
} | ||
return url; | ||
} |
{ | ||
"name": "better-youtube-api", | ||
"version": "0.1.2", | ||
"description": "A very easy to use Youtube Data v3 API.", | ||
"version": "0.2.0", | ||
"description": "A very easy to use promise-based Youtube Data v3 API.", | ||
"main": "out/index.js", | ||
@@ -16,3 +16,6 @@ "scripts": { | ||
"youtube api", | ||
"youtube videos" | ||
"youtube videos", | ||
"promise", | ||
"promise based", | ||
"no dependencies" | ||
], | ||
@@ -23,7 +26,7 @@ "devDependencies": { | ||
"@types/node": "^10.5.6", | ||
"tslint": "^5.11.0", | ||
"tslint-config-standard": "^7.1.0", | ||
"chai": "^4.1.2", | ||
"mocha": "^5.2.0", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"tslint-config-standard": "^7.1.0", | ||
"typescript": "^3.0.1" | ||
@@ -30,0 +33,0 @@ }, |
# Better YouTube API | ||
Want to access data from the YouTube Data v3 API? Want a Node.js YouTube API wrapper with typings? No problem! We've got ya covered. `npm i better-youtube-api` | ||
Want to access data from the YouTube Data v3 API? Want a Node.js YouTube API wrapper with typings and promises? No problem! We've got ya covered. `npm i better-youtube-api` | ||
@@ -28,2 +28,2 @@ # Examples | ||
You can do the same thing with playlists and channels by replacing `Video` with either one. | ||
You can do the same thing with playlists, channels, and comments by replacing `Video` with either one. |
import { YouTube } from '..' | ||
import { youtube_v3 } from 'googleapis' | ||
import { Playlist } from '.' | ||
import { Thumbnail } from '../types' | ||
@@ -22,3 +22,3 @@ /** | ||
*/ | ||
public data: youtube_v3.Schema$Channel | youtube_v3.Schema$SearchResult | ||
public data: any | ||
@@ -58,3 +58,9 @@ /** | ||
*/ | ||
public profilePictures: youtube_v3.Schema$ThumbnailDetails | ||
public profilePictures: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -91,3 +97,3 @@ /** | ||
constructor (youtube: YouTube, data: youtube_v3.Schema$Channel | youtube_v3.Schema$SearchResult) { | ||
constructor (youtube: YouTube, data) { | ||
this.youtube = youtube | ||
@@ -99,6 +105,6 @@ this.data = data | ||
private _init (data: youtube_v3.Schema$Channel | youtube_v3.Schema$SearchResult) { | ||
private _init (data) { | ||
if (data.kind === 'youtube#channel' && (data as youtube_v3.Schema$Channel).status.isLinked) { | ||
const channel = data as youtube_v3.Schema$Channel | ||
if (data.kind === 'youtube#channel' && data.status.isLinked) { | ||
const channel = data | ||
@@ -116,3 +122,3 @@ this.id = channel.id | ||
} else if (data.kind === 'youtube#searchResult') { | ||
this.id = (data as youtube_v3.Schema$SearchResult).id.channelId | ||
this.id = data.id.channelId | ||
} else { | ||
@@ -143,7 +149,7 @@ throw new Error(`Invalid channel type: ${data.kind}`) | ||
public async fetchVideos () { | ||
if (!((this.data as youtube_v3.Schema$Channel).contentDetails)) { | ||
if (!(this.data.contentDetails)) { | ||
await this.fetch() | ||
} | ||
const videos = await this.youtube.getPlaylist((this.data as youtube_v3.Schema$Channel).contentDetails.relatedPlaylists.uploads) | ||
const videos = await this.youtube.getPlaylist(this.data.contentDetails.relatedPlaylists.uploads) | ||
this.videos = videos | ||
@@ -150,0 +156,0 @@ |
export * from './Video' | ||
export * from './Channel' | ||
export * from './Playlist' | ||
export * from './Comment' |
import { YouTube } from '..' | ||
import { youtube_v3 } from 'googleapis' | ||
import { Video } from '.' | ||
import { Thumbnail } from '../types' | ||
@@ -17,3 +17,3 @@ /** | ||
*/ | ||
public data: youtube_v3.Schema$Playlist | youtube_v3.Schema$SearchResult | ||
public data: any | ||
@@ -58,3 +58,9 @@ /** | ||
*/ | ||
public thumbnails: youtube_v3.Schema$ThumbnailDetails | ||
public thumbnails: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -76,3 +82,3 @@ /** | ||
constructor (youtube: YouTube, data: youtube_v3.Schema$Playlist | youtube_v3.Schema$SearchResult) { | ||
constructor (youtube: YouTube, data) { | ||
this.youtube = youtube | ||
@@ -84,5 +90,5 @@ this.data = data | ||
private _init (data: youtube_v3.Schema$Playlist | youtube_v3.Schema$SearchResult) { | ||
private _init (data) { | ||
if (data.kind === 'youtube#playlist') { | ||
const playlist = data as youtube_v3.Schema$Playlist | ||
const playlist = data | ||
@@ -94,3 +100,3 @@ this.id = playlist.id | ||
} else if (data.kind === 'youtube#searchResult') { | ||
this.id = (data as youtube_v3.Schema$SearchResult).id.playlistId | ||
this.id = data.id.playlistId | ||
} else { | ||
@@ -110,4 +116,4 @@ throw new Error(`Invalid playlist type: ${data.kind}`) | ||
*/ | ||
public async getVideos () { | ||
this.videos = await this.youtube.getPlaylistItems(this.id) | ||
public async fetchVideos (maxResults: number = -1) { | ||
this.videos = await this.youtube.getPlaylistItems(this.id, maxResults) | ||
return this.videos | ||
@@ -117,2 +123,10 @@ } | ||
/** | ||
* 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. | ||
@@ -119,0 +133,0 @@ * Only useful if `this.full` is false, or if you want updated playlist info. |
import { YouTube } from '..' | ||
import { youtube_v3 } from 'googleapis' | ||
import { Thumbnail } from '../types' | ||
import { YTComment } from './Comment' | ||
@@ -16,3 +17,3 @@ /** | ||
*/ | ||
public data: youtube_v3.Schema$Video | youtube_v3.Schema$PlaylistItem | youtube_v3.Schema$SearchResult | ||
public data | ||
@@ -42,3 +43,9 @@ /** | ||
*/ | ||
public thumbnails: youtube_v3.Schema$ThumbnailDetails | ||
public thumbnails: { | ||
default?: Thumbnail, | ||
high?: Thumbnail, | ||
maxres?: Thumbnail | ||
medium?: Thumbnail, | ||
standard?: Thumbnail | ||
} | ||
@@ -100,3 +107,8 @@ /** | ||
constructor (youtube: YouTube, data: youtube_v3.Schema$Video | youtube_v3.Schema$PlaylistItem | youtube_v3.Schema$SearchResult) { | ||
/** | ||
* The video's comments. Only defined when Video#fetchComments is called. | ||
*/ | ||
public comments: YTComment[] | ||
constructor (youtube: YouTube, data) { | ||
this.youtube = youtube | ||
@@ -108,5 +120,5 @@ this.data = data | ||
private _init (data: youtube_v3.Schema$Video | youtube_v3.Schema$PlaylistItem | youtube_v3.Schema$SearchResult) { | ||
private _init (data) { | ||
if (data.kind === 'youtube#video') { | ||
const video = data as youtube_v3.Schema$Video | ||
const video = data | ||
@@ -122,6 +134,6 @@ this._length = video.contentDetails.duration | ||
} else if (data.kind === 'youtube#playlistItem') { | ||
this.id = (data.snippet as youtube_v3.Schema$PlaylistItemSnippet).resourceId.videoId | ||
this.private = data.snippet.title === 'Private video' ? true : false | ||
this.id = data.snippet.resourceId.videoId | ||
this.private = data.snippet.title === 'Private video' | ||
} else if (data.kind === 'youtube#searchResult') { | ||
this.id = (data.id as youtube_v3.Schema$ResourceId).videoId | ||
this.id = data.id.videoId | ||
} else { | ||
@@ -151,2 +163,7 @@ throw new Error(`Invalid video type: ${data.kind}.`) | ||
} | ||
public async fetchComments (maxResults: number = -1) { | ||
this.comments = await this.youtube.getVideoComments(this.id, maxResults) | ||
return this.comments | ||
} | ||
} |
161
src/index.ts
@@ -1,4 +0,5 @@ | ||
import { Video, Channel, Playlist } from './entities' | ||
import { Video, Channel, Playlist, YTComment } from './entities' | ||
import { parseUrl, request } from './util' | ||
export * from './entities' | ||
export * from './types' | ||
@@ -53,3 +54,3 @@ /** | ||
private async getItemById (type: 'video' | 'channel' | 'playlist', id: string) { | ||
private async getItemById (type: 'video' | 'channel' | 'playlist' | 'comment', id: string) { | ||
let result | ||
@@ -75,2 +76,8 @@ | ||
}) | ||
} else if (type === 'comment') { | ||
result = await request.api('comments', { | ||
id, | ||
part: 'snippet', | ||
key: this.token | ||
}) | ||
} | ||
@@ -137,2 +144,10 @@ | ||
/** | ||
* Get a comment object from the ID of a comment. | ||
* @param id The ID of the comment. | ||
*/ | ||
public async getComment (id: string) { | ||
return new YTComment(this, await this.getItemById('comment', id)) | ||
} | ||
/** | ||
* Get a video object from the url of a video. | ||
@@ -180,3 +195,3 @@ * @param url The url of the video. | ||
/** | ||
* Get `maxResults` videos in a playlist. Used mostly internally with `Playlist#getVideos`. | ||
* Get `maxResults` videos in a playlist. Used mostly internally with `Playlist#fetchVideos`. | ||
* @param playlistId The ID of the playlist. | ||
@@ -209,3 +224,3 @@ * @param maxResults The maximum amount of videos to get from the playlist. If <= 0 or not included, returns all videos in the playlist. | ||
const totalResults = results.pageInfo.totalResults | ||
const perPage = 50 | ||
const perPage = results.pageInfo.resultsPerPage | ||
const pages = Math.floor(totalResults / perPage) | ||
@@ -240,2 +255,140 @@ | ||
} | ||
/** | ||
* 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[] = [] | ||
if (maxResults <= 0) { | ||
full = true | ||
} else { | ||
full = false | ||
} | ||
if (maxResults > 100) { | ||
return Promise.reject('Max results must be 100 or below') | ||
} | ||
const results = await request.api('commentThreads', { | ||
videoId, | ||
part: 'snippet,replies', | ||
key: this.token, | ||
maxResults: full ? 100 : maxResults, | ||
textFormat: 'plainText' | ||
}).catch(() => { | ||
return Promise.reject('Comment thread not found') | ||
}) | ||
const totalResults = results.pageInfo.totalResults | ||
const perPage = results.pageInfo.resultsPerPage | ||
const pages = Math.floor(totalResults / perPage) | ||
results.items.forEach(item => { | ||
const comment = new YTComment(this, item.snippet.topLevelComment) | ||
comments.push(comment) | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new YTComment(this, reply) | ||
comment.replies.push(created) | ||
}) | ||
} | ||
}) | ||
if (!full || pages === 0) { | ||
return comments | ||
} | ||
let oldRes = results | ||
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' | ||
}) | ||
oldRes = newResults | ||
newResults.items.forEach(item => { | ||
const comment = new YTComment(this, item.snippet.topLevelComment) | ||
comments.push(comment) | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new YTComment(this, reply) | ||
comment.replies.push(created) | ||
}) | ||
} | ||
}) | ||
} | ||
return comments | ||
} | ||
/** | ||
* 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 | ||
} | ||
} |
@@ -7,8 +7,3 @@ import { request as https } from 'https' | ||
api: (subUrl: string, params: Object): Promise<any> => { | ||
let url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? subUrl : '/' + subUrl) | ||
for (let param in params) { | ||
url += (!url.includes('?') ? '?' : '&') + param + '=' + params[param] | ||
} | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? subUrl : '/' + subUrl) + parseParams(params) | ||
return get(url) | ||
@@ -99,1 +94,11 @@ } | ||
} | ||
function parseParams (params: Object) { | ||
let url = '' | ||
for (let param in params) { | ||
url += (!url.includes('?') ? '?' : '&') + param + '=' + params[param] | ||
} | ||
return url | ||
} |
import 'mocha' | ||
import { expect } from 'chai' | ||
import { YouTube, Video, Playlist } from '../src' | ||
import { YouTube, Video, YTComment, Playlist } from '../src' | ||
import { apiKey } from './config' | ||
@@ -43,3 +43,2 @@ import { parseUrl } from '../src/util' | ||
const youtube = new YouTube(apiKey) | ||
expect(await youtube.getVideo('dQw4w9WgXcQ')).to.be.instanceOf(Video) | ||
@@ -59,2 +58,17 @@ }) | ||
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', () => { | ||
@@ -81,2 +95,27 @@ it('should reject if the playlist isn\'t found', async () => { | ||
}) | ||
describe('Video comments', () => { | ||
it('should work with valid videos with comments and replies', async () => { | ||
const youtube = new YouTube(apiKey) | ||
const comments = await youtube.getVideoComments('Lq1D8PFnjWY') | ||
expect(comments.find(comment => comment.text.displayed.startsWith('comment'))).to.be.instanceOf(YTComment) | ||
expect(comments.find(comment => comment.text.displayed.startsWith('comment')).replies[0]).to.be.instanceOf(YTComment) | ||
}) | ||
it('should not work with valid videos with comments disabled', async () => { | ||
const youtube = new YouTube(apiKey) | ||
expect(await youtube.getVideoComments('24EWkH5ipdw').catch(error => { return error })).to.equal('Comment thread not found') | ||
}) | ||
it('should not work with invalid videos', async () => { | ||
const youtube = new YouTube(apiKey) | ||
expect(await youtube.getVideoComments('0').catch(error => { return error })).to.equal('Comment thread not found') | ||
}) | ||
it('should return an array with a length of <= maxResults', async () => { | ||
const youtube = new YouTube(apiKey) | ||
expect((await youtube.getVideoComments('Lq1D8PFnjWY', 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
78177
29
2268
10