fixed channel icon issues

This commit is contained in:
killer069 2021-11-22 13:13:00 +05:30
parent c0d3dc68bf
commit 283d864183
5 changed files with 135 additions and 44 deletions

View File

@ -41,9 +41,9 @@ export class YouTubeChannel {
*/
url?: string;
/**
* YouTube Channel Icon data.
* YouTube Channel Icons data.
*/
icon?: ChannelIconInterface;
icons?: ChannelIconInterface[];
/**
* YouTube Channel subscribers count.
*/
@ -60,7 +60,7 @@ export class YouTubeChannel {
this.artist = !!data.artist || false;
this.id = data.id || null;
this.url = data.url || null;
this.icon = data.icon || { url: null, width: 0, height: 0 };
this.icons = data.icon || [{ url: null, width: 0, height: 0 }];
this.subscribers = data.subscribers || null;
}
@ -71,9 +71,9 @@ export class YouTubeChannel {
*/
iconURL(options = { size: 0 }): string | undefined {
if (typeof options.size !== 'number' || options.size < 0) throw new Error('invalid icon size');
if (!this.icon?.url) return undefined;
const def = this.icon.url.split('=s')[1].split('-c')[0];
return this.icon.url.replace(`=s${def}-c`, `=s${options.size}-c`);
if (!this.icons?.[0]?.url) return undefined;
const def = this.icons?.[0]?.url.split('=s')[1].split('-c')[0];
return this.icons?.[0]?.url.replace(`=s${def}-c`, `=s${options.size}-c`);
}
/**
* Converts Channel Class to channel name.
@ -93,7 +93,7 @@ export class YouTubeChannel {
artist: this.artist,
id: this.id,
url: this.url,
icon: this.icon,
icons: this.icons,
type: this.type,
subscribers: this.subscribers
};
@ -128,7 +128,7 @@ interface ChannelJSON{
/**
* YouTube Channel Icon data.
*/
icon?: ChannelIconInterface;
icons?: ChannelIconInterface[];
/**
* YouTube Channel subscribers count.
*/

View File

@ -46,12 +46,23 @@ export function setCookieToken(options: { cookie: string }) {
youtubeData = { cookie };
youtubeData.file = false;
}
export function cookieHeaders(headCookie: string[]) {
/**
* Updates cookies locally either in file or in memory.
*
* Example
* ```ts
* const response = ... // Any https package get function.
*
* play.cookieHeaders(response.headers['set-cookie'])
* ```
* @param headCookie response headers['set-cookie'] array
* @returns Nothing
*/
export function cookieHeaders(headCookie: string[]): void {
if (!youtubeData?.cookie) return;
headCookie.forEach((x: string) => {
x.split(';').forEach((x) => {
const arr = x.split('=');
x.split(';').forEach((z) => {
const arr = z.split('=');
if (arr.length <= 1) return;
const key = arr.shift()?.trim() as string;
const value = arr.join('=').trim();

View File

@ -18,13 +18,25 @@ const video_id_pattern = /^[a-zA-Z\d_-]{11,12}$/;
const playlist_id_pattern = /^(PL|UU|LL|RD|OL)[a-zA-Z\d_-]{16,41}$/;
const DEFAULT_API_KEY = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8';
const video_pattern =
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/;
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|shorts\/|embed\/|v\/)?)([\w\-]+)(\S+)?$/;
const playlist_pattern =
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?(youtube\.com)\/(?:(playlist|watch))(.*)?((\?|\&)list=)(PL|UU|LL|RD|OL)[a-zA-Z\d_-]{16,41}(.*)?$/;
/**
* Command to validate a YouTube url
* @param url Url for validation
* @returns type of url or false.
* Validate YouTube URL or ID.
*
* **CAUTION :** If your search word is 11-12 long, you might get it validated as video ID.
*
* To avoid above, add one more condition to yt_validate
* ```ts
* if (url.startsWith('https') && yt_validate(url) === 'video') {
* // YouTube Video Url.
* }
* ```
* @param url YouTube URL OR ID
* @returns
* ```
* 'playlist' | 'video' | 'search' | false
* ```
*/
export function yt_validate(url: string): 'playlist' | 'video' | 'search' | false {
if (url.indexOf('list=') === -1) {
@ -51,7 +63,7 @@ export function yt_validate(url: string): 'playlist' | 'video' | 'search' | fals
}
}
/**
* Function to extract ID of YouTube url.
* Extract ID of YouTube url.
* @param url ID or url of YouTube
* @returns ID of video or playlist.
*/
@ -75,9 +87,32 @@ export function extractID(url: string): string {
}
/**
* Basic function to get data from a YouTube url or ID.
* @param url YouTube url or ID
* @param options cookie and proxy parameters to add
* @returns Data containing video_details, LiveStreamData and formats of video url.
*
* Example
* ```ts
* const video = await play.video_basic_info('youtube video url')
*
* const res = ... // Any https package get function.
* const video = await play.video_basic_info(res.body, { htmldata : true })
*
* const video = await play.video_basic_info('youtube video url', { proxy : [{
host : "IP or hostname",
port : 8080,
authentication: {
username: 'username';
password: 'very secret';
}
}] }) // Authentication is optional.
// OR
const video = await play.video_basic_info('youtube video url', { proxy : ['url'] })
* ```
* @param url YouTube url or ID or html body data
* @param options Video Info Options
* - `Proxy[]` proxy : sends data through a proxy
* - `boolean` htmldata : given data is html data or not
* @returns Video Basic Info {@link InfoData}.
*/
export async function video_basic_info(url: string, options: InfoOptions = {}) : Promise<InfoData> {
let body: string;
@ -112,11 +147,11 @@ export async function video_basic_info(url: string, options: InfoOptions = {}) :
player_response.playabilityStatus.errorScreen.playerKavRenderer?.reason.simpleText
}`
);
const ownerInfo = initial_response.contents.twoColumnWatchNextResults.results?.results?.contents[1]?.videoSecondaryInfoRenderer
?.owner?.videoOwnerRenderer
const badge =
initial_response.contents.twoColumnWatchNextResults.results?.results?.contents[1]?.videoSecondaryInfoRenderer
?.owner?.videoOwnerRenderer?.badges &&
initial_response.contents.twoColumnWatchNextResults.results?.results?.contents[1]?.videoSecondaryInfoRenderer
?.owner?.videoOwnerRenderer?.badges[0];
ownerInfo?.badges &&
ownerInfo?.badges[0];
const html5player = `https://www.youtube.com${body.split('"jsUrl":"')[1].split('"')[0]}`;
const related: string[] = [];
initial_response.contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results.forEach(
@ -142,7 +177,8 @@ export async function video_basic_info(url: string, options: InfoOptions = {}) :
id: vid.channelId,
url: `https://www.youtube.com/channel/${vid.channelId}`,
verified: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('verified')),
artist: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('artist'))
artist: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('artist')),
icons : ownerInfo?.thumbnail?.thumbnails || undefined
},
views: vid.viewCount,
tags: vid.keywords,
@ -182,14 +218,40 @@ function parseSeconds(seconds: number): string {
return hDisplay + mDisplay + sDisplay;
}
/**
* Function which gets data from video_basic_info and deciphers it if it contains signatures.
* @param url YouTube Video URL
* @param options cookie and proxy parameters to add
* @returns Data containing video_details, LiveStreamData and formats of video url.
* Gets data from YouTube url or ID or html body data and deciphers it.
* ```
* video_basic_info + decipher_info = video_info
* ```
*
* Example
* ```ts
* const video = await play.video_info('youtube video url')
*
* const res = ... // Any https package get function.
* const video = await play.video_info(res.body, { htmldata : true })
*
* const video = await play.video_info('youtube video url', { proxy : [{
host : "IP or hostname",
port : 8080,
authentication: {
username: 'username';
password: 'very secret';
}
}] }) // Authentication is optional.
// OR
const video = await play.video_info('youtube video url', { proxy : ['url'] })
* ```
* @param url YouTube url or ID or html body data
* @param options Video Info Options
* - `Proxy[]` proxy : sends data through a proxy
* - `boolean` htmldata : given data is html data or not
* @returns Deciphered Video Info {@link InfoData}.
*/
export async function video_info(url: string, options: InfoOptions = {}): Promise<InfoData> {
const data = await video_basic_info(url, options);
if (data.LiveStreamData.isLive === true && data.LiveStreamData.hlsManifestUrl !== null) {
if (data.LiveStreamData.isLive === true && data.LiveStreamData.dashManifestUrl !== null) {
return data;
} else if (data.format[0].signatureCipher || data.format[0].cipher) {
data.format = await format_decipher(data.format, data.html5player);
@ -200,11 +262,11 @@ export async function video_info(url: string, options: InfoOptions = {}): Promis
}
/**
* Function uses data from video_basic_info and deciphers it if it contains signatures.
* @param data basic_video_info data
* @returns Data containing video_details, LiveStreamData and formats of video url.
* @param data Data - {@link InfoData}
* @returns Deciphered Video Info {@link InfoData}
*/
export async function decipher_info(data: InfoData) {
if (data.LiveStreamData.isLive === true && data.LiveStreamData.hlsManifestUrl !== null) {
if (data.LiveStreamData.isLive === true && data.LiveStreamData.dashManifestUrl !== null) {
return data;
} else if (data.format[0].signatureCipher || data.format[0].cipher) {
data.format = await format_decipher(data.format, data.html5player);
@ -214,9 +276,32 @@ export async function decipher_info(data: InfoData) {
}
}
/**
* Function to get YouTube playlist info from a playlist url.
* Gets YouTube playlist info from a playlist url.
*
* Example
* ```ts
* const playlist = await play.playlist_info('youtube playlist url')
*
* const playlist = await play.playlist_info('youtube playlist url', { incomplete : true })
*
* const playlist = await play.playlist_info('youtube playlist url', { proxy : [{
host : "IP or hostname",
port : 8080,
authentication: {
username: 'username';
password: 'very secret';
}
}] }) // Authentication is optional.
// OR
const playlist = await play.playlist_info('youtube playlist url', { proxy : ['url'] })
* ```
* @param url Playlist URL
* @param options incomplete and proxy to add.
* @param options Playlist Info Options
* - `boolean` incomplete : If set to true, parses playlist with hidden videos.
* - `Proxy[]` proxy : sends data through a proxy
*
* @returns YouTube Playlist
*/
export async function playlist_info(url: string, options: PlaylistOptions = {}): Promise<YouTubePlayList> {

View File

@ -134,14 +134,8 @@ export function parseVideo(data?: any): YouTubeVideo {
data.videoRenderer.ownerText.runs[0].navigationEndpoint.browseEndpoint.canonicalBaseUrl ||
data.videoRenderer.ownerText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url
}`,
icon: {
url: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
.thumbnails[0].url,
width: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
.thumbnails[0].width,
height: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
.thumbnails[0].height
},
icons : data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
.thumbnails,
verified: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('verified')),
artist: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('artist'))
},

View File

@ -91,7 +91,6 @@ export async function stream(url: string, options: StreamOptions = {}): Promise<
* @returns Array of YouTube or Spotify or SoundCloud or Deezer
deezer?: 'track' | 'playlist' | 'album';
*/
export async function search( query: string, options: { source : { deezer : "album" } } & SearchOptions) : Promise<DeezerAlbum[]>;
export async function search( query: string, options: { source : { deezer : "playlist" } } & SearchOptions) : Promise<DeezerPlaylist[]>;
export async function search( query: string, options: { source : { deezer : "track" } } & SearchOptions) : Promise<DeezerTrack[]>;
@ -104,6 +103,8 @@ export async function search( query: string, options: { source : { spotify : "tr
export async function search( query: string, options: { source : { youtube : "channel" } } & SearchOptions) : Promise<YouTubeChannel[]>;
export async function search( query: string, options: { source : { youtube : "playlist" } } & SearchOptions) : Promise<YouTubePlayList[]>;
export async function search( query: string, options: { source : { youtube : "video" } } & SearchOptions) : Promise<YouTubeVideo[]>;
export async function search( query: string, options: { limit : number } & SearchOptions ) : Promise<YouTubeVideo[]>;
export async function search( query: string, options? : SearchOptions) : Promise<YouTubeVideo[]>;
export async function search(
query: string,
options: SearchOptions = {}