mirror of
https://github.com/musix-org/musix-oss
synced 2025-06-16 12:36:01 +00:00
fix
This commit is contained in:
21
node_modules/simple-youtube-api/LICENSE
generated
vendored
Normal file
21
node_modules/simple-youtube-api/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Hyper-Coder
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
14
node_modules/simple-youtube-api/README.md
generated
vendored
Normal file
14
node_modules/simple-youtube-api/README.md
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# Simple YouTube API
|
||||
|
||||
[](https://discord.gg/A97Qftr)
|
||||
[](https://travis-ci.org/HyperCoder2975/simple-youtube-api)
|
||||
|
||||
This library is designed to greatly simplify interacting with the basic functions of the YouTube API.
|
||||
It deals with viewing/searching videos, playlists, and channels.
|
||||
|
||||
## Installation
|
||||
$ npm install simple-youtube-api
|
||||
|
||||
## Usage
|
||||
- [Documentation](https://HyperCoder2975.github.io/simple-youtube-api/master/)
|
||||
- [Examples](https://github.com/HyperCoder2975/simple-youtube-api/tree/master/examples)
|
294
node_modules/simple-youtube-api/dist/sya.js
generated
vendored
Normal file
294
node_modules/simple-youtube-api/dist/sya.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/simple-youtube-api/dist/sya.min.js
generated
vendored
Normal file
1
node_modules/simple-youtube-api/dist/sya.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
13
node_modules/simple-youtube-api/examples/playlist.js
generated
vendored
Normal file
13
node_modules/simple-youtube-api/examples/playlist.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
const YouTube = require('simple-youtube-api');
|
||||
const youtube = new YouTube(' Y o u r A p i K e y ');
|
||||
|
||||
youtube.getPlaylist('https://www.youtube.com/playlist?list=PL2BN1Zd8U_MsyMeK8r9Vdv1lnQGtoJaSa')
|
||||
.then(playlist => {
|
||||
console.log(`The playlist's title is ${playlist.title}`);
|
||||
playlist.getVideos()
|
||||
.then(videos => {
|
||||
console.log(`This playlist has ${videos.length === 50 ? '50+' : videos.length} videos.`);
|
||||
})
|
||||
.catch(console.log);
|
||||
})
|
||||
.catch(console.log);
|
8
node_modules/simple-youtube-api/examples/search.js
generated
vendored
Normal file
8
node_modules/simple-youtube-api/examples/search.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
const YouTube = require('simple-youtube-api');
|
||||
const youtube = new YouTube(' Y o u r A p i K e y ');
|
||||
|
||||
youtube.searchVideos('Centuries', 4)
|
||||
.then(results => {
|
||||
console.log(`The video's title is ${results[0].title}`);
|
||||
})
|
||||
.catch(console.log);
|
8
node_modules/simple-youtube-api/examples/video.js
generated
vendored
Normal file
8
node_modules/simple-youtube-api/examples/video.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
const YouTube = require('simple-youtube-api');
|
||||
const youtube = new YouTube(' Y o u r A p i K e y ');
|
||||
|
||||
youtube.videoById('https://www.youtube.com/watch?v=3odIdmuFfEY')
|
||||
.then(video => {
|
||||
console.log(`The video's title is ${video[0].title}`);
|
||||
})
|
||||
.catch(console.log);
|
70
node_modules/simple-youtube-api/package.json
generated
vendored
Normal file
70
node_modules/simple-youtube-api/package.json
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"_from": "simple-youtube-api",
|
||||
"_id": "simple-youtube-api@5.1.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-cbWRGTDjWDrC+8LR3gpLaXeRb0+e/eiX/bKYIS0tQ4gBUimhXVhsWWhzWJ49aTBnAA0vChrD3U0ZDL8/CUWNcA==",
|
||||
"_location": "/simple-youtube-api",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "simple-youtube-api",
|
||||
"name": "simple-youtube-api",
|
||||
"escapedName": "simple-youtube-api",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/simple-youtube-api/-/simple-youtube-api-5.1.1.tgz",
|
||||
"_shasum": "6e6d3892f6651c87cd7591d07f8e0259860447b4",
|
||||
"_spec": "simple-youtube-api",
|
||||
"_where": "C:\\Users\\matia\\Bot Files",
|
||||
"author": {
|
||||
"name": "Hyper-Coder"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/HyperCoder2975/simple-youtube-api/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"iso8601-duration": "^1.0.0",
|
||||
"node-fetch": "^2.2.1"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "A module to simplify the YouTube API.",
|
||||
"devDependencies": {
|
||||
"dotenv": "^4.0.0",
|
||||
"jsdoc": "^3.4.0",
|
||||
"lodash.defaultsdeep": "^4.6.0",
|
||||
"minami": "^1.1.0",
|
||||
"mocha": "^3.5.3",
|
||||
"webpack": "^4.25.1",
|
||||
"webpack-cli": "^3.1.2"
|
||||
},
|
||||
"homepage": "https://github.com/HyperCoder2975/simple-youtube-api",
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Will Nelson",
|
||||
"email": "will@pleb.xyz"
|
||||
}
|
||||
],
|
||||
"name": "simple-youtube-api",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/HyperCoder2975/simple-youtube-api.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"docs": "jsdoc -c jsdoc.json",
|
||||
"test": "mocha"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"unpkg": "dist/sya.min.js",
|
||||
"version": "5.1.1"
|
||||
}
|
101
node_modules/simple-youtube-api/src/Request.js
generated
vendored
Normal file
101
node_modules/simple-youtube-api/src/Request.js
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
const fetch = require('node-fetch');
|
||||
const Constants = require('./util/Constants');
|
||||
|
||||
class Request {
|
||||
constructor(youtube) {
|
||||
this.youtube = youtube;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the YouTube API
|
||||
* @param {string} endpoint The endpoint to query
|
||||
* @param {object} [qs={}] Query strings
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
make(endpoint, qs = {}) {
|
||||
qs = Object.assign({ key: this.youtube.key }, qs);
|
||||
const params = Object.keys(qs).filter(k => qs[k]).map(k => `${k}=${qs[k]}`);
|
||||
return fetch(encodeURI(`https://www.googleapis.com/youtube/v3/${endpoint}${params.length ? `?${params.join('&')}` : ''}`))
|
||||
.then(result => result.json())
|
||||
.then(result => {
|
||||
if (result.error) return Promise.reject(result.error);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource from the YouTube API
|
||||
* @param {string} type The type of resource to get
|
||||
* @param {object} [qs={}] Any other query options
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
getResource(type, qs = {}) {
|
||||
qs = Object.assign({ part: Constants.PARTS[type] }, qs);
|
||||
return this.make(Constants.ENDPOINTS[type], qs).then(result =>
|
||||
result.items.length ? result.items[0] : Promise.reject(new Error(`resource ${result.kind} not found`))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource from the YouTube API, by ID
|
||||
* @param {string} type The type of resource to get
|
||||
* @param {string} id The ID of the resource to get
|
||||
* @param {object} [qs={}] Any other query options
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
getResourceByID(type, id, qs = {}) {
|
||||
return this.getResource(type, Object.assign(qs, { id }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a video from the YouTube API
|
||||
* @param {string} id The video to get
|
||||
* @param {object} [options] Any request options
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
getVideo(id, options) {
|
||||
return this.getResourceByID('Videos', id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a playlist from the YouTube API
|
||||
* @param {string} id The playlist to get
|
||||
* @param {object} [options] Any request options
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
getPlaylist(id, options) {
|
||||
return this.getResourceByID('Playlists', id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel from the YouTube API
|
||||
* @param {string} id The channel to get
|
||||
* @param {object} [options] Any request options
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
getChannel(id, options) {
|
||||
return this.getResourceByID('Channels', id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a paginated resource.
|
||||
* @param {string} endpoint The endpoint to query.
|
||||
* @param {number} [count=Infinity] How many results to retrieve.
|
||||
* @param {Object} [options={}] Additional options to send.
|
||||
* @param {Array} [fetched=[]] Previously fetched resources.
|
||||
* @param {?string} [pageToken] The page token to retrieve.
|
||||
* @returns {Promise<Array<object>>}
|
||||
*/
|
||||
getPaginated(endpoint, count = Infinity, options = {}, fetched = [], pageToken = null) {
|
||||
if(count < 1) return Promise.reject('Cannot fetch less than 1.');
|
||||
|
||||
const limit = count > 50 ? 50 : count;
|
||||
return this.make(endpoint, Object.assign(options, { pageToken, maxResults: limit })).then(result => {
|
||||
const results = fetched.concat(result.items);
|
||||
if(result.nextPageToken && limit !== count) return this.getPaginated(endpoint, count - limit, options, results, result.nextPageToken);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Request;
|
226
node_modules/simple-youtube-api/src/index.js
generated
vendored
Normal file
226
node_modules/simple-youtube-api/src/index.js
generated
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
const Request = require('./Request');
|
||||
|
||||
const Video = require('./structures/Video');
|
||||
const Playlist = require('./structures/Playlist');
|
||||
const Channel = require('./structures/Channel');
|
||||
|
||||
const util = require('./util');
|
||||
const Constants = require('./util/Constants');
|
||||
|
||||
/**
|
||||
* Information about a thumbnail
|
||||
* @typedef {Object} Thumbnail
|
||||
* @property {string} url The URL of this thumbnail
|
||||
* @property {number} width The width of this thumbnail
|
||||
* @property {number} height The height of this thumbnail
|
||||
*/
|
||||
|
||||
/**
|
||||
* The YouTube API module
|
||||
*/
|
||||
class YouTube {
|
||||
/**
|
||||
* @param {string} key The YouTube Data API v3 key to use
|
||||
*/
|
||||
constructor(key) {
|
||||
if (typeof key !== 'string') throw new Error('The YouTube API key you provided was not a string.');
|
||||
/**
|
||||
* The YouTube Data API v3 key
|
||||
* @type {?string}
|
||||
*/
|
||||
this.key = key;
|
||||
Object.defineProperty(this, 'key', { enumerable: false });
|
||||
|
||||
this.request = new Request(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the YouTube API
|
||||
* @param {string} endpoint The endpoint of the API
|
||||
* @param {Object} qs The query string options
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get a video by URL or ID
|
||||
* @param {string} url The video URL or ID
|
||||
* @param {Object} [options = {}] Options to request with the video.
|
||||
* @returns {Promise<?Video>}
|
||||
* @example
|
||||
* API.getVideo('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
|
||||
* .then(results => {
|
||||
* console.log(`The video's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getVideo(url, options = {}) {
|
||||
const id = Video.extractID(url);
|
||||
if (!id) return Promise.reject(new Error(`No video ID found in URL: ${url}`));
|
||||
return this.getVideoByID(id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a video by ID
|
||||
* @param {string} id The video ID
|
||||
* @param {Object} [options = {}] Options to request with the video.
|
||||
* @returns {Promise<?Video>}
|
||||
* @example
|
||||
* API.getVideoByID('3odIdmuFfEY')
|
||||
* .then(results => {
|
||||
* console.log(`The video's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getVideoByID(id, options = {}) {
|
||||
return this.request.getVideo(id, options).then(result => result ? new Video(this, result) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a playlist by URL or ID
|
||||
* @param {string} url The playlist URL or ID
|
||||
* @param {Object} [options = {}] Options to request with the playlist.
|
||||
* @returns {Promise<?Playlist>}
|
||||
* @example
|
||||
* API.getPlaylist('https://www.youtube.com/playlist?list=PLuY9odN8x9puRuCxiddyRzJ3F5jR-Gun9')
|
||||
* .then(results => {
|
||||
* console.log(`The playlist's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getPlaylist(url, options = {}) {
|
||||
const id = Playlist.extractID(url);
|
||||
if (!id) return Promise.reject(new Error(`No playlist ID found in URL: ${url}`));
|
||||
return this.getPlaylistByID(id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a playlist by ID
|
||||
* @param {string} id The playlist ID
|
||||
* @param {Object} [options = {}] Options to request with the playlist.
|
||||
* @returns {Promise<?Playlist>}
|
||||
* @example
|
||||
* API.getPlaylistByID('PL2BN1Zd8U_MsyMeK8r9Vdv1lnQGtoJaSa')
|
||||
* .then(results => {
|
||||
* console.log(`The playlist's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getPlaylistByID(id, options = {}) {
|
||||
return this.request.getPlaylist(id, options).then(result => result ? new Playlist(this, result) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel by URL or ID
|
||||
* @param {string} url The channel URL or ID
|
||||
* @param {Object} [options = {}] Options to request with the channel.
|
||||
* @returns {Promise<?Channel>}
|
||||
* @example
|
||||
* API.getChannel('https://www.youtube.com/channel/UC477Kvszl9JivqOxN1dFgPQ')
|
||||
* .then(results => {
|
||||
* console.log(`The channel's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getChannel(url, options = {}) {
|
||||
const id = Channel.extractID(url);
|
||||
if (!id) return Promise.reject(new Error(`No channel ID found in URL: ${url}`));
|
||||
return this.getChannelByID(id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel by ID
|
||||
* @param {string} id The channel ID
|
||||
* @param {Object} [options = {}] Options to request with the channel.
|
||||
* @returns {Promise<?Channel>}
|
||||
* @example
|
||||
* API.getChannelByID('UC477Kvszl9JivqOxN1dFgPQ')
|
||||
* .then(results => {
|
||||
* console.log(`The channel's title is ${results[0].title}`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
getChannelByID(id, options = {}) {
|
||||
return this.request.getChannel(id, options).then(result => result ? new Channel(this, result) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search YouTube for videos, playlists, and channels
|
||||
* @param {string} query The string to search for
|
||||
* @param {number} [limit = 5] Maximum results to obtain
|
||||
* @param {Object} [options] Additional options to pass to the API request
|
||||
* @returns {Promise<Array<Video|Playlist|Channel|null>>}
|
||||
* @example
|
||||
* API.search('Centuries')
|
||||
* .then(results => {
|
||||
* console.log(`I got ${results.length} results`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
search(query, limit = 5, options = {}) {
|
||||
return this.request.getPaginated(Constants.ENDPOINTS.Search, limit, Object.assign(options, { q: query, part: Constants.PARTS.Search }))
|
||||
.then(result => result.map(item => {
|
||||
if (item.id.kind === Constants.KINDS.Video) return new Video(this, item);
|
||||
if (item.id.kind === Constants.KINDS.Playlist) return new Playlist(this, item);
|
||||
if (item.id.kind === Constants.KINDS.Channel) return new Channel(this, item);
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search YouTube for videos
|
||||
* @param {string} query The string to search for
|
||||
* @param {number} [limit = 5] Maximum results to obtain
|
||||
* @param {Object} [options] Additional options to pass to the API request
|
||||
* @returns {Promise<Video[]>}
|
||||
* @example
|
||||
* API.searchVideos('Centuries')
|
||||
* .then(results => {
|
||||
* console.log(`I got ${results.length} videos`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
searchVideos(query, limit = 5, options = {}) {
|
||||
return this.search(query, limit, Object.assign(options, { type: 'video' }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search YouTube for playlists
|
||||
* @param {string} query The string to search for
|
||||
* @param {number} [limit = 5] Maximum results to obtain
|
||||
* @param {Object} [options] Additional options to pass to the API request
|
||||
* @returns {Promise<Playlist[]>}
|
||||
* @example
|
||||
* API.searchPlaylists('Centuries')
|
||||
* .then(results => {
|
||||
* console.log(`I got ${results.length} playlists`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
searchPlaylists(query, limit = 5, options = {}) {
|
||||
return this.search(query, limit, Object.assign(options, { type: 'playlist' }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search YouTube for channels
|
||||
* @param {string} query The string to search for
|
||||
* @param {number} [limit = 5] Maximum results to obtain
|
||||
* @param {Object} [options] Additional options to pass to the API request
|
||||
* @returns {Promise<Channel[]>}
|
||||
* @example
|
||||
* API.searchChannels('Centuries')
|
||||
* .then(results => {
|
||||
* console.log(`I got ${results.length} channels`);
|
||||
* })
|
||||
* .catch(console.error);
|
||||
*/
|
||||
searchChannels(query, limit = 5, options = {}) {
|
||||
return this.search(query, limit, Object.assign(options, { type: 'channel' }));
|
||||
}
|
||||
}
|
||||
|
||||
YouTube.Video = Video;
|
||||
YouTube.Playlist = Playlist;
|
||||
YouTube.Channel = Channel;
|
||||
YouTube.util = util;
|
||||
|
||||
module.exports = YouTube;
|
212
node_modules/simple-youtube-api/src/structures/Channel.js
generated
vendored
Normal file
212
node_modules/simple-youtube-api/src/structures/Channel.js
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
const { parseURL } = require('../util');
|
||||
const Constants = require('../util/Constants');
|
||||
|
||||
/**
|
||||
* Represents a YouTube channel
|
||||
* @class
|
||||
*/
|
||||
class Channel {
|
||||
/**
|
||||
* @param {YouTube} youtube The YouTube instance creating this
|
||||
* @param {Object} data The data of the channel
|
||||
*/
|
||||
constructor(youtube, data) {
|
||||
/**
|
||||
* The YouTube instance that created this
|
||||
* @type {YouTube}
|
||||
*/
|
||||
this.youtube = youtube;
|
||||
Object.defineProperty(this, 'youtube', { enumerable: false });
|
||||
|
||||
/**
|
||||
* The type to filter search results
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = 'channel';
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
if (!data) return;
|
||||
|
||||
/**
|
||||
* Raw data from the YouTube API
|
||||
* @type {object}
|
||||
*/
|
||||
this.raw = data;
|
||||
|
||||
/**
|
||||
* Whether this is a full channel object.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.full = data.kind === Constants.KINDS.Channel;
|
||||
|
||||
/**
|
||||
* The YouTube resource from which this channel was created.
|
||||
* @type {string}
|
||||
*/
|
||||
this.kind = data.kind;
|
||||
|
||||
/**
|
||||
* This channel's ID
|
||||
* @type {string}
|
||||
* @name Channel#id
|
||||
*/
|
||||
|
||||
/**
|
||||
* This channel's title
|
||||
* @type {?string}
|
||||
* @name Channel#title
|
||||
*/
|
||||
|
||||
switch (data.kind) {
|
||||
case Constants.KINDS.Playlist:
|
||||
case Constants.KINDS.PlaylistItem:
|
||||
case Constants.KINDS.Video:
|
||||
if (data.snippet) {
|
||||
this.id = data.snippet.channelId;
|
||||
this.title = data.snippet.channelTitle;
|
||||
break;
|
||||
} else {
|
||||
throw new Error('Attempted to make a channel out of a resource with no channel data.');
|
||||
}
|
||||
case Constants.KINDS.SearchResult:
|
||||
if (data.id.kind === Constants.KINDS.Channel) {
|
||||
this.id = data.id.channelId;
|
||||
break;
|
||||
} else if (data.snippet) {
|
||||
this.id = data.snippet.channelId;
|
||||
this.title = data.snippet.channelTitle;
|
||||
break;
|
||||
} else {
|
||||
throw new Error('Attempted to make a channel out of a search result with no channel data.');
|
||||
}
|
||||
case Constants.KINDS.Channel:
|
||||
this.id = data.id;
|
||||
if (data.snippet) {
|
||||
this.title = data.snippet.title;
|
||||
|
||||
/**
|
||||
* This channel's description
|
||||
* @type {?string}
|
||||
* @name Channel#description
|
||||
*/
|
||||
this.description = data.snippet.description;
|
||||
|
||||
/**
|
||||
* The channel's custom URL if it has one
|
||||
* @type {?string}
|
||||
*/
|
||||
this.customURL = data.snippet.customUrl;
|
||||
|
||||
/**
|
||||
* The channel's creation date
|
||||
* @type {?Date}
|
||||
* @name Channel#publishedAt
|
||||
*/
|
||||
this.publishedAt = new Date(data.snippet.publishedAt);
|
||||
|
||||
/**
|
||||
* The channel's thumbnails: available types are 'default', 'medium', and 'high'
|
||||
* @type {?Object.<string, Thumbnail>}
|
||||
*/
|
||||
this.thumbnails = data.snippet.thumbnails;
|
||||
|
||||
/**
|
||||
* The channel's default language
|
||||
* @type {?string}
|
||||
*/
|
||||
this.defaultLanguage = data.snippet.defaultLanguage;
|
||||
|
||||
/**
|
||||
* Information about the channel as specified in the `hl` query parameter
|
||||
* @type {?{title: string, description: string}}
|
||||
*/
|
||||
this.localized = data.snippet.localized;
|
||||
|
||||
/**
|
||||
* The country of the channel
|
||||
* @type {?string}
|
||||
*/
|
||||
this.country = data.snippet.country;
|
||||
}
|
||||
|
||||
if (data.contentDetails) {
|
||||
/**
|
||||
* Playlists associated with this channel; all values are playlist IDs
|
||||
* @type {?Object}
|
||||
* @property {?string} likes The channel's liked videos
|
||||
* @property {?string} favorites The channel's favorited videos (note: favorited videos are deprecated)
|
||||
* @property {?string} uploads The channel's uploaded videos
|
||||
*/
|
||||
this.relatedPlaylists = data.contentDetails.relatedPlaylists;
|
||||
}
|
||||
|
||||
if (data.statistics) {
|
||||
/**
|
||||
* The number of times the channel has been viewed
|
||||
* @type {?number}
|
||||
*/
|
||||
this.viewCount = data.statistics.viewCount;
|
||||
|
||||
/**
|
||||
* The number of comments on the channel
|
||||
* @type {?number}
|
||||
*/
|
||||
this.commentCount = data.statistics.commentCount;
|
||||
|
||||
/**
|
||||
* The number of subscribers the channel has
|
||||
* @type {?number}
|
||||
*/
|
||||
this.subscriberCount = data.statistics.subscriberCount;
|
||||
|
||||
/**
|
||||
* Whether the channel's subscriber count is public
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.hiddenSubscriberCount = data.statistics.hiddenSubscriberCount;
|
||||
|
||||
/**
|
||||
* The number of videos this channel has uploaded
|
||||
* @type {?number}
|
||||
*/
|
||||
this.videoCount = data.statistics.videoCount;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown channel kind: ${data.kind}.`);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the full representation of this channel.
|
||||
* @param {object} [options] Any extra query params
|
||||
* @returns {Channel}
|
||||
*/
|
||||
fetch(options) {
|
||||
return this.youtube.request.getChannel(this.id, options).then(this._patch.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to this channel
|
||||
* @type {string}
|
||||
*/
|
||||
get url() {
|
||||
return `https://www.youtube.com/channel/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel ID from a string (URL or ID)
|
||||
* @param {string} url The string to get the ID from
|
||||
* @returns {?string}
|
||||
*/
|
||||
static extractID(url) {
|
||||
return parseURL(url).channel;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Channel;
|
180
node_modules/simple-youtube-api/src/structures/Playlist.js
generated
vendored
Normal file
180
node_modules/simple-youtube-api/src/structures/Playlist.js
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
const { parseURL } = require('../util');
|
||||
const Constants = require('../util/Constants');
|
||||
const Video = require('./Video');
|
||||
const Channel = require('./Channel');
|
||||
|
||||
/** Represents a YouTube playlist */
|
||||
class Playlist {
|
||||
/**
|
||||
* @param {YouTube} youtube The YouTube instance creating this
|
||||
* @param {Object} data The data of the playlist
|
||||
*/
|
||||
constructor(youtube, data) {
|
||||
/**
|
||||
* The YouTube instance that created this
|
||||
* @type {YouTube}
|
||||
*/
|
||||
this.youtube = youtube;
|
||||
Object.defineProperty(this, 'youtube', { enumerable: false });
|
||||
|
||||
/**
|
||||
* The type to filter search results
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = 'playlist';
|
||||
|
||||
/**
|
||||
* Videos in this playlist. Available after calling {@link Playlist#getVideos}.
|
||||
* @type {Array<Video>}
|
||||
*/
|
||||
this.videos = [];
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
if (!data) return;
|
||||
|
||||
this.raw = data;
|
||||
|
||||
/**
|
||||
* The channel this playlist is in
|
||||
* @type {Channel}
|
||||
*/
|
||||
this.channel = new Channel(this.youtube, data);
|
||||
|
||||
/**
|
||||
* This playlist's ID
|
||||
* @type {string}
|
||||
* @name Playlist#id
|
||||
*/
|
||||
|
||||
switch (data.kind) {
|
||||
case Constants.KINDS.SearchResult:
|
||||
if (data.id.kind === Constants.KINDS.Playlist) this.id = data.id.playlistId;
|
||||
else throw new Error('Attempted to make a playlist out of a non-playlist search result.');
|
||||
break;
|
||||
case Constants.KINDS.Playlist:
|
||||
this.id = data.id;
|
||||
break;
|
||||
case Constants.KINDS.PlaylistItem:
|
||||
if (data.snippet) this.id = data.snippet.playlistId;
|
||||
else throw new Error('Attempted to make a playlist out of a resource with no playlist data.');
|
||||
return this; // don't pull extra info from playlist item info
|
||||
default:
|
||||
throw new Error(`Unknown playlist kind: ${data.kind}.`);
|
||||
}
|
||||
|
||||
if (data.snippet) {
|
||||
/**
|
||||
* This playlist's title
|
||||
* @type {?string}
|
||||
*/
|
||||
this.title = data.snippet.title;
|
||||
|
||||
/**
|
||||
* This playlist's description
|
||||
* @type {?string}
|
||||
*/
|
||||
this.description = data.snippet.description;
|
||||
|
||||
/**
|
||||
* The date/time this playlist was published
|
||||
* @type {?Date}
|
||||
*/
|
||||
this.publishedAt = new Date(data.snippet.publishedAt);
|
||||
|
||||
/**
|
||||
* Thumbnails for this playlist
|
||||
* @type {?Object.<string, Thumbnail>}
|
||||
*/
|
||||
this.thumbnails = data.snippet.thumbnails;
|
||||
|
||||
/**
|
||||
* Channel title of this playlist
|
||||
* @type {?string}
|
||||
*/
|
||||
this.channelTitle = data.snippet.channelTitle;
|
||||
|
||||
/**
|
||||
* The language in this playlist's title and description
|
||||
* @type {?string}
|
||||
*/
|
||||
this.defaultLanguage = data.snippet.defaultLanguage;
|
||||
|
||||
/**
|
||||
* Information about the playlist as specified in the `hl` parameter
|
||||
* @type {?{title: string, description: string}}
|
||||
*/
|
||||
this.localized = data.snippet.localized;
|
||||
}
|
||||
|
||||
if (data.status) {
|
||||
/**
|
||||
* The privacy status of this video
|
||||
* @type {string}
|
||||
*/
|
||||
this.privacy = data.status.privacyStatus;
|
||||
}
|
||||
|
||||
if (data.contentDetails) {
|
||||
/**
|
||||
* The total number of videos in this playlist
|
||||
* @type {number}
|
||||
*/
|
||||
this.length = data.contentDetails.itemCount;
|
||||
}
|
||||
|
||||
if (data.player) {
|
||||
/**
|
||||
* A string with an iframe tag for embedding this playlist
|
||||
* @type {string}
|
||||
*/
|
||||
this.embedHTML = data.player.embedHtml;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to this playlist
|
||||
* @type {string}
|
||||
*/
|
||||
get url() {
|
||||
return `https://www.youtube.com/playlist?list=${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the full representation of this playlist.
|
||||
* @param {object} [options] Any extra query params
|
||||
* @returns {Playlist}
|
||||
*/
|
||||
fetch(options) {
|
||||
return this.youtube.request.getPlaylist(this.id, options).then(this._patch.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets videos in the playlist
|
||||
* @param {Number} [limit] Maximum number of videos to obtain. Fetches all if not provided.
|
||||
* @param {Object} [options] Options to retrieve for each video.
|
||||
* @returns {Promise<Video[]>}
|
||||
*/
|
||||
getVideos(limit, options) {
|
||||
return this.youtube.request.getPaginated(
|
||||
Constants.ENDPOINTS.PlaylistItems,
|
||||
limit,
|
||||
Object.assign({ playlistId: this.id, part: Constants.PARTS.PlaylistItems }, options)
|
||||
).then(items => this.videos = items.map(i => new Video(this.youtube, i)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a playlist ID from a string (URL or ID)
|
||||
* @param {string} url The string to get the ID from
|
||||
* @returns {?string}
|
||||
*/
|
||||
static extractID(url) {
|
||||
return parseURL(url).playlist;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Playlist;
|
181
node_modules/simple-youtube-api/src/structures/Video.js
generated
vendored
Normal file
181
node_modules/simple-youtube-api/src/structures/Video.js
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
const duration = require('iso8601-duration');
|
||||
const { parseURL } = require('../util');
|
||||
const Constants = require('../util/Constants');
|
||||
const Channel = require('./Channel');
|
||||
|
||||
/** Represents a YouTube video */
|
||||
class Video {
|
||||
/**
|
||||
* @param {YouTube} youtube The YouTube instance creating this
|
||||
* @param {Object} data The data of the video
|
||||
*/
|
||||
constructor(youtube, data) {
|
||||
/**
|
||||
* The YouTube instance that created this
|
||||
* @type {YouTube}
|
||||
*/
|
||||
this.youtube = youtube;
|
||||
Object.defineProperty(this, 'youtube', { enumerable: false });
|
||||
|
||||
/**
|
||||
* The type to filter search results
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = 'video';
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
if (!data) return;
|
||||
|
||||
/**
|
||||
* The raw data from the YouTube API.
|
||||
* @type {object}
|
||||
*/
|
||||
this.raw = data;
|
||||
|
||||
/**
|
||||
* Whether this is a full (returned from the videos API end point) or partial video (returned
|
||||
* as part of another resource).
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.full = data.kind === Constants.KINDS.Video;
|
||||
|
||||
/**
|
||||
* The resource that this video was created from.
|
||||
* @type {string}
|
||||
*/
|
||||
this.kind = data.kind;
|
||||
|
||||
/**
|
||||
* This video's ID
|
||||
* @type {string}
|
||||
* @name Video#id
|
||||
*/
|
||||
|
||||
switch (data.kind) {
|
||||
case Constants.KINDS.PlaylistItem:
|
||||
if (data.snippet) {
|
||||
if (data.snippet.resourceId.kind === Constants.KINDS.Video) this.id = data.snippet.resourceId.videoId;
|
||||
else throw new Error('Attempted to make a video out of a non-video playlist item.');
|
||||
break;
|
||||
} else {
|
||||
throw new Error('Attempted to make a video out of a playlist item with no video data.');
|
||||
}
|
||||
case Constants.KINDS.Video:
|
||||
this.id = data.id;
|
||||
break;
|
||||
case Constants.KINDS.SearchResult:
|
||||
if (data.id.kind === Constants.KINDS.Video) this.id = data.id.videoId;
|
||||
else throw new Error('Attempted to make a video out of a non-video search result.');
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown video kind: ${data.kind}.`);
|
||||
}
|
||||
|
||||
if (data.snippet) {
|
||||
/**
|
||||
* This video's title
|
||||
* @type {string}
|
||||
*/
|
||||
this.title = data.snippet.title;
|
||||
|
||||
/**
|
||||
* This video's description
|
||||
* @type {string}
|
||||
*/
|
||||
this.description = data.snippet.description;
|
||||
|
||||
/**
|
||||
* The thumbnails of this video.
|
||||
* @type {Object.<'default', 'medium', 'high', 'standard', 'maxres'>}
|
||||
*/
|
||||
this.thumbnails = data.snippet.thumbnails;
|
||||
|
||||
/**
|
||||
* The date/time this video was published
|
||||
* @type {Date}
|
||||
*/
|
||||
this.publishedAt = new Date(data.snippet.publishedAt);
|
||||
|
||||
/**
|
||||
* The channel this video is in.
|
||||
* @type {Channel}
|
||||
*/
|
||||
this.channel = new Channel(this.youtube, data);
|
||||
}
|
||||
|
||||
if(data.contentDetails) {
|
||||
/**
|
||||
* An object containing time period information. All properties are integers, and do not include the lower
|
||||
* precision ones.
|
||||
* @typedef {Object} DurationObject
|
||||
* @property {number} [hours] How many hours the video is long
|
||||
* @property {number} [minutes] How many minutes the video is long
|
||||
* @property {number} [seconds] How many seconds the video is long
|
||||
*/
|
||||
|
||||
/**
|
||||
* The duration of the video
|
||||
* @type {?DurationObject}
|
||||
*/
|
||||
this.duration = data.contentDetails.duration ? duration.parse(data.contentDetails.duration) : null;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maxiumum available resolution thumbnail.
|
||||
* @type {object}
|
||||
*/
|
||||
get maxRes() {
|
||||
const t = this.thumbnails;
|
||||
return t.maxres || t.standard || t.high || t.medium || t.default;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to this video
|
||||
* @type {string}
|
||||
*/
|
||||
get url() {
|
||||
return `https://www.youtube.com/watch?v=${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The short URL to this video
|
||||
* @type {string}
|
||||
*/
|
||||
get shortURL() {
|
||||
return `https://youtu.be/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration of the video in seconds
|
||||
* @type {number}
|
||||
*/
|
||||
get durationSeconds() {
|
||||
return this.duration ? duration.toSeconds(this.duration) : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the full representation of this video.
|
||||
* @param {object} [options] Any extra query params
|
||||
* @returns {Video}
|
||||
*/
|
||||
fetch(options) {
|
||||
return this.youtube.request.getVideo(this.id, options).then(this._patch.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a video ID from a string (URL or ID)
|
||||
* @param {string} url The string to get the ID from
|
||||
* @returns {?string}
|
||||
*/
|
||||
static extractID(url) {
|
||||
return parseURL(url).video;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Video;
|
23
node_modules/simple-youtube-api/src/util/Constants.js
generated
vendored
Normal file
23
node_modules/simple-youtube-api/src/util/Constants.js
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
exports.PARTS = {
|
||||
Search: 'snippet',
|
||||
Videos: 'snippet,contentDetails',
|
||||
Playlists: 'snippet',
|
||||
PlaylistItems: 'snippet,status',
|
||||
Channels: 'snippet'
|
||||
};
|
||||
|
||||
exports.KINDS = {
|
||||
Video: 'youtube#video',
|
||||
PlaylistItem: 'youtube#playlistItem',
|
||||
Playlist: 'youtube#playlist',
|
||||
SearchResult: 'youtube#searchResult',
|
||||
Channel: 'youtube#channel'
|
||||
};
|
||||
|
||||
exports.ENDPOINTS = {
|
||||
PlaylistItems: 'playlistItems',
|
||||
Channels: 'channels',
|
||||
Videos: 'videos',
|
||||
Playlists: 'playlists',
|
||||
Search: 'search'
|
||||
};
|
36
node_modules/simple-youtube-api/src/util/index.js
generated
vendored
Normal file
36
node_modules/simple-youtube-api/src/util/index.js
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
const { parse } = require('url');
|
||||
|
||||
/**
|
||||
* Parse a string as a potential YouTube resource URL.
|
||||
* @param {string} url
|
||||
* @returns {{video: ?string, channel: ?string, playlist: ?string}}
|
||||
*/
|
||||
exports.parseURL = (url) => {
|
||||
const parsed = parse(url, true);
|
||||
switch (parsed.hostname) {
|
||||
case 'www.youtube.com':
|
||||
case 'youtube.com':
|
||||
case 'm.youtube.com': {
|
||||
const idRegex = /^[a-zA-Z0-9-_]+$/;
|
||||
if (parsed.pathname === '/watch') {
|
||||
if (!idRegex.test(parsed.query.v)) return {};
|
||||
const response = { video: parsed.query.v };
|
||||
if (parsed.query.list) response.playlist = parsed.query.list;
|
||||
return response;
|
||||
} else if (parsed.pathname === '/playlist') {
|
||||
if(!idRegex.test(parsed.query.list)) return {};
|
||||
return { playlist: parsed.query.list };
|
||||
} else if (parsed.pathname.startsWith('/channel/')) {
|
||||
const id = parsed.pathname.replace('/channel/', '');
|
||||
if (!idRegex.test(id)) return {};
|
||||
return { channel: id };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
case 'youtu.be':
|
||||
return { video: /^\/[a-zA-Z0-9-_]+$/.test(parsed.pathname) ? parsed.pathname.slice(1) : null };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user