better-youtube-api
Advanced tools
Comparing version 1.0.7 to 2.0.0
@@ -0,0 +0,0 @@ ### 1.0.7 |
@@ -6,7 +6,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./Video")); | ||
__export(require("./Channel")); | ||
__export(require("./Playlist")); | ||
__export(require("./Comment")); | ||
__export(require("./video")); | ||
__export(require("./channel")); | ||
__export(require("./playlist")); | ||
__export(require("./comment")); | ||
//# sourceMappingURL=index.js.map |
252
out/index.js
@@ -18,2 +18,3 @@ "use strict"; | ||
const caching_1 = require("./util/caching"); | ||
const oauth_1 = require("./oauth"); | ||
__export(require("./entities")); | ||
@@ -27,2 +28,3 @@ /** | ||
* @param token Your YouTube Data API v3 token. Don't share this with anybody. | ||
* It could be an API key or an OAuth 2.0 token. | ||
* @param options Caching options. Recommended to change. | ||
@@ -32,2 +34,3 @@ */ | ||
this.token = token; | ||
this.oauth = new oauth_1.OAuth(this); | ||
this._shouldCache = options.cache; | ||
@@ -40,2 +43,9 @@ this._cacheSearches = options.cacheSearches; | ||
} | ||
get token() { | ||
return this._token; | ||
} | ||
set token(val) { | ||
this._token = val; | ||
this.tokenType = val.startsWith('ya29') ? 'oauth' : 'key'; | ||
} | ||
_cache(id, value) { | ||
@@ -53,3 +63,3 @@ if (!this._shouldCache) { | ||
searchVideos(searchTerm, maxResults = 10) { | ||
return this.search('video', searchTerm, maxResults); | ||
return this.search(entities_1.Video, searchTerm, maxResults); | ||
} | ||
@@ -62,3 +72,3 @@ /** | ||
searchChannels(searchTerm, maxResults = 10) { | ||
return this.search('channel', searchTerm, maxResults); | ||
return this.search(entities_1.Channel, searchTerm, maxResults); | ||
} | ||
@@ -71,3 +81,3 @@ /** | ||
searchPlaylists(searchTerm, maxResults = 10) { | ||
return this.search('playlist', searchTerm, maxResults); | ||
return this.search(entities_1.Playlist, searchTerm, maxResults); | ||
} | ||
@@ -79,3 +89,3 @@ /** | ||
getVideo(id) { | ||
return this.getItemById('video', id); | ||
return this.getItemById(entities_1.Video, id); | ||
} | ||
@@ -87,3 +97,3 @@ /** | ||
getChannel(id) { | ||
return this.getItemById('channel', id); | ||
return this.getItemById(entities_1.Channel, id); | ||
} | ||
@@ -95,3 +105,3 @@ /** | ||
getPlaylist(id) { | ||
return this.getItemById('playlist', id); | ||
return this.getItemById(entities_1.Playlist, id); | ||
} | ||
@@ -103,3 +113,3 @@ /** | ||
getComment(id) { | ||
return this.getItemById('comment', id); | ||
return this.getItemById(entities_1.YTComment, id); | ||
} | ||
@@ -115,3 +125,3 @@ /** | ||
} | ||
return this.getItemById('video', id.video); | ||
return this.getItemById(entities_1.Video, id.video); | ||
} | ||
@@ -127,3 +137,3 @@ /** | ||
} | ||
return this.getItemById('channel', id.channel); | ||
return this.getItemById(entities_1.Channel, id.channel); | ||
} | ||
@@ -139,3 +149,3 @@ /** | ||
} | ||
return this.getItemById('playlist', id.playlist); | ||
return this.getItemById(entities_1.Playlist, id.playlist); | ||
} | ||
@@ -151,4 +161,3 @@ /** | ||
/** | ||
* 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. | ||
* Get `maxResults` comments from a video. Used mostly internally with `Video#fetchComments`. | ||
* @param videoId The ID of the video. | ||
@@ -158,5 +167,13 @@ * @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); | ||
return this.getPaginatedItems('commentThreads:video', videoId, maxResults); | ||
} | ||
/** | ||
* Get `maxResults` comments from a channel's discussion tab. Used mostly internally with `Channel#fetchComments`. | ||
* @param channelId The ID of the channel. | ||
* @param maxResults The maximum amount of comments to get from the channel. If <= 0 or not included, returns all comments on the channel. | ||
*/ | ||
getChannelComments(channelId, maxResults = -1) { | ||
return this.getPaginatedItems('commentThreads:channel', channelId, maxResults); | ||
} | ||
/** | ||
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`. | ||
@@ -172,3 +189,3 @@ * @param commentId The ID of the comment to get replies from. | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const cached = caching_1.Cache.get(`search://${type}/"${searchTerm}"/${maxResults}`); | ||
const cached = caching_1.Cache.get(`search://${type.endpoint}/"${searchTerm}"/${maxResults}`); | ||
if (this._shouldCache && cached) { | ||
@@ -183,24 +200,11 @@ return cached; | ||
maxResults, | ||
key: this.token, | ||
part: 'snippet', | ||
type | ||
}); | ||
type: type.endpoint.substring(0, type.endpoint.length - 1) | ||
}, this.token, this.tokenType); | ||
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: | ||
return Promise.reject('Type must be a video, channel, or playlist'); | ||
} | ||
items.push(new type(this, item)); | ||
}); | ||
if (this._shouldCache && this._cacheSearches) { | ||
this._cache(`search://${type}/"${searchTerm}"/${maxResults}`, items); | ||
this._cache(`search://${type.endpoint}/"${searchTerm}"/${maxResults}`, items); | ||
} | ||
@@ -213,57 +217,16 @@ return items; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const cached = caching_1.Cache.get(`get://${type}/${id}`); | ||
if (!([entities_1.Video, entities_1.Channel, entities_1.Playlist, entities_1.YTComment].includes(type))) { | ||
return Promise.reject('Type must be a video, channel, playlist, or comment.'); | ||
} | ||
const cached = caching_1.Cache.get(`get://${type.endpoint}/${id}`); | ||
if (this._shouldCache && cached) { | ||
return cached; | ||
} | ||
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 | ||
}); | ||
} | ||
const result = yield util_1.request.api(type.endpoint, { id, part: type.part }, this.token, this.tokenType); | ||
if (result.items.length === 0) { | ||
return Promise.reject('Item not found'); | ||
} | ||
let endResult; | ||
switch (type) { | ||
case 'video': | ||
endResult = new entities_1.Video(this, result.items[0]); | ||
break; | ||
case 'playlist': | ||
endResult = new entities_1.Playlist(this, result.items[0]); | ||
break; | ||
case 'channel': | ||
endResult = new entities_1.Channel(this, result.items[0]); | ||
break; | ||
case 'comment': | ||
endResult = new entities_1.YTComment(this, result.items[0]); | ||
break; | ||
default: | ||
return Promise.reject('Type must be a video, channel, playlist, or comment.'); | ||
} | ||
let endResult = new type(this, result.items[0], result.items[0].snippet.channelId ? 'channel' : 'video'); | ||
if (this._shouldCache) { | ||
this._cache(`get://${type}/${id}`, endResult); | ||
this._cache(`get://${type.endpoint}/${id}`, endResult); | ||
} | ||
@@ -274,5 +237,5 @@ return endResult; | ||
/* istanbul ignore next */ | ||
getPaginatedItems(type, id, maxResults = -1) { | ||
getPaginatedItems(endpoint, id, maxResults = -1) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const cached = caching_1.Cache.get(`get://${type}/${id}/${maxResults}`); | ||
const cached = caching_1.Cache.get(`get://${endpoint}/${id}/${maxResults}`); | ||
if (this._shouldCache && cached) { | ||
@@ -289,88 +252,65 @@ return cached; | ||
} | ||
const options = { | ||
part: 'snippet', | ||
maxResults: 0 | ||
}; | ||
let max; | ||
if (type === 'playlistItems') { | ||
let clazz; | ||
let commentType; | ||
if (endpoint === 'playlistItems') { | ||
max = 50; | ||
clazz = entities_1.Video; | ||
options.playlistId = id; | ||
} | ||
else if (type === 'commentThreads' || type === 'comments') { | ||
else if (endpoint.startsWith('commentThreads')) { | ||
max = 100; | ||
clazz = entities_1.YTComment; | ||
const [, type] = endpoint.split(':'); | ||
commentType = type ? type : 'video'; | ||
endpoint = 'commentThreads'; | ||
options[`${type}Id`] = id; | ||
options.part += ',replies'; | ||
options.textFormat = 'plainText'; | ||
} | ||
else if (endpoint === 'comments') { | ||
max = 100; | ||
clazz = entities_1.YTComment; | ||
options.parentId = id; | ||
} | ||
else { | ||
return Promise.reject('Unknown item type ' + type); | ||
return Promise.reject('Unknown item type ' + endpoint); | ||
} | ||
if (maxResults > max) { | ||
return Promise.reject(`Max results must be ${max} or below for ${type}`); | ||
return Promise.reject(`Max results must be ${max} or below for ${endpoint}`); | ||
} | ||
const options = { | ||
part: 'snippet', | ||
key: this.token, | ||
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; | ||
const perPage = results.pageInfo.resultsPerPage; | ||
const pages = Math.floor(totalResults / perPage); | ||
results.items.forEach(item => { | ||
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; | ||
options.maxResults = full ? max : maxResults; | ||
let results; | ||
let pages = null; | ||
let shouldReturn = !full; | ||
for (let i = 1; i < pages ? pages : 3; i++) { | ||
results = yield util_1.request.api(endpoint, options, this.token, this.tokenType).catch(() => { | ||
return Promise.reject('Items not found'); | ||
}); | ||
if (results.items.length === 0) { | ||
return Promise.reject('Items not found'); | ||
} | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new entities_1.YTComment(this, reply); | ||
comment.replies.push(created); | ||
}); | ||
if (!pages) { | ||
pages = results.pageInfo.totalResults / results.pageInfo.resultsPerPage; | ||
if (pages <= 1) { | ||
shouldReturn = true; | ||
} | ||
pages = Math.floor(pages); | ||
} | ||
}); | ||
if (!full || pages === 0) { | ||
return items; | ||
} | ||
let oldRes = results; | ||
options.pageToken = oldRes.nextPageToken; | ||
for (let i = 1; i < pages; i++) { | ||
const newResults = yield util_1.request.api(type, options); | ||
oldRes = newResults; | ||
newResults.items.forEach(item => { | ||
results.items.forEach(item => { | ||
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.snippet.topLevelComment) { | ||
comment = new entities_1.YTComment(this, item.snippet.topLevelComment, commentType); | ||
items.push(comment); | ||
} | ||
else { | ||
items.push(new clazz(this, item, commentType)); | ||
} | ||
if (item.replies) { | ||
item.replies.comments.forEach(reply => { | ||
const created = new entities_1.YTComment(this, reply); | ||
const created = new entities_1.YTComment(this, reply, commentType); | ||
comment.replies.push(created); | ||
@@ -380,5 +320,11 @@ }); | ||
}); | ||
if (results.nextPageToken && !shouldReturn) { | ||
options.pageToken = results.nextPageToken; | ||
} | ||
else { | ||
return items; | ||
} | ||
} | ||
if (this._shouldCache) { | ||
this._cache(`get://${type}/${id}/${maxResults}`, items); | ||
this._cache(`get://${endpoint}/${id}/${maxResults}`, items); | ||
} | ||
@@ -385,0 +331,0 @@ return items; |
@@ -25,3 +25,3 @@ "use strict"; | ||
const time = new Date().getTime(); | ||
if (!item || item.t <= 0) { | ||
if (item.t <= 0) { | ||
return; | ||
@@ -28,0 +28,0 @@ } |
@@ -6,5 +6,20 @@ "use strict"; | ||
exports.request = { | ||
api: (subUrl, params) => { | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? subUrl : '/' + subUrl) + parseParams(params); | ||
return get(url); | ||
api: (subUrl, params, token, type) => { | ||
/* istanbul ignore next */ | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? '' : '/') + subUrl + parseParams(params) + | ||
(type === 'key' ? (params ? `&key=${token}` : `?key=${token}`) : ''); | ||
/* istanbul ignore next */ | ||
return get(url, type === 'oauth' ? token : undefined); | ||
}, | ||
post: (subUrl, params, token, data) => { | ||
/* istanbul ignore next */ | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? '' : '/') + subUrl + parseParams(params); | ||
/* istanbul ignore next */ | ||
return post(url, data, token); | ||
}, | ||
put: (subUrl, params, token, data) => { | ||
/* istanbul ignore next */ | ||
const url = 'https://www.googleapis.com/youtube/v3' + (subUrl.startsWith('/') ? '' : '/') + subUrl + parseParams(params); | ||
/* istanbul ignore next */ | ||
return put(url, data, token); | ||
} | ||
@@ -15,10 +30,9 @@ }; | ||
*/ | ||
function get(url) { | ||
/* istanbul ignore next */ | ||
function get(url, token) { | ||
const options = parseUrlToOptions(url, 'GET'); | ||
return req(options, req => { | ||
req.on('error', error => { | ||
throw error; | ||
}); | ||
req.end(); | ||
}); | ||
if (token) { | ||
options.headers['Authorization'] = `Bearer ${token}`; | ||
} | ||
return req(options, req => reqCallback(req)); | ||
} | ||
@@ -28,2 +42,25 @@ /** | ||
*/ | ||
/* istanbul ignore next */ | ||
function post(url, data, token) { | ||
const options = parseUrlToOptions(url, 'POST'); | ||
if (token) { | ||
options.headers['Authorization'] = `Bearer ${token}`; | ||
} | ||
return req(options, req => reqCallback(req, data)); | ||
} | ||
/** | ||
* @ignore | ||
*/ | ||
/* istanbul ignore next */ | ||
function put(url, data, token) { | ||
const options = parseUrlToOptions(url, 'PUT'); | ||
if (token) { | ||
options.headers['Authorization'] = `Bearer ${token}`; | ||
} | ||
return req(options, req => reqCallback(req, data)); | ||
} | ||
/** | ||
* @ignore | ||
*/ | ||
/* istanbul ignore next */ | ||
function parseUrlToOptions(url, type) { | ||
@@ -44,2 +81,3 @@ const parsed = url_1.parse(url); | ||
*/ | ||
/* istanbul ignore next */ | ||
function req(options, reqFunction) { | ||
@@ -54,2 +92,5 @@ return new Promise((resolve, reject) => { | ||
res.on('end', () => { | ||
if (res.statusCode === 404) { | ||
return reject(new Error('Not found')); | ||
} | ||
const parsed = JSON.parse(data); | ||
@@ -71,2 +112,16 @@ if (parsed.error) { | ||
*/ | ||
/* istanbul ignore next */ | ||
function reqCallback(req, data) { | ||
req.on('error', error => { | ||
throw error; | ||
}); | ||
if (data) { | ||
req.write(data); | ||
} | ||
req.end(); | ||
} | ||
/** | ||
* @ignore | ||
*/ | ||
/* istanbul ignore next */ | ||
function parseParams(params) { | ||
@@ -73,0 +128,0 @@ let url = ''; |
{ | ||
"name": "better-youtube-api", | ||
"version": "1.0.7", | ||
"version": "2.0.0", | ||
"description": "A very easy to use promise-based Youtube Data v3 API.", | ||
@@ -8,4 +8,4 @@ "main": "out/index.js", | ||
"scripts": { | ||
"test": "mocha --exit", | ||
"coverage": "nyc mocha --exit && nyc report --reporter=text-lcov | coveralls", | ||
"test": "mocha --exit --require ts-node/register --require source-map-support/register \"./test/!(oauth).spec.ts\"", | ||
"coverage": "nyc yarn test && nyc report --reporter=text-lcov | coveralls", | ||
"docs": "gulp typedoc", | ||
@@ -12,0 +12,0 @@ "prepublishOnly": "gulp" |
@@ -1,4 +0,4 @@ | ||
export * from './Video'; | ||
export * from './Channel'; | ||
export * from './Playlist'; | ||
export * from './Comment'; | ||
export * from './video'; | ||
export * from './channel'; | ||
export * from './playlist'; | ||
export * from './comment'; |
import { Video, Channel, Playlist, YTComment } from './entities'; | ||
import { OAuth } from './oauth'; | ||
export * from './entities'; | ||
@@ -11,6 +12,10 @@ export * from './types'; | ||
private _cacheTTL; | ||
private _token; | ||
token: string; | ||
tokenType: 'key' | 'oauth'; | ||
oauth: OAuth; | ||
/** | ||
* | ||
* @param token Your YouTube Data API v3 token. Don't share this with anybody. | ||
* It could be an API key or an OAuth 2.0 token. | ||
* @param options Caching options. Recommended to change. | ||
@@ -80,4 +85,3 @@ */ | ||
/** | ||
* 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. | ||
* Get `maxResults` comments from a video. Used mostly internally with `Video#fetchComments`. | ||
* @param videoId The ID of the video. | ||
@@ -88,2 +92,8 @@ * @param maxResults The maximum amount of comments to get from the video. If <= 0 or not included, returns all comments on the video. | ||
/** | ||
* Get `maxResults` comments from a channel's discussion tab. Used mostly internally with `Channel#fetchComments`. | ||
* @param channelId The ID of the channel. | ||
* @param maxResults The maximum amount of comments to get from the channel. If <= 0 or not included, returns all comments on the channel. | ||
*/ | ||
getChannelComments(channelId: string, maxResults?: number): Promise<YTComment[]>; | ||
/** | ||
* Get `maxResults` replies to a comment. Used mostly internally with `Comment#fetchReplies`. | ||
@@ -90,0 +100,0 @@ * @param commentId The ID of the comment to get replies from. |
export declare const request: { | ||
api: (subUrl: string, params: Object) => Promise<any>; | ||
api: (subUrl: string, params: Object, token: string, type: "oauth" | "key") => Promise<any>; | ||
post: (subUrl: string, params: Object, token: string, data: any) => Promise<any>; | ||
put: (subUrl: string, params: Object, token: string, data: any) => Promise<any>; | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
132405
50
1667
2