1
0
mirror of https://github.com/musix-org/musix-oss synced 2025-06-16 12:36:01 +00:00
This commit is contained in:
MatteZ02
2019-05-30 12:06:47 +03:00
parent cbdffcf19c
commit 5eb0264906
2502 changed files with 360854 additions and 0 deletions

21
node_modules/simple-youtube-api/LICENSE generated vendored Normal file
View 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
View File

@ -0,0 +1,14 @@
# Simple YouTube API
[![Module support server](https://discordapp.com/api/guilds/430216837276368897/embed.png)](https://discord.gg/A97Qftr)
[![Build Status](https://travis-ci.org/HyperCoder2975/simple-youtube-api.svg?branch=master)](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

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

File diff suppressed because one or more lines are too long

13
node_modules/simple-youtube-api/examples/playlist.js generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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;

View 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;

View 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
View 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
View 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
View 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 {};
}
};