discord-player-youtubei
Advanced tools
Comparing version
@@ -1,10 +0,56 @@ | ||
export { A as AsyncTrackingContext, S as StreamOptions, a as YoutubeiExtractor, Y as YoutubeiOptions } from './Youtube-BN77pcWP.js'; | ||
import { OAuth2Tokens } from 'youtubei.js'; | ||
import { Track, BaseExtractor, ExtractorStreamable, SearchQueryType, ExtractorSearchContext, ExtractorInfo, Playlist, GuildQueueHistory } from 'discord-player'; | ||
import Innertube, { InnerTubeClient, OAuth2Tokens } from 'youtubei.js'; | ||
import { DownloadOptions } from 'youtubei.js/dist/src/types'; | ||
import { Readable } from 'node:stream'; | ||
import { Video } from 'youtubei.js/dist/src/parser/nodes'; | ||
import { AsyncLocalStorage } from 'node:async_hooks'; | ||
import * as youtubei_js_agnostic from 'youtubei.js/agnostic'; | ||
import 'discord-player'; | ||
import 'youtubei.js/dist/src/types'; | ||
import 'node:stream'; | ||
import 'youtubei.js/dist/src/parser/nodes'; | ||
import 'node:async_hooks'; | ||
import { TypedEmitter } from 'tiny-typed-emitter'; | ||
import { VideoInfo } from 'youtubei.js/dist/src/parser/youtube'; | ||
import { YTNode } from 'youtubei.js/dist/src/parser/helpers'; | ||
import { Author } from 'youtubei.js/dist/src/parser/misc'; | ||
import { ChatAction } from 'youtubei.js/dist/src/parser/youtube/LiveChat'; | ||
interface StreamOptions { | ||
useClient?: InnerTubeClient; | ||
} | ||
interface YoutubeiOptions { | ||
authentication?: string; | ||
overrideDownloadOptions?: DownloadOptions; | ||
createStream?: (q: Track, extractor: BaseExtractor<object>) => Promise<string | Readable>; | ||
signOutOnDeactive?: boolean; | ||
streamOptions?: StreamOptions; | ||
overrideBridgeMode?: "ytmusic" | "yt"; | ||
disablePlayer?: boolean; | ||
ignoreSignInErrors?: boolean; | ||
} | ||
interface AsyncTrackingContext { | ||
useClient: InnerTubeClient; | ||
} | ||
declare class YoutubeiExtractor extends BaseExtractor<YoutubeiOptions> { | ||
#private; | ||
static identifier: string; | ||
innerTube: Innertube; | ||
_stream: (q: Track, extractor: BaseExtractor<object>) => Promise<ExtractorStreamable>; | ||
static instance?: YoutubeiExtractor; | ||
priority: number; | ||
static ytContext: AsyncLocalStorage<AsyncTrackingContext>; | ||
static setClientMode(client: InnerTubeClient): void; | ||
static getStreamingContext(): AsyncTrackingContext; | ||
activate(): Promise<void>; | ||
signIn(tokens: string): Promise<void>; | ||
deactivate(): Promise<void>; | ||
validate(query: string, type?: SearchQueryType | null | undefined): Promise<boolean>; | ||
bridge(track: Track, ext: BaseExtractor | null): Promise<ExtractorStreamable | null>; | ||
bridgeFromYTMusic(query: string, track: Track): Promise<ExtractorStreamable | null>; | ||
bridgeFromYT(query: string, track: Track): Promise<ExtractorStreamable | null>; | ||
handle(query: string, context: ExtractorSearchContext): Promise<ExtractorInfo>; | ||
buildTrack(vid: Video, context: ExtractorSearchContext, pl?: Playlist): Track<any>; | ||
stream(info: Track<unknown>): Promise<ExtractorStreamable>; | ||
getRelatedTracks(track: Track<{ | ||
duration_ms: number; | ||
live: boolean; | ||
}>, history: GuildQueueHistory<unknown>): Promise<ExtractorInfo>; | ||
} | ||
declare function objectToToken(tokens: OAuth2Tokens): string; | ||
@@ -15,4 +61,44 @@ declare function tokenToObject(token: string): OAuth2Tokens; | ||
declare class LiveChatAuthor { | ||
username: string; | ||
url: string; | ||
thumbnail: string; | ||
verifiedChannel: boolean; | ||
verifiedArtist: boolean; | ||
isMod: boolean; | ||
id: string; | ||
raw: Author; | ||
constructor(author: Author); | ||
} | ||
declare enum ChatMessageType { | ||
Regular = 1, | ||
Premium = 2, | ||
PremiumSticker = 3 | ||
} | ||
declare class LiveChatMessage { | ||
author: LiveChatAuthor; | ||
type: ChatMessageType; | ||
content?: string; | ||
timestamp: number; | ||
constructor(chatUpdate: YTNode, type: ChatMessageType); | ||
} | ||
declare enum LiveChatEvents { | ||
MessageCreate = "messageCreate" | ||
} | ||
interface LiveChatEventsData { | ||
[LiveChatEvents.MessageCreate]: (message: LiveChatMessage) => void; | ||
} | ||
declare class LiveChat extends TypedEmitter<LiveChatEventsData> { | ||
chat: ReturnType<typeof VideoInfo['prototype']['getLiveChat']>; | ||
chatUpdateHandler: (action: ChatAction) => void; | ||
constructor(chat: ReturnType<typeof VideoInfo['prototype']['getLiveChat']>); | ||
destroy(): void; | ||
} | ||
declare function getLiveChat(videoUrl: string, ext?: YoutubeiExtractor): Promise<LiveChat>; | ||
declare function generateOauthTokens(): Promise<void>; | ||
export { generateOauthTokens, getYoutubeiInstance, objectToToken, tokenToObject }; | ||
export { type AsyncTrackingContext, ChatMessageType, LiveChatEvents, type StreamOptions, YoutubeiExtractor, type YoutubeiOptions, generateOauthTokens, getLiveChat, getYoutubeiInstance, objectToToken, tokenToObject }; |
@@ -33,4 +33,7 @@ "use strict"; | ||
__export(lib_exports, { | ||
ChatMessageType: () => ChatMessageType, | ||
LiveChatEvents: () => LiveChatEvents, | ||
YoutubeiExtractor: () => YoutubeiExtractor, | ||
generateOauthTokens: () => generateOauthTokens, | ||
getLiveChat: () => getLiveChat, | ||
getYoutubeiInstance: () => getYoutubeiInstance, | ||
@@ -41,3 +44,3 @@ objectToToken: () => objectToToken, | ||
module.exports = __toCommonJS(lib_exports); | ||
var import_youtubei3 = __toESM(require("youtubei.js")); | ||
var import_youtubei4 = __toESM(require("youtubei.js")); | ||
@@ -460,2 +463,111 @@ // lib/common/tokenUtils.ts | ||
// lib/utils/live/LiveChat.ts | ||
var import_tiny_typed_emitter = require("tiny-typed-emitter"); | ||
// lib/utils/live/LiveChatAuthor.ts | ||
var LiveChatAuthor = class { | ||
username; | ||
url; | ||
thumbnail; | ||
verifiedChannel; | ||
verifiedArtist; | ||
isMod; | ||
id; | ||
raw; | ||
constructor(author) { | ||
this.username = author.name; | ||
this.url = author.url; | ||
this.thumbnail = author.best_thumbnail?.url ?? author.thumbnails[0].url; | ||
this.verifiedChannel = author.is_verified || false; | ||
this.verifiedArtist = author.is_verified_artist || false; | ||
this.isMod = author.is_moderator || false; | ||
this.id = author.id; | ||
this.raw = author; | ||
} | ||
}; | ||
// lib/utils/live/LiveChatMessage.ts | ||
var ChatMessageType = /* @__PURE__ */ ((ChatMessageType2) => { | ||
ChatMessageType2[ChatMessageType2["Regular"] = 1] = "Regular"; | ||
ChatMessageType2[ChatMessageType2["Premium"] = 2] = "Premium"; | ||
ChatMessageType2[ChatMessageType2["PremiumSticker"] = 3] = "PremiumSticker"; | ||
return ChatMessageType2; | ||
})(ChatMessageType || {}); | ||
var LiveChatMessage = class { | ||
author; | ||
type; | ||
content; | ||
timestamp; | ||
constructor(chatUpdate, type) { | ||
this.author = new LiveChatAuthor(chatUpdate.author); | ||
this.type = type; | ||
this.timestamp = chatUpdate.timestamp || Date.now(); | ||
if (chatUpdate.type === "LiveChatTextMessage" || chatUpdate.type === "LiveChatPaidMessage") { | ||
this.content = chatUpdate.message.toString(); | ||
} | ||
} | ||
}; | ||
// lib/utils/live/LiveChat.ts | ||
var import_youtubei3 = require("youtubei.js"); | ||
var LiveChatEvents = /* @__PURE__ */ ((LiveChatEvents2) => { | ||
LiveChatEvents2["MessageCreate"] = "messageCreate"; | ||
return LiveChatEvents2; | ||
})(LiveChatEvents || {}); | ||
var LiveChat = class extends import_tiny_typed_emitter.TypedEmitter { | ||
chat; | ||
// this is scuffed but i cant access 'this' inside other non-arrow functions | ||
chatUpdateHandler = (action) => { | ||
if (action.is(import_youtubei3.YTNodes.AddChatItemAction)) { | ||
const { item } = action.as(import_youtubei3.YTNodes.AddChatItemAction); | ||
switch (item.type) { | ||
case "LiveChatTextMessage": { | ||
this.emit("messageCreate" /* MessageCreate */, new LiveChatMessage(item, 1 /* Regular */)); | ||
break; | ||
} | ||
case "LiveChatPaidMessage": { | ||
this.emit("messageCreate" /* MessageCreate */, new LiveChatMessage(item, 2 /* Premium */)); | ||
break; | ||
} | ||
case "LiveChatPaidSticker": { | ||
this.emit("messageCreate" /* MessageCreate */, new LiveChatMessage(item, 3 /* PremiumSticker */)); | ||
break; | ||
} | ||
default: { | ||
break; | ||
} | ||
} | ||
} | ||
}; | ||
constructor(chat) { | ||
super(); | ||
chat.on("chat-update", this.chatUpdateHandler); | ||
this.chat = chat; | ||
} | ||
destroy() { | ||
this.chat.off("chat-update", this.chatUpdateHandler); | ||
this.chat.stop(); | ||
} | ||
}; | ||
// lib/utils/live/getLiveChat.ts | ||
var YOUTUBE_URL_REGEX = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(?:-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|live\/|v\/)?)([\w\-]+)(\S+)?$/; | ||
function parseYoutubeVideo(videoUrl) { | ||
if (!YOUTUBE_URL_REGEX.test(videoUrl)) throw new Error("This is not a valid video URL"); | ||
const idExtractor = new URL(videoUrl); | ||
let id = idExtractor.searchParams.get("v"); | ||
if (!id) id = videoUrl.split("/").at(-1)?.split("?").at(0); | ||
return id; | ||
} | ||
async function getLiveChat(videoUrl, ext) { | ||
const instance = YoutubeiExtractor.instance ?? ext; | ||
if (!instance) throw new Error("Invoked getLiveChat before player.extractors.register"); | ||
const innertube = instance.innerTube; | ||
const videoId = parseYoutubeVideo(videoUrl); | ||
const info = await innertube.getInfo(videoId); | ||
const chat = info.getLiveChat(); | ||
chat.start(); | ||
return new LiveChat(chat); | ||
} | ||
// lib/index.ts | ||
@@ -470,3 +582,3 @@ var exit = (message, clean) => { | ||
async function generateOauthTokens() { | ||
const youtube = await import_youtubei3.default.create(); | ||
const youtube = await import_youtubei4.default.create(); | ||
youtube.session.on("auth-pending", (data) => { | ||
@@ -490,4 +602,7 @@ const { verification_url: verify, user_code } = data; | ||
0 && (module.exports = { | ||
ChatMessageType, | ||
LiveChatEvents, | ||
YoutubeiExtractor, | ||
generateOauthTokens, | ||
getLiveChat, | ||
getYoutubeiInstance, | ||
@@ -494,0 +609,0 @@ objectToToken, |
# Getting the live chat of a live video | ||
discord-player-youtubei exports a `utils` module that is used for all sorts of stuff. Live chat is one of them | ||
discord-player-youtubei random functions that is used for all sorts of stuff. Live chat is one of them. | ||
```ts | ||
import { getLiveChat, LiveChatEvents, ChatMessageType } from "discord-player-youtubei/utils" | ||
import { getLiveChat, LiveChatEvents, ChatMessageType } from "discord-player-youtubei" | ||
@@ -17,4 +17,4 @@ const chat = await getLiveChat("videourl") // must be live video. or else it will throw an error | ||
As you can see, the live chat API tries to act closely to discord.js' event systems to implement easy access to its users | ||
As you can see, the live chat API tries to act closely to discord.js' event systems to implement easy access to its users. | ||
If you finally want to stop listening to chat events, you can implement `chat.destroy()`. |
{ | ||
"name": "discord-player-youtubei", | ||
"version": "1.2.5-beta.7", | ||
"version": "1.2.5-beta.8", | ||
"description": "An unofficial package to test the use of youtubei in discord-player v6.", | ||
@@ -26,8 +26,3 @@ "main": "dist/index.js", | ||
"bin": "./bin/index.js", | ||
"packageManager": "yarn@4.3.1", | ||
"exports": { | ||
"utils": { | ||
"default": "./dist/utils/index.js" | ||
} | ||
} | ||
"packageManager": "yarn@4.3.1" | ||
} |
2
-33.33%41070
-31.37%7
-30%689
-39.83%