structures/Channel.js

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;