@elizaos/plugin-music 2.0.0-beta.1 → 2.0.11-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils/playbackTransportIntent.ts","../src/actions/confirmation.ts","../src/actions/manageRouting.ts","../src/actions/manageZones.ts","../src/actions/downloadMusic.ts","../src/utils/smartFetchService.ts","../src/actions/playlistOp.ts","../src/components/playlists.ts","../src/components/componentData.ts","../src/components/storageContext.ts","../src/actions/playMusicQuery.ts","../src/utils/json.ts","../src/actions/searchYouTube.ts","../src/actions/musicLibrary.ts","../src/actions/playAudio.ts","../src/discordVoice.ts","../src/service.ts","../src/core/broadcast.ts","../src/core/streamCore.ts","../src/utils/opusBroadcastNormalize.ts","../src/utils/ffmpegEnv.ts","../src/utils/musicDebug.ts","../src/core/streamMultiplexer.ts","../src/queue.ts","../src/utils/streamFallback.ts","../src/utils/ytdlpFallback.ts","../src/utils/ytdlpCheck.ts","../src/utils/ytdlpCli.ts","../src/utils/ytdlpYoutube.ts","../src/router/audioRouter.ts","../src/router/mixSessionManager.ts","../src/router/zoneManager.ts","../src/services/audioCache.ts","../src/utils/progressiveMessage.ts","../src/actions/music-player-action-docs.ts","../src/actions/playbackOp.ts","../src/services/smartMusicFetch.ts","../src/utils/resolveMusicGuildId.ts","../src/actions/music.ts","../src/providers/musicInfoProvider.ts","../src/providers/musicLibraryProvider.ts","../src/components/musicLibrary.ts","../src/providers/musicPlaylistsProvider.ts","../src/providers/musicQueueProvider.ts","../src/providers/wikipediaProvider.ts","../src/routes.ts","../src/search-category.ts","../src/services/musicLibraryService.ts","../src/components/analytics.ts","../src/components/djTips.ts","../src/components/preferences.ts","../src/components/repetitionControl.ts","../src/components/songMemory.ts","../src/services/musicEntityDetectionService.ts","../src/services/musicInfoService.ts","../src/services/geniusClient.ts","../src/utils/retry.ts","../src/services/lastFmClient.ts","../src/services/musicBrainzClient.ts","../src/services/theAudioDbClient.ts","../src/services/spotifyClient.ts","../src/services/wikipediaClient.ts","../src/services/wikipediaExtractionService.ts","../src/services/youtubeSearch.ts","../src/components/djGuildSettings.ts","../src/components/djIntroOptions.ts","../src/route-fallback.ts","../src/services/musicStorage.ts"],"sourcesContent":["import {\n type IAgentRuntime,\n logger,\n type Plugin,\n promoteSubactionsToActions,\n} from \"@elizaos/core\";\nimport { musicAction } from \"./actions/music\";\nimport { musicInfoProvider } from \"./providers/musicInfoProvider\";\nimport musicLibraryProvider from \"./providers/musicLibraryProvider\";\nimport musicPlaylistsProvider from \"./providers/musicPlaylistsProvider\";\nimport { musicQueueProvider } from \"./providers/musicQueueProvider\";\nimport { wikipediaProvider } from \"./providers/wikipediaProvider\";\nimport { musicPlayerRoutes } from \"./routes\";\nimport { registerMusicLibrarySearchCategories } from \"./search-category\";\nimport { MusicService } from \"./service\";\nimport { MusicLibraryService } from \"./services/musicLibraryService\";\n\nexport { musicAction } from \"./actions/music\";\nexport type { DJAnalytics } from \"./components/analytics\";\nexport * from \"./components/analytics\";\nexport { trackListenerSnapshot } from \"./components/analytics\";\nexport * from \"./components/djGuildSettings\";\nexport {\n DEFAULT_GUILD_SETTINGS,\n getDJGuildSettings,\n resetDJGuildSettings,\n setAutonomyLevel,\n setDJGuildSettings,\n toggleDJ,\n} from \"./components/djGuildSettings\";\nexport * from \"./components/djIntroOptions\";\nexport {\n buildIntroPrompt,\n DEFAULT_DJ_INTRO_OPTIONS,\n getDJIntroOptions,\n resetDJIntroOptions,\n setDJIntroOptions,\n} from \"./components/djIntroOptions\";\nexport * from \"./components/djTips\";\nexport {\n getDJTipStats,\n getRecentTips,\n getTopTippers,\n trackDJTip,\n} from \"./components/djTips\";\nexport type { LibrarySong } from \"./components/musicLibrary\";\nexport * from \"./components/musicLibrary\";\nexport type { Playlist } from \"./components/playlists\";\nexport * from \"./components/playlists\";\nexport type { UserMusicPreferences } from \"./components/preferences\";\nexport * from \"./components/preferences\";\nexport { repetitionControl } from \"./components/repetitionControl\";\nexport * from \"./components/songMemory\";\nexport {\n getMostRequestedSongs,\n getSongMemory,\n getTopSongs,\n recordSongDedication,\n recordSongPlay,\n recordSongRequest,\n} from \"./components/songMemory\";\nexport type {\n AudioSubscription,\n BroadcastState,\n BroadcastTrackMetadata,\n IAudioBroadcast,\n} from \"./contracts\";\nexport { Broadcast } from \"./core\";\nexport type { CrossFadeOptions, QueuedTrack } from \"./queue\";\nexport { tryHandleMusicPlayerStatusFallback } from \"./route-fallback\";\nexport {\n type AudioRouteConfig,\n AudioRouter,\n type AudioRoutingMode,\n type MixConfig,\n type MixSession,\n MixSessionManager,\n type Zone,\n ZoneManager,\n} from \"./router\";\nexport { MusicService } from \"./service\";\nexport type { DetectedMusicEntity } from \"./services/musicEntityDetectionService\";\nexport {\n MusicEntityDetectionHelper,\n MusicEntityDetectionService,\n} from \"./services/musicEntityDetectionService\";\nexport { MusicInfoHelper, MusicInfoService } from \"./services/musicInfoService\";\nexport { MusicLibraryService } from \"./services/musicLibraryService\";\nexport { MusicStorageService, type StoredTrack } from \"./services/musicStorage\";\nexport type {\n MusicInfoServiceStatus,\n ServiceHealth,\n ServiceStatus,\n} from \"./services/serviceStatus\";\nexport type {\n FetchProgress,\n FetchResult,\n SmartFetchOptions,\n} from \"./services/smartMusicFetch\";\nexport { SmartMusicFetchService } from \"./services/smartMusicFetch\";\nexport { SpotifyClient } from \"./services/spotifyClient\";\nexport { WikipediaClient, WikipediaService } from \"./services/wikipediaClient\";\nexport type {\n ExtractedMusicInfo,\n WikipediaExtractionContext,\n} from \"./services/wikipediaExtractionService\";\nexport {\n WikipediaExtractionHelper,\n WikipediaExtractionService,\n} from \"./services/wikipediaExtractionService\";\nexport type { YouTubeSearchResult } from \"./services/youtubeSearch\";\nexport {\n YouTubeSearchHelper,\n YouTubeSearchService,\n} from \"./services/youtubeSearch\";\nexport type {\n AlbumInfo,\n ArtistInfo,\n MusicInfoResult,\n TrackInfo,\n} from \"./types\";\nexport type {\n AudioFeatureSeed,\n AudioFeatures,\n RecommendationRequest,\n TrackRecommendation,\n} from \"./types/audioFeatures\";\n\ninterface DiscordMusicBridgeService {\n clientReadyPromise?: Promise<void> | null;\n voiceManager?: Parameters<MusicService[\"setVoiceManager\"]>[0];\n}\n\nconst musicPlugin: Plugin = {\n name: \"music\",\n description:\n \"Music library, discovery, playlists, analytics, playback engine, queue, routing API, and streaming routes — unified under the MUSIC action.\",\n services: [MusicLibraryService, MusicService],\n actions: [...promoteSubactionsToActions(musicAction)],\n providers: [\n musicInfoProvider,\n wikipediaProvider,\n musicLibraryProvider,\n musicPlaylistsProvider,\n musicQueueProvider,\n ],\n routes: musicPlayerRoutes,\n autoEnable: {\n envKeys: [\n \"LASTFM_API_KEY\",\n \"GENIUS_API_KEY\",\n \"THEAUDIODB_API_KEY\",\n \"SPOTIFY_CLIENT_ID\",\n \"SPOTIFY_CLIENT_SECRET\",\n ],\n },\n init: async (_config: Record<string, string>, runtime: IAgentRuntime) => {\n registerMusicLibrarySearchCategories(runtime);\n logger.debug(\n \"Music plugin initialized (library + playback); Discord wiring deferred\",\n );\n\n runtime\n .getServiceLoadPromise(\"discord\")\n .then(async (service) => {\n const discordService = service as DiscordMusicBridgeService | null;\n if (!discordService) {\n logger.warn(\n \"Discord service not found — music playback runs web-only\",\n );\n return;\n }\n\n if (discordService.clientReadyPromise) {\n logger.debug(\"Music plugin waiting for Discord client...\");\n await discordService.clientReadyPromise;\n }\n\n runtime\n .getServiceLoadPromise(\"music\")\n .then((svc) => {\n const musicService = svc as MusicService | null;\n if (!musicService) {\n logger.warn(\"Music service not available after load\");\n return;\n }\n const voiceManager = discordService.voiceManager;\n if (voiceManager) {\n musicService.setVoiceManager(voiceManager);\n logger.debug(\"Music service wired to Discord voice manager\");\n } else {\n logger.warn(\n \"Discord voice manager unavailable — music playback web-only\",\n );\n }\n })\n .catch((error: unknown) => {\n logger.error(`Music service Discord wiring failed: ${error}`);\n });\n })\n .catch((error: unknown) => {\n logger.warn(`Discord unavailable — music playback web-only: ${error}`);\n });\n },\n};\n\nexport default musicPlugin;\n","/**\n * True when the user message is only playback transport (pause/skip/stop/resume),\n * not a request to play new audio. Used so PLAY_AUDIO validate returns false and\n * the runtime picks PLAYBACK_OP (op=pause|resume|skip|stop) instead.\n */\nexport function isPlaybackTransportControlOnlyMessage(text: string): boolean {\n const raw = (text || \"\").trim().toLowerCase();\n if (!raw) return false;\n if (/https?:\\/\\//.test(raw)) return false;\n if (/\\byoutube\\.|youtu\\.be|spotify\\.|soundcloud\\./.test(raw)) return false;\n if (/\\bplay\\s+/.test(raw) && !/^(don't|do not|never)\\s+play\\b/.test(raw)) {\n return false;\n }\n\n const t = raw.replace(/[.!?…]+$/g, \"\").trim();\n\n const patterns: RegExp[] = [\n /^(pause|pause\\s+it|pause\\s+the\\s+music|pause\\s+playback)(\\s+please)?$/,\n /^(please\\s+)?pause(\\s+the\\s+music|\\s+it)?$/,\n /^(can you|could you)\\s+pause(\\s+the\\s+music|\\s+it)?\\??$/,\n /^(hold|mute)\\s+(the\\s+)?music$/,\n /^(resume|unpause)(\\s+playback|\\s+the\\s+music|\\s+playing)?(\\s+please)?$/,\n /^(please\\s+)?(resume|unpause)$/,\n /^(can you|could you)\\s+(resume|unpause)(\\s+the\\s+music)?\\??$/,\n /^continue(\\s+the\\s+music|\\s+playing)?(\\s+please)?$/,\n /^(skip|skip\\s+it|skip\\s+this|next(\\s+track|\\s+song)?)(\\s+please)?$/,\n /^(please\\s+)?skip(\\s+this|\\s+it)?$/,\n /^(can you|could you)\\s+skip(\\s+this)?\\??$/,\n /^(stop\\s+the\\s+music|stop\\s+playing|stop\\s+playback)(\\s+please)?$/,\n /^(please\\s+)?stop\\s+the\\s+music$/,\n /^(can you|could you)\\s+stop\\s+(the\\s+music|playing)\\??$/,\n ];\n\n if (patterns.some((p) => p.test(t))) {\n return true;\n }\n\n // Short, single-intent phrases the model often paraphrases (not matched above).\n const loose = raw.replace(/[.!?…]+$/g, \"\").trim();\n if (loose.length > 160) return false;\n if (/\\bplay\\s+[a-z0-9]/i.test(loose)) return false;\n const wantsStop =\n /\\bstop\\s+(the\\s+)?music\\b/.test(loose) || /\\bstop\\s+playing\\b/.test(loose);\n const wantsResume =\n /\\b(unpause|resume)\\b/.test(loose) ||\n /\\bcontinue\\s+(the\\s+)?(music|playing)\\b/.test(loose);\n const wantsSkip =\n /\\bskip\\b/.test(loose) || /\\bnext\\s+(track|song)\\b/.test(loose);\n const wantsPause =\n /\\bpause(d|ing)?\\b/.test(loose) ||\n /\\bhold\\s+(the\\s+)?music\\b/.test(loose) ||\n /\\bmute\\s+(the\\s+)?music\\b/.test(loose);\n const n = [wantsStop, wantsResume, wantsSkip, wantsPause].filter(\n Boolean,\n ).length;\n return n === 1;\n}\n\nexport type PlaybackTransportKind = \"pause\" | \"resume\" | \"skip\" | \"stop\";\n\n/**\n * Which dedicated music action should run (PLAY_AUDIO must not be used).\n */\nexport function classifyPlaybackTransportIntent(\n text: string,\n): PlaybackTransportKind | null {\n if (!isPlaybackTransportControlOnlyMessage(text)) return null;\n const t = text\n .trim()\n .toLowerCase()\n .replace(/[.!?…]+$/g, \"\")\n .trim();\n\n if (\n /\\bstop\\s+(the\\s+)?music\\b/.test(t) ||\n /\\bstop\\s+playing\\b/.test(t) ||\n /\\bstop\\s+playback\\b/.test(t)\n ) {\n return \"stop\";\n }\n if (\n /\\b(unpause|resume)\\b/.test(t) ||\n /\\bcontinue\\s+(the\\s+)?(music|playing)\\b/.test(t)\n ) {\n return \"resume\";\n }\n if (/\\bskip\\b/.test(t) || /\\bnext\\s+(track|song)\\b/.test(t)) {\n return \"skip\";\n }\n return \"pause\";\n}\n","import type { ActionResult } from \"@elizaos/core\";\n\ntype OptionsRecord = Record<string, unknown>;\n\nexport function mergedOptions(options?: OptionsRecord): OptionsRecord {\n const direct = options ?? {};\n const parameters =\n direct.parameters && typeof direct.parameters === \"object\"\n ? (direct.parameters as OptionsRecord)\n : {};\n return { ...direct, ...parameters };\n}\n\nexport function isConfirmed(options?: OptionsRecord): boolean {\n const raw = mergedOptions(options).confirmed;\n return raw === true || raw === \"true\";\n}\n\nexport function confirmationRequired(\n preview: string,\n data: OptionsRecord,\n): ActionResult {\n return {\n success: false,\n text: preview,\n data: { requiresConfirmation: true, preview, ...data },\n };\n}\n","import type {\n Action,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n Service,\n State,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport type { AudioRouter, AudioRoutingMode, ZoneManager } from \"../router\";\n\ninterface MusicRoutingService extends Service {\n capabilityDescription: string;\n stop(): Promise<void>;\n getAudioRouter(): AudioRouter;\n getZoneManager(): ZoneManager;\n setRoutingMode(mode: AudioRoutingMode): void;\n getRoutingMode(): AudioRoutingMode;\n listRoutingTargets(): string[];\n startBroadcastRoute(\n sourceId: string,\n targetIds: string[],\n mode?: AudioRoutingMode,\n ): Promise<{\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n }>;\n stopBroadcastRoute(sourceId: string): Promise<void>;\n getRoutingStatus(): {\n mode: AudioRoutingMode;\n activeRoutes: Array<{\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n }>;\n registeredTargets: string[];\n zoneCount: number;\n };\n}\n\nconst ROUTING_CONTEXTS = [\"media\", \"automation\", \"settings\"] as const;\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nasync function emit(\n callback: HandlerCallback | undefined,\n source: string,\n text: string,\n success: boolean,\n data?: Record<string, unknown>,\n): Promise<ActionResult> {\n await callback?.({ text, source });\n return {\n success,\n text,\n values: { success, ...(data ?? {}) },\n data: { actionName: \"MANAGE_ROUTING\", ...(data ?? {}) },\n };\n}\n\nfunction readParams(options: unknown): Record<string, unknown> {\n const direct =\n options && typeof options === \"object\"\n ? (options as Record<string, unknown>)\n : {};\n const parameters =\n direct.parameters && typeof direct.parameters === \"object\"\n ? (direct.parameters as Record<string, unknown>)\n : {};\n return { ...direct, ...parameters };\n}\n\nfunction routingTextFromOptions(options: unknown): string | null {\n const params = readParams(options);\n const operation =\n typeof params.operation === \"string\" ? params.operation.toLowerCase() : \"\";\n const mode = typeof params.mode === \"string\" ? params.mode.toLowerCase() : \"\";\n const sourceId =\n typeof params.sourceId === \"string\" ? params.sourceId.trim() : \"\";\n const targetIds = Array.isArray(params.targetIds)\n ? params.targetIds.filter(\n (target): target is string => typeof target === \"string\",\n )\n : [];\n if (operation === \"set_mode\" && mode) return `set mode ${mode}`;\n if (operation === \"start_route\" && sourceId && targetIds.length > 0) {\n return `route ${sourceId} to ${targetIds.join(\", \")}`;\n }\n if (operation === \"stop_route\" && sourceId) return `stop routing ${sourceId}`;\n if (operation === \"status\") return \"show routing status\";\n return null;\n}\n\n/**\n * Action to manage audio routing\n * Allows users to configure simulcast/independent modes and routing assignments\n */\nexport const manageRouting = {\n name: \"MANAGE_ROUTING\",\n contexts: [\"media\", \"automation\", \"settings\"],\n contextGate: { anyOf: [\"media\", \"automation\", \"settings\"] },\n roleGate: { minRole: \"USER\" },\n similes: [\n \"SET_ROUTING_MODE\",\n \"ROUTE_AUDIO\",\n \"STOP_ROUTING\",\n \"set mode\",\n \"route to\",\n \"simulcast to\",\n \"independent mode\",\n \"stop routing\",\n ],\n description: \"Manage audio routing modes and assignments\",\n descriptionCompressed: \"manage audio rout mode assignment\",\n\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n state?: State,\n options?: unknown,\n ) => {\n const musicService = (await runtime.getService(\n \"music\",\n )) as MusicRoutingService | null;\n if (\n !musicService?.getAudioRouter ||\n !musicService.getZoneManager ||\n !musicService.startBroadcastRoute\n ) {\n return false;\n }\n if (selectedContextMatches(state, ROUTING_CONTEXTS)) return true;\n return routingTextFromOptions(options) !== null;\n },\n\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n _options: unknown,\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n const timeoutMs = 10_000;\n const maxCommandBytes = 2000;\n const source = message.content.source || \"unknown\";\n const effectiveCallback: HandlerCallback = callback ?? (async () => []);\n try {\n const musicService = (await runtime.getService(\n \"music\",\n )) as MusicRoutingService | null;\n if (!musicService) {\n return emit(callback, source, \"Music service not available\", false, {\n error: \"MUSIC_SERVICE_UNAVAILABLE\",\n });\n }\n const routingService = musicService as MusicRoutingService;\n if (\n !routingService.getAudioRouter ||\n !routingService.getZoneManager ||\n !routingService.startBroadcastRoute\n ) {\n return emit(\n callback,\n source,\n \"Audio routing is not available in this runtime\",\n false,\n {\n error: \"AUDIO_ROUTING_UNAVAILABLE\",\n },\n );\n }\n\n const text = (\n routingTextFromOptions(_options)?.toLowerCase() ||\n message.content.text?.toLowerCase() ||\n \"\"\n ).slice(0, maxCommandBytes);\n\n // Parse command\n if (text.includes(\"set mode\") || text.includes(\"switch mode\")) {\n return handleSetMode(routingService, text, effectiveCallback, source);\n } else if (\n /\\bsimulcast\\s+.+\\s+to\\b/.test(text) ||\n /\\broute\\s+.+\\s+to\\b/.test(text)\n ) {\n return Promise.race([\n handleStartRouting(routingService, text, effectiveCallback, source),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"routing operation timed out\")),\n timeoutMs,\n ),\n ),\n ]);\n } else if (text.includes(\"stop routing\")) {\n return Promise.race([\n handleStopRouting(routingService, text, effectiveCallback, source),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"routing stop timed out\")),\n timeoutMs,\n ),\n ),\n ]);\n } else if (\n text.includes(\"show routing\") ||\n text.includes(\"routing status\")\n ) {\n return handleShowRouting(routingService, effectiveCallback, source);\n } else {\n return emit(\n callback,\n source,\n `Available routing commands:\n• set mode simulcast|independent\n• route <stream> to <zone1>, <zone2>\n• simulcast <stream> to all\n• stop routing <stream>\n• show routing status`,\n false,\n { error: \"UNRECOGNIZED_ROUTING_COMMAND\" },\n );\n }\n } catch (error) {\n logger.error(`Error managing routing: ${error}`);\n return emit(\n callback,\n source,\n `Error managing routing: ${error instanceof Error ? error.message : String(error)}`,\n false,\n {\n error: error instanceof Error ? error.message : String(error),\n },\n );\n }\n },\n\n parameters: [\n {\n name: \"operation\",\n description: \"Routing operation to perform.\",\n required: false,\n schema: {\n type: \"string\",\n enum: [\"set_mode\", \"start_route\", \"stop_route\", \"status\"],\n },\n },\n {\n name: \"mode\",\n description: \"Audio routing mode.\",\n required: false,\n schema: { type: \"string\", enum: [\"simulcast\", \"independent\"] },\n },\n {\n name: \"sourceId\",\n description: \"Stream or source id for routing.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"targetIds\",\n description: \"Routing target ids or zone names.\",\n required: false,\n schema: { type: \"array\", items: { type: \"string\" } },\n },\n ],\n\n examples: [\n [\n {\n name: \"{{user1}}\",\n content: { text: \"set mode simulcast\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"✅ Routing mode set to: simulcast\",\n action: \"SET_ROUTING_MODE\",\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"simulcast main-stream to all zones\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"🎵 Broadcasting main-stream to 3 zone(s) in simulcast mode\",\n action: \"ROUTE_AUDIO\",\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"stop routing main-stream\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"✅ Stopped routing for main-stream\",\n action: \"STOP_ROUTING\",\n },\n },\n ],\n ],\n} as Action;\n\nasync function handleSetMode(\n musicService: MusicRoutingService,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"set mode <simulcast|independent>\"\n const match = text.match(/(?:set|switch) mode (simulcast|independent)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: set mode simulcast|independent\",\n false,\n {\n error: \"INVALID_MODE_FORMAT\",\n },\n );\n }\n\n const [, mode] = match;\n musicService.setRoutingMode(mode as AudioRoutingMode);\n logger.log(`[ManageRouting] Set default routing mode to: ${mode}`);\n\n return emit(callback, source, `Routing mode set to: ${mode}`, true, { mode });\n}\n\nasync function handleStartRouting(\n musicService: MusicRoutingService,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"route <stream> to <zones>\" or \"simulcast <stream> to <zones>\"\n const routeMatch = text.match(/route (.+?) to (.+)/);\n const simulcastMatch = text.match(/simulcast (.+?) to (.+)/);\n\n const match = routeMatch || simulcastMatch;\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: route <stream> to <zone1>, <zone2> or simulcast <stream> to all\",\n false,\n {\n error: \"INVALID_ROUTE_FORMAT\",\n },\n );\n }\n\n const [, streamId, zonesStr] = match;\n const selectors = zonesStr\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n const targetIds = resolveTargetIds(musicService, selectors);\n if (targetIds.length === 0) {\n return emit(\n callback,\n source,\n \"No routing targets matched. Create zones first or register routing targets.\",\n false,\n {\n error: \"NO_ROUTING_TARGETS\",\n },\n );\n }\n\n const mode = simulcastMatch ? \"simulcast\" : musicService.getRoutingMode();\n const route = await musicService.startBroadcastRoute(\n streamId.trim(),\n targetIds,\n mode,\n );\n logger.log(\n `[ManageRouting] Routed \"${streamId}\" to targets: ${targetIds.join(\", \")}`,\n );\n\n return emit(\n callback,\n source,\n `Broadcasting ${route.sourceId} to ${route.targetIds.length} target(s) in ${route.mode} mode`,\n true,\n {\n sourceId: route.sourceId,\n targetIds: route.targetIds,\n mode: route.mode,\n },\n );\n}\n\nasync function handleStopRouting(\n musicService: MusicRoutingService,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"stop routing <stream>\"\n const match = text.match(/stop routing (.+)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: stop routing <stream>\",\n false,\n {\n error: \"INVALID_STOP_ROUTE_FORMAT\",\n },\n );\n }\n\n const [, streamId] = match;\n await musicService.stopBroadcastRoute(streamId.trim());\n logger.log(`[ManageRouting] Stopped routing for \"${streamId}\"`);\n\n return emit(callback, source, `Stopped routing for ${streamId}`, true, {\n sourceId: streamId.trim(),\n });\n}\n\nasync function handleShowRouting(\n musicService: MusicRoutingService,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n const status = musicService.getRoutingStatus();\n const routesText = status.activeRoutes.length\n ? status.activeRoutes\n .map(\n (route) =>\n ` - ${route.sourceId} → ${route.targetIds.length} targets (${route.mode})`,\n )\n .join(\"\\n\")\n : \" - none\";\n\n return emit(\n callback,\n source,\n `Routing Status:\n• Mode: ${status.mode}\n• Registered Targets: ${status.registeredTargets.length}\n• Zones: ${status.zoneCount}\n• Active Routes: ${status.activeRoutes.length}\n${routesText}`,\n true,\n { status },\n );\n}\n\nfunction resolveTargetIds(\n musicService: MusicRoutingService,\n selectors: string[],\n): string[] {\n const zoneManager = musicService.getZoneManager();\n const registeredTargets = new Set(musicService.listRoutingTargets());\n\n if (selectors.length === 1 && selectors[0].includes(\"all\")) {\n const zoneTargets = zoneManager.list().flatMap((zone) => zone.targetIds);\n const allTargets =\n zoneTargets.length > 0 ? zoneTargets : Array.from(registeredTargets);\n return [...new Set(allTargets)];\n }\n\n const targetIds = new Set<string>();\n for (const selector of selectors) {\n if (zoneManager.exists(selector)) {\n for (const targetId of zoneManager.getTargets(selector)) {\n targetIds.add(targetId);\n }\n continue;\n }\n if (registeredTargets.has(selector)) {\n targetIds.add(selector);\n }\n }\n\n return Array.from(targetIds);\n}\n\nexport default manageRouting;\n","import type {\n Action,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n Service,\n State,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport type { ZoneManager } from \"../router\";\n\ninterface MusicZoneService extends Service {\n capabilityDescription: string;\n stop(): Promise<void>;\n getZoneManager(): ZoneManager;\n}\n\nconst ZONE_CONTEXTS = [\"media\", \"automation\", \"settings\"] as const;\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nasync function emit(\n callback: HandlerCallback | undefined,\n source: string,\n text: string,\n success: boolean,\n data?: Record<string, unknown>,\n): Promise<ActionResult> {\n await callback?.({ text, source });\n return {\n success,\n text,\n values: { success, ...(data ?? {}) },\n data: { actionName: \"MANAGE_ZONES\", ...(data ?? {}) },\n };\n}\n\nfunction readParams(options: unknown): Record<string, unknown> {\n const direct =\n options && typeof options === \"object\"\n ? (options as Record<string, unknown>)\n : {};\n const parameters =\n direct.parameters && typeof direct.parameters === \"object\"\n ? (direct.parameters as Record<string, unknown>)\n : {};\n return { ...direct, ...parameters };\n}\n\nfunction zoneTextFromOptions(options: unknown): string | null {\n const params = readParams(options);\n const operation =\n typeof params.operation === \"string\" ? params.operation.toLowerCase() : \"\";\n const zoneName =\n typeof params.zoneName === \"string\" ? params.zoneName.trim() : \"\";\n const targetIds = Array.isArray(params.targetIds)\n ? params.targetIds.filter(\n (target): target is string => typeof target === \"string\",\n )\n : [];\n if (operation === \"create\" && zoneName && targetIds.length > 0) {\n return `create zone ${zoneName} with ${targetIds.join(\", \")}`;\n }\n if (operation === \"delete\" && zoneName) return `delete zone ${zoneName}`;\n if (operation === \"show\" && zoneName) return `show zone ${zoneName}`;\n if (operation === \"list\") return \"list zones\";\n if (operation === \"add\" && zoneName && targetIds[0]) {\n return `add ${targetIds[0]} to zone ${zoneName}`;\n }\n if (operation === \"remove\" && zoneName && targetIds[0]) {\n return `remove ${targetIds[0]} from zone ${zoneName}`;\n }\n return null;\n}\n\nfunction formatMetadata(metadata: Record<string, unknown>): string {\n return Object.entries(metadata)\n .map(([key, value]) => {\n if (Array.isArray(value)) return `${key}: ${value.join(\", \")}`;\n if (value && typeof value === \"object\") {\n return `${key}: ${Object.keys(value).join(\", \")}`;\n }\n return `${key}: ${String(value)}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Action to manage audio zones dynamically\n * Allows users to create, delete, and modify zones at runtime\n */\nexport const manageZones = {\n name: \"MANAGE_ZONES\",\n contexts: [\"media\", \"automation\", \"settings\"],\n contextGate: { anyOf: [\"media\", \"automation\", \"settings\"] },\n roleGate: { minRole: \"USER\" },\n similes: [\n \"CREATE_ZONE\",\n \"DELETE_ZONE\",\n \"LIST_ZONES\",\n \"ADD_TO_ZONE\",\n \"REMOVE_FROM_ZONE\",\n \"manage zones\",\n \"create zone\",\n \"delete zone\",\n \"list zones\",\n \"show zones\",\n ],\n description: \"Manage audio zones for multi-bot voice routing\",\n descriptionCompressed: \"manage audio zone multi-bot voice rout\",\n\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n state?: State,\n options?: unknown,\n ) => {\n const musicService = (await runtime.getService(\n \"music\",\n )) as MusicZoneService | null;\n if (!musicService?.getZoneManager?.()) {\n return false;\n }\n if (selectedContextMatches(state, ZONE_CONTEXTS)) return true;\n return zoneTextFromOptions(options) !== null;\n },\n\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n _options: unknown,\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n const timeoutMs = 10_000;\n const maxCommandBytes = 2000;\n const source = message.content.source || \"unknown\";\n const effectiveCallback: HandlerCallback = callback ?? (async () => []);\n try {\n const musicService = (await runtime.getService(\n \"music\",\n )) as MusicZoneService | null;\n if (!musicService) {\n return emit(callback, source, \"Music service not available\", false, {\n error: \"MUSIC_SERVICE_UNAVAILABLE\",\n });\n }\n\n const zoneManager = (musicService as MusicZoneService).getZoneManager?.();\n if (!zoneManager) {\n return emit(callback, source, \"Zone manager not available\", false, {\n error: \"ZONE_MANAGER_UNAVAILABLE\",\n });\n }\n\n const text = (\n zoneTextFromOptions(_options)?.toLowerCase() ||\n message.content.text?.toLowerCase() ||\n \"\"\n ).slice(0, maxCommandBytes);\n\n // Parse command\n if (text.includes(\"create zone\")) {\n return Promise.race([\n handleCreateZone(zoneManager, text, effectiveCallback, source),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"zone operation timed out\")),\n timeoutMs,\n ),\n ),\n ]);\n } else if (text.includes(\"delete zone\") || text.includes(\"remove zone\")) {\n return handleDeleteZone(zoneManager, text, effectiveCallback, source);\n } else if (/\\b(?:list|show)\\s+zones?\\b/.test(text)) {\n return handleListZones(zoneManager, text, effectiveCallback, source);\n } else if (/\\badd\\s+.+\\s+to zone\\b/.test(text)) {\n return handleAddToZone(zoneManager, text, effectiveCallback, source);\n } else if (/\\bremove\\s+.+\\s+from zone\\b/.test(text)) {\n return handleRemoveFromZone(\n zoneManager,\n text,\n effectiveCallback,\n source,\n );\n } else {\n return emit(\n callback,\n source,\n `Available zone commands:\n• create zone <name> with <targetIds>\n• delete zone <name>\n• list zones\n• add <targetId> to zone <name>\n• remove <targetId> from zone <name>`,\n false,\n { error: \"UNRECOGNIZED_ZONE_COMMAND\" },\n );\n }\n } catch (error) {\n logger.error(`Error managing zones: ${error}`);\n return emit(\n callback,\n source,\n `Error managing zones: ${error instanceof Error ? error.message : String(error)}`,\n false,\n {\n error: error instanceof Error ? error.message : String(error),\n },\n );\n }\n },\n\n parameters: [\n {\n name: \"operation\",\n description: \"Zone operation to perform.\",\n required: false,\n schema: {\n type: \"string\",\n enum: [\"create\", \"delete\", \"list\", \"add\", \"remove\", \"show\"],\n },\n },\n {\n name: \"zoneName\",\n description: \"Audio zone name.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"targetIds\",\n description: \"Target ids to create, add, or remove from a zone.\",\n required: false,\n schema: { type: \"array\", items: { type: \"string\" } },\n },\n ],\n\n examples: [\n [\n {\n name: \"{{user1}}\",\n content: {\n text: \"create zone main-stage with bot1:guild1:channel1, bot2:guild1:channel2\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: '✅ Created zone \"main-stage\" with 2 targets',\n action: \"CREATE_ZONE\",\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"list all zones\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"Active zones:\\n• main-stage (2 targets)\\n• vip-lounge (1 target)\",\n action: \"LIST_ZONES\",\n },\n },\n ],\n [\n {\n name: \"{{user1}}\",\n content: { text: \"delete zone main-stage\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: '✅ Deleted zone \"main-stage\"',\n action: \"DELETE_ZONE\",\n },\n },\n ],\n ],\n} as Action;\n\nasync function handleCreateZone(\n zoneManager: ZoneManager,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"create zone <name> with <targetIds>\"\n const match = text.match(/create zone (\\w+[\\w-]*) with (.+)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: create zone <name> with <targetId1>, <targetId2>, ...\",\n false,\n {\n error: \"INVALID_CREATE_ZONE_FORMAT\",\n },\n );\n }\n\n const [, zoneName, targetsStr] = match;\n const targetIds = [\n ...new Set(\n targetsStr\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean),\n ),\n ];\n const zone = zoneManager.create(zoneName, targetIds);\n logger.log(\n `[ManageZones] Created zone \"${zone.name}\" with targets: ${zone.targetIds.join(\", \")}`,\n );\n\n return emit(\n callback,\n source,\n `Created zone \"${zoneName}\" with ${targetIds.length} target(s)`,\n true,\n {\n zoneName,\n targetIds,\n },\n );\n}\n\nasync function handleDeleteZone(\n zoneManager: ZoneManager,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"delete zone <name>\"\n const match = text.match(/(?:delete|remove) zone (\\w+[\\w-]*)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: delete zone <name>\",\n false,\n {\n error: \"INVALID_DELETE_ZONE_FORMAT\",\n },\n );\n }\n\n const [, zoneName] = match;\n if (!zoneManager.delete(zoneName)) {\n return emit(callback, source, `Zone \"${zoneName}\" not found`, false, {\n error: \"ZONE_NOT_FOUND\",\n zoneName,\n });\n }\n logger.log(`[ManageZones] Deleted zone \"${zoneName}\"`);\n\n return emit(callback, source, `Deleted zone \"${zoneName}\"`, true, {\n zoneName,\n });\n}\n\nasync function handleListZones(\n zoneManager: ZoneManager,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n const detailMatch = text.match(/show zone (\\w+[\\w-]*)/);\n if (detailMatch) {\n const zone = zoneManager.get(detailMatch[1]);\n if (!zone) {\n return emit(\n callback,\n source,\n `Zone \"${detailMatch[1]}\" not found`,\n false,\n {\n error: \"ZONE_NOT_FOUND\",\n zoneName: detailMatch[1],\n },\n );\n }\n\n const metadata = zone.metadata\n ? `\\nMetadata:\\n${formatMetadata(zone.metadata)}`\n : \"\";\n return emit(\n callback,\n source,\n `Zone \"${zone.name}\":\n• Targets: ${zone.targetIds.length}\n• IDs: ${zone.targetIds.join(\", \")}${metadata}`,\n true,\n { zone },\n );\n }\n\n const zones = zoneManager.list();\n logger.log(`[ManageZones] Listing ${zones.length} zone(s)`);\n\n if (zones.length === 0) {\n return emit(callback, source, \"No zones configured yet.\", true, {\n zones: [],\n });\n }\n\n return emit(\n callback,\n source,\n `Active zones:\n${zones.map((zone) => `• ${zone.name} (${zone.targetIds.length} targets)`).join(\"\\n\")}\n\nUse \"show zone <name>\" for details`,\n true,\n { zones },\n );\n}\n\nasync function handleAddToZone(\n zoneManager: ZoneManager,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"add <targetId> to zone <name>\"\n const match = text.match(/add (.+?) to zone (\\w+[\\w-]*)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: add <targetId> to zone <name>\",\n false,\n {\n error: \"INVALID_ADD_TO_ZONE_FORMAT\",\n },\n );\n }\n\n const [, targetId, zoneName] = match;\n zoneManager.addTarget(zoneName, targetId.trim());\n logger.log(`[ManageZones] Added \"${targetId}\" to zone \"${zoneName}\"`);\n\n return emit(callback, source, `Added target to zone \"${zoneName}\"`, true, {\n zoneName,\n targetId: targetId.trim(),\n });\n}\n\nasync function handleRemoveFromZone(\n zoneManager: ZoneManager,\n text: string,\n callback: HandlerCallback,\n source: string,\n): Promise<ActionResult> {\n // Parse: \"remove <targetId> from zone <name>\"\n const match = text.match(/remove (.+?) from zone (\\w+[\\w-]*)/);\n if (!match) {\n return emit(\n callback,\n source,\n \"Invalid format. Use: remove <targetId> from zone <name>\",\n false,\n {\n error: \"INVALID_REMOVE_FROM_ZONE_FORMAT\",\n },\n );\n }\n\n const [, targetId, zoneName] = match;\n zoneManager.removeTarget(zoneName, targetId.trim());\n logger.log(`[ManageZones] Removed \"${targetId}\" from zone \"${zoneName}\"`);\n\n return emit(\n callback,\n source,\n `Removed target from zone \"${zoneName}\"`,\n true,\n {\n zoneName,\n targetId: targetId.trim(),\n },\n );\n}\n\nexport default manageZones;\n","import {\n type ActionExample,\n type ActionResult,\n getActiveRoutingContextsForTurn,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport {\n getSmartMusicFetchService,\n type MusicFetchProgress,\n} from \"../utils/smartFetchService\";\nimport {\n confirmationRequired,\n isConfirmed,\n mergedOptions,\n} from \"./confirmation\";\n\nconst DOWNLOAD_MUSIC_CONTEXTS = [\"media\", \"files\"] as const;\nconst DOWNLOAD_MUSIC_KEYWORDS = [\n \"download\",\n \"fetch\",\n \"save\",\n \"grab\",\n \"music\",\n \"song\",\n \"track\",\n \"album\",\n \"library\",\n \"descargar\",\n \"guardar\",\n \"música\",\n \"canción\",\n \"télécharger\",\n \"musique\",\n \"chanson\",\n \"herunterladen\",\n \"speichern\",\n \"musik\",\n \"lied\",\n \"scaricare\",\n \"salvare\",\n \"baixar\",\n \"下载\",\n \"音乐\",\n \"保存\",\n \"ダウンロード\",\n \"音楽\",\n] as const;\n\nfunction hasDownloadMusicContext(message: Memory, state?: State): boolean {\n const active = new Set(\n getActiveRoutingContextsForTurn(state, message).map((context) =>\n `${context}`.toLowerCase(),\n ),\n );\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") active.add(item.toLowerCase());\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n return DOWNLOAD_MUSIC_CONTEXTS.some((context) => active.has(context));\n}\n\nfunction hasDownloadMusicIntent(message: Memory, state?: State): boolean {\n const text = [\n typeof message.content?.text === \"string\" ? message.content.text : \"\",\n typeof state?.values?.recentMessages === \"string\"\n ? state.values.recentMessages\n : \"\",\n ]\n .join(\"\\n\")\n .toLowerCase();\n return DOWNLOAD_MUSIC_KEYWORDS.some((keyword) =>\n text.includes(keyword.toLowerCase()),\n );\n}\n\nfunction readDownloadQuery(\n message: Memory,\n options?: Record<string, unknown>,\n): string {\n const maxQueryLength = 200;\n const direct = readDirectDownloadQuery(options);\n if (direct) return direct.slice(0, maxQueryLength);\n return (message.content.text || \"\").trim().slice(0, maxQueryLength);\n}\n\nfunction readDirectDownloadQuery(\n options?: Record<string, unknown>,\n): string | null {\n const merged = mergedOptions(options);\n const direct = merged.query ?? merged.searchQuery;\n if (typeof direct === \"string\" && direct.trim().length > 0) {\n return direct.trim();\n }\n return null;\n}\n\nexport const downloadMusicSimiles = [\n \"DOWNLOAD_MUSIC\",\n \"FETCH_MUSIC\",\n \"GET_MUSIC\",\n \"DOWNLOAD_SONG\",\n \"SAVE_MUSIC\",\n \"GRAB_MUSIC\",\n];\n\nexport async function validateDownloadMusic(\n _runtime: IAgentRuntime,\n message: Memory,\n state?: State,\n options?: Record<string, unknown>,\n): Promise<boolean> {\n return (\n (readDirectDownloadQuery(options)?.length ?? 0) >= 3 ||\n hasDownloadMusicContext(message, state) ||\n hasDownloadMusicIntent(message, state)\n );\n}\n\nexport async function handleDownloadMusic(\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n): Promise<ActionResult | undefined> {\n if (!callback) return { success: false, error: \"Missing callback\" };\n\n const timeoutMs = 120_000;\n const query = readDownloadQuery(message, options);\n\n if (!query || query.length < 3) {\n await callback({\n text: \"Please tell me what song you'd like to download (at least 3 characters).\",\n source: message.content.source,\n });\n return;\n }\n\n const preview = `Confirmation required before downloading music to the library: \"${query}\".`;\n if (!isConfirmed(options)) {\n await callback({\n text: preview,\n source: message.content.source,\n });\n return confirmationRequired(preview, { op: \"download\", query });\n }\n\n try {\n const smartFetch = getSmartMusicFetchService(runtime);\n const preferredQuality =\n (runtime.getSetting(\"MUSIC_QUALITY_PREFERENCE\") as string) || \"mp3_320\";\n\n await callback({\n text: `Searching for \"${query}\"...`,\n source: message.content.source,\n });\n\n let lastProgress = \"\";\n const onProgress = async (progress: MusicFetchProgress) => {\n const progressLabel = progress.stage || progress.message || \"working\";\n const statusText = progress.details\n ? `${progressLabel}: ${String(progress.details)}`\n : progressLabel;\n if (statusText !== lastProgress) {\n lastProgress = statusText;\n logger.info(`[DOWNLOAD_MUSIC] ${statusText}`);\n await callback({\n text: statusText,\n source: message.content.source,\n });\n }\n };\n\n const result = await Promise.race([\n smartFetch.fetchMusic({\n query,\n requestedBy: message.entityId,\n onProgress,\n preferredQuality: preferredQuality as \"flac\" | \"mp3_320\" | \"any\",\n }),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"music download timed out\")),\n timeoutMs,\n ),\n ),\n ]);\n\n if (!result.success || !result.url) {\n await callback({\n text: `Couldn't find or download \"${query}\". ${result.error || \"Please try a different search term.\"}`,\n source: message.content.source,\n });\n return;\n }\n\n let sourceText = \"\";\n if (result.source === \"library\") {\n sourceText = \"Already in your library\";\n } else if (result.source === \"ytdlp\") {\n sourceText = \"Fetched from streaming service\";\n } else if (result.source === \"torrent\") {\n sourceText = \"Fetched via torrent\";\n }\n\n const responseText = `**${result.title || query}** - ${sourceText}\\nAvailable in your music library`;\n\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought: `Downloaded music: ${result.title || query} (source: ${result.source})`,\n actions: [\"MUSIC_LIBRARY\"],\n },\n metadata: {\n type: \"custom\",\n actionName: \"MUSIC_LIBRARY\",\n legacyActionName: \"DOWNLOAD_MUSIC\",\n audioUrl: result.url,\n title: result.title || query,\n source: result.source,\n },\n },\n \"messages\",\n );\n\n await callback({\n text: responseText,\n source: message.content.source,\n });\n return { success: true, text: responseText };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(\"Error in DOWNLOAD_MUSIC action:\", errorMessage);\n\n await callback({\n text: `I encountered an error while trying to download \"${query}\". ${errorMessage}`,\n source: message.content.source,\n });\n return { success: false, error: errorMessage };\n }\n}\n\nexport const downloadMusicExamples: ActionExample[][] = [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Download Comfortably Numb by Pink Floyd\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll download that to your library!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"fetch some Led Zeppelin for me\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Searching and downloading Led Zeppelin!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"grab the entire Dark Side of the Moon album\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll download that album for you!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n];\n","import type { IAgentRuntime, UUID } from \"@elizaos/core\";\n\nexport type MusicFetchProgress = {\n stage?: string;\n message?: string;\n details?: unknown;\n};\n\nexport type MusicFetchResult = {\n success: boolean;\n source: \"library\" | \"ytdlp\" | \"torrent\";\n url?: string;\n title?: string;\n duration?: number;\n error?: string;\n};\n\ntype PreferredQuality = \"flac\" | \"mp3_320\" | \"any\";\n\nexport interface SmartMusicFetchServiceLike {\n fetchMusic(options: {\n query: string;\n requestedBy?: UUID;\n onProgress?: (progress: MusicFetchProgress) => Promise<void> | void;\n preferredQuality?: PreferredQuality;\n }): Promise<MusicFetchResult>;\n}\n\nexport function getSmartMusicFetchService(\n runtime: IAgentRuntime,\n): SmartMusicFetchServiceLike {\n const service = runtime.getService(\n \"smart-music-fetch\",\n ) as SmartMusicFetchServiceLike | null;\n\n if (!service) {\n throw new Error(\"Smart music fetch service is not available\");\n }\n\n return service;\n}\n","import {\n type ActionExample,\n type ActionResult,\n ChannelType,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n type UUID,\n} from \"@elizaos/core\";\nimport type { Playlist } from \"../components/playlists\";\nimport { loadPlaylists, savePlaylist } from \"../components/playlists\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\nimport {\n getSmartMusicFetchService,\n type MusicFetchProgress,\n} from \"../utils/smartFetchService\";\nimport {\n confirmationRequired,\n isConfirmed,\n mergedOptions,\n} from \"./confirmation\";\n\ntype PlaylistOp = \"save\" | \"load\" | \"delete\" | \"add\";\n\ninterface QueueTrack {\n url: string;\n title: string;\n duration?: number;\n}\n\ninterface MusicQueueReadService {\n getQueueList(guildId: string): QueueTrack[];\n getCurrentTrack(guildId: string): QueueTrack | null;\n}\n\ninterface MusicQueueAddService {\n addTrack(\n guildId: string,\n track: {\n url: string;\n title: string;\n duration?: number;\n requestedBy: UUID;\n },\n ): Promise<unknown>;\n}\n\nconst MUSIC_SERVICE_NAME = \"music\";\nconst MUSIC_LIBRARY_SERVICE_NAME = \"musicLibrary\";\n\nfunction normalizeOp(value: unknown): PlaylistOp | null {\n if (typeof value !== \"string\") return null;\n const v = value\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\");\n if (v === \"save\" || v === \"load\" || v === \"delete\" || v === \"add\") return v;\n if (v === \"remove\") return \"delete\";\n if (v === \"play\" || v === \"restore\") return \"load\";\n if (v === \"create\" || v === \"store\") return \"save\";\n return null;\n}\n\nfunction readPlaylistOp(options: Record<string, unknown>, messageText: string) {\n return (\n normalizeOp(options.subaction) ??\n normalizeOp(options.playlistOp) ??\n normalizeOp(options.op) ??\n inferOpFromText(messageText)\n );\n}\n\nfunction inferOpFromText(text: string): PlaylistOp | null {\n const lower = text.toLowerCase();\n if (/\\b(add|put)\\b.+\\b(to|in)\\b.+\\bplaylist\\b/.test(lower)) return \"add\";\n if (/\\b(delete|remove)\\b.+\\bplaylist\\b/.test(lower)) return \"delete\";\n if (/\\b(load|play|restore)\\b.+\\bplaylist\\b/.test(lower)) return \"load\";\n if (/\\b(save|create|store)\\b.+\\bplaylist\\b/.test(lower)) return \"save\";\n return null;\n}\n\nfunction readPlaylistName(\n options: Record<string, unknown>,\n messageText: string,\n fallbackPattern?: RegExp,\n): string | undefined {\n const direct = options.playlistName ?? options.name;\n if (typeof direct === \"string\" && direct.trim().length > 0) {\n return direct.trim();\n }\n const pattern =\n fallbackPattern ??\n /(?:save|load|create|store|restore|delete|remove|play).*?playlist.*?(?:named|called|as)?\\s*[\"']?([^\"']+)[\"']?/i;\n const match = messageText.match(pattern);\n if (match?.[1]) return match[1].trim();\n const quoted = messageText.match(/[\"']([^\"']+)[\"']/);\n if (quoted?.[1]) return quoted[1].trim();\n return undefined;\n}\n\nasync function handleSave(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(\n MUSIC_SERVICE_NAME,\n ) as MusicQueueReadService | null;\n if (!musicService) {\n const text = \"Music service is not available.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Music service unavailable\" };\n }\n\n const musicLibrary = runtime.getService(\n MUSIC_LIBRARY_SERVICE_NAME,\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n const text = \"Music library service is not available.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Music library service unavailable\" };\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n if (!currentServerId) {\n const text = \"I could not determine which server you are in.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing server id\" };\n }\n\n const queue = musicService.getQueueList(currentServerId);\n const currentTrack = musicService.getCurrentTrack(currentServerId);\n\n if (queue.length === 0 && !currentTrack) {\n const text =\n \"The queue is empty. Add some tracks before saving a playlist.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Queue is empty\" };\n }\n\n const userId = message.entityId as UUID;\n if (!userId) {\n const text = \"I could not determine your user ID.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing user id\" };\n }\n\n const messageText = message.content.text || \"\";\n const playlistName =\n readPlaylistName(options, messageText) ||\n `Playlist ${new Date().toLocaleDateString()}`;\n\n const tracks: Array<{ url: string; title: string; duration?: number }> = [];\n if (currentTrack) {\n tracks.push({\n url: currentTrack.url,\n title: currentTrack.title,\n duration: currentTrack.duration,\n });\n }\n for (const track of queue) {\n tracks.push({\n url: track.url,\n title: track.title,\n duration: track.duration,\n });\n }\n\n const preview = `Confirmation required before saving playlist \"${playlistName}\" with ${tracks.length} track${tracks.length !== 1 ? \"s\" : \"\"}.`;\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"playlist\",\n subaction: \"save\",\n playlistName,\n trackCount: tracks.length,\n });\n }\n\n const playlist: Omit<Playlist, \"id\" | \"createdAt\" | \"updatedAt\"> = {\n name: playlistName,\n tracks,\n };\n const saved = await musicLibrary.savePlaylist(userId, playlist);\n const isDM = room?.type === ChannelType.DM;\n\n let responseText = `Saved playlist \"${saved.name}\" with ${saved.tracks.length} track${saved.tracks.length !== 1 ? \"s\" : \"\"}.`;\n if (!isDM) {\n responseText +=\n \" Tip: You can manage playlists in DMs to keep group chats clean.\";\n }\n await callback({ text: responseText, source: message.content.source });\n return { success: true, text: responseText };\n}\n\nasync function handleLoad(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(\n MUSIC_SERVICE_NAME,\n ) as MusicQueueAddService | null;\n if (!musicService) {\n const text = \"Music service is not available.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Music service unavailable\" };\n }\n\n const musicLibrary = runtime.getService(\n MUSIC_LIBRARY_SERVICE_NAME,\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n const text = \"Music library service is not available.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Music library service unavailable\" };\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n if (!currentServerId) {\n const text = \"I could not determine which server you are in.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing server id\" };\n }\n\n const userId = message.entityId as UUID;\n if (!userId) {\n const text = \"I could not determine your user ID.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing user id\" };\n }\n\n const playlists = await musicLibrary.loadPlaylists(userId);\n if (playlists.length === 0) {\n const text =\n \"You don't have any saved playlists. Save a queue first using 'save playlist'.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"No playlists available\" };\n }\n\n const messageText = message.content.text || \"\";\n const requestedName = readPlaylistName(options, messageText);\n\n let selected: Playlist | undefined;\n if (requestedName) {\n selected = playlists.find(\n (p) => p.name.toLowerCase() === requestedName.toLowerCase(),\n );\n if (!selected) {\n const text = `I couldn't find a playlist named \"${requestedName}\". Your playlists: ${playlists.map((p) => `\"${p.name}\"`).join(\", \")}`;\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Playlist not found\" };\n }\n } else {\n selected = [...playlists].sort((a, b) => b.updatedAt - a.updatedAt)[0];\n }\n\n const preview = `Confirmation required before loading playlist \"${selected.name}\" and adding ${selected.tracks.length} track${selected.tracks.length !== 1 ? \"s\" : \"\"} to the queue.`;\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"playlist\",\n subaction: \"load\",\n playlistId: selected.id,\n playlistName: selected.name,\n trackCount: selected.tracks.length,\n });\n }\n\n for (const track of selected.tracks) {\n await musicService.addTrack(currentServerId, {\n url: track.url,\n title: track.title,\n duration: track.duration,\n requestedBy: userId,\n });\n }\n\n const isDM = room?.type === ChannelType.DM;\n const addedCount = selected.tracks.length;\n let responseText = `Loaded playlist \"${selected.name}\" and added ${addedCount} track${addedCount !== 1 ? \"s\" : \"\"} to the queue.`;\n if (!isDM) {\n responseText +=\n \" Tip: You can manage playlists in DMs to keep group chats clean.\";\n }\n await callback({ text: responseText, source: message.content.source });\n return { success: true, text: responseText };\n}\n\nasync function handleDelete(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicLibrary = runtime.getService(\n MUSIC_LIBRARY_SERVICE_NAME,\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n const text = \"Music library service is not available.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Music library service unavailable\" };\n }\n\n const userId = message.entityId as UUID;\n if (!userId) {\n const text = \"I could not determine your user ID.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing user id\" };\n }\n\n const playlists = await musicLibrary.loadPlaylists(userId);\n if (playlists.length === 0) {\n const text = \"You don't have any saved playlists to delete.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"No playlists available\" };\n }\n\n const messageText = message.content.text || \"\";\n const playlistName = readPlaylistName(options, messageText);\n\n if (!playlistName) {\n const list = playlists.map((p) => `\"${p.name}\"`).join(\", \");\n const text = `Please specify which playlist to delete. Your playlists: ${list}\\n\\nExample: \"delete playlist My Favorites\"`;\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing playlist name\" };\n }\n\n const selected = playlists.find(\n (p) => p.name.toLowerCase() === playlistName.toLowerCase(),\n );\n if (!selected) {\n const list = playlists.map((p) => `\"${p.name}\"`).join(\", \");\n const text = `I couldn't find a playlist named \"${playlistName}\". Your playlists: ${list}`;\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Playlist not found\" };\n }\n\n const preview = `Confirmation required before deleting playlist \"${selected.name}\" (${selected.tracks.length} track${selected.tracks.length !== 1 ? \"s\" : \"\"}).`;\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"playlist\",\n subaction: \"delete\",\n playlistId: selected.id,\n playlistName: selected.name,\n trackCount: selected.tracks.length,\n });\n }\n\n const deleted = await musicLibrary.deletePlaylist(userId, selected.id);\n if (!deleted) {\n const text = \"I encountered an error while deleting the playlist.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Delete failed\" };\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const isDM = room?.type === ChannelType.DM;\n let responseText = `Deleted playlist \"${selected.name}\".`;\n if (!isDM) {\n responseText +=\n \" Tip: You can manage playlists in DMs to keep group chats clean.\";\n }\n await callback({ text: responseText, source: message.content.source });\n return { success: true, text: responseText };\n}\n\nasync function handleAdd(\n runtime: IAgentRuntime,\n message: Memory,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const messageText = message.content.text || \"\";\n const directSong =\n typeof options.song === \"string\" && options.song.trim().length > 0\n ? options.song.trim()\n : typeof options.songQuery === \"string\" &&\n options.songQuery.trim().length > 0\n ? options.songQuery.trim()\n : typeof options.query === \"string\" && options.query.trim().length > 0\n ? options.query.trim()\n : undefined;\n const directName = readPlaylistName(options, \"\");\n\n let songQuery = directSong;\n let playlistName = directName;\n\n if (!songQuery || !playlistName) {\n const match = messageText.match(/add\\s+(.+?)\\s+to\\s+(?:playlist\\s+)?(.+)/i);\n if (match) {\n songQuery ||= match[1].trim();\n playlistName ||= match[2].trim();\n }\n }\n\n if (!songQuery || songQuery.length < 3) {\n const text =\n 'Please specify what song to add and which playlist. Example: \"add Bohemian Rhapsody to my favorites\"';\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing song name\" };\n }\n if (!playlistName || playlistName.length < 2) {\n const text = \"Please specify a playlist name (at least 2 characters).\";\n await callback({ text, source: message.content.source });\n return { success: false, error: \"Missing playlist name\" };\n }\n\n const preview = `Confirmation required before adding \"${songQuery}\" to playlist \"${playlistName}\".`;\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"playlist\",\n subaction: \"add\",\n songQuery,\n playlistName,\n });\n }\n\n const smartFetch = getSmartMusicFetchService(runtime);\n const preferredQuality =\n (runtime.getSetting(\"MUSIC_QUALITY_PREFERENCE\") as string) || \"mp3_320\";\n\n await callback({\n text: `Searching for \"${songQuery}\"...`,\n source: message.content.source,\n });\n\n let lastProgress = \"\";\n const onProgress = async (progress: MusicFetchProgress) => {\n const label = progress.stage || progress.message || \"working\";\n const statusText = progress.details\n ? `${label}: ${String(progress.details)}`\n : label;\n if (statusText !== lastProgress) {\n lastProgress = statusText;\n logger.info(`[PLAYLIST_OP add] ${statusText}`);\n }\n };\n\n const result = await smartFetch.fetchMusic({\n query: songQuery,\n requestedBy: message.entityId,\n onProgress,\n preferredQuality: preferredQuality as \"flac\" | \"mp3_320\" | \"any\",\n });\n\n if (!result.success || !result.url) {\n const text = `Couldn't find or download \"${songQuery}\". ${result.error || \"Please try a different search term.\"}`;\n await callback({ text, source: message.content.source });\n return { success: false, error: result.error || \"Music not found\" };\n }\n\n const existing = await loadPlaylists(runtime, message.entityId);\n let target = existing.find(\n (p) => p.name.toLowerCase() === playlistName.toLowerCase(),\n );\n if (!target) {\n target = {\n id: crypto.randomUUID(),\n name: playlistName,\n tracks: [],\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n }\n\n const trackExists = target.tracks.some((t) => t.url === result.url);\n if (trackExists) {\n const text = `**${result.title || songQuery}** is already in playlist \"${playlistName}\"`;\n await callback({ text, source: message.content.source });\n return { success: true, text };\n }\n\n target.tracks.push({\n url: result.url,\n title: result.title || songQuery,\n duration: result.duration,\n addedAt: Date.now(),\n });\n target.updatedAt = Date.now();\n await savePlaylist(runtime, message.entityId, target);\n\n let responseText = `Added **${result.title || songQuery}** to playlist \"${playlistName}\"`;\n if (result.source === \"torrent\") responseText += \"\\nFetched via torrent\";\n responseText += `\\nPlaylist now has ${target.tracks.length} track${target.tracks.length !== 1 ? \"s\" : \"\"}`;\n\n await callback({ text: responseText, source: message.content.source });\n\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought: `Added ${result.title || songQuery} to playlist ${playlistName} (source: ${result.source})`,\n actions: [\"MUSIC_LIBRARY\"],\n },\n metadata: {\n type: \"custom\",\n actionName: \"MUSIC_LIBRARY\",\n legacyActionName: \"PLAYLIST\",\n op: \"add\",\n audioUrl: result.url,\n title: result.title || songQuery,\n playlistName,\n playlistId: target.id,\n source: result.source,\n },\n },\n \"messages\",\n );\n\n return { success: true, text: responseText };\n}\n\nexport const playlistOpSimiles = [\n \"PLAYLIST\",\n \"PLAYLIST\",\n \"MUSIC_PLAYLIST\",\n \"SAVE_PLAYLIST\",\n \"LOAD_PLAYLIST\",\n \"DELETE_PLAYLIST\",\n \"ADD_TO_PLAYLIST\",\n \"REMOVE_PLAYLIST\",\n \"PLAY_PLAYLIST\",\n];\n\nexport const playlistOpExamples: ActionExample[][] = [\n [\n {\n name: \"{{name1}}\",\n content: { text: 'save this queue as playlist \"Favorites\"' },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: 'Confirmation required before saving playlist \"Favorites\".',\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: { text: 'load my playlist \"Workout\"' },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: 'Confirmation required before loading playlist \"Workout\".',\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: { text: \"add Bohemian Rhapsody to my favorites\" },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: 'Confirmation required before adding \"Bohemian Rhapsody\" to playlist \"my favorites\".',\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n];\n\nexport async function validatePlaylistOp(\n _runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n options?: Record<string, unknown>,\n): Promise<boolean> {\n const merged = mergedOptions(options);\n return Boolean(readPlaylistOp(merged, message.content?.text ?? \"\"));\n}\n\nexport async function handlePlaylistOp(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n): Promise<ActionResult | undefined> {\n if (!callback) return { success: false, error: \"Missing callback\" };\n const merged = mergedOptions(options);\n const op = readPlaylistOp(merged, message.content?.text ?? \"\");\n if (!op) {\n const text =\n \"Could not determine playlist op. Use subaction=save, load, delete, or add.\";\n await callback({ text, source: message.content.source });\n return { success: false, error: text };\n }\n\n if (op === \"save\")\n return handleSave(runtime, message, state, merged, callback);\n if (op === \"load\")\n return handleLoad(runtime, message, state, merged, callback);\n if (op === \"delete\")\n return handleDelete(runtime, message, state, merged, callback);\n return handleAdd(runtime, message, merged, callback);\n}\n","import { type IAgentRuntime, logger, type UUID } from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { ensureAgentStorageContext } from \"./storageContext\";\n\n/**\n * Represents a track in a playlist\n */\nexport interface PlaylistTrack {\n url: string;\n title: string;\n duration?: number;\n addedAt?: number;\n requestedBy?: string;\n dedicatedTo?: string;\n dedicationMessage?: string;\n}\n\n/**\n * Represents a saved playlist\n */\nexport interface Playlist {\n id: string;\n name: string;\n tracks: PlaylistTrack[];\n createdAt: number;\n updatedAt: number;\n isFavorite?: boolean;\n}\n\nconst PLAYLIST_COMPONENT_TYPE = \"dj_playlist\";\n\n/**\n * Save a playlist to user's entity components\n */\nexport async function savePlaylist(\n runtime: IAgentRuntime,\n entityId: UUID,\n playlist: Omit<Playlist, \"id\" | \"createdAt\" | \"updatedAt\"> & {\n id?: string;\n createdAt?: number;\n },\n): Promise<Playlist> {\n const playlistId = playlist.id || (v4() as string);\n const now = Date.now();\n\n const fullPlaylist: Playlist = {\n ...playlist,\n id: playlistId,\n createdAt: playlist.createdAt || now,\n updatedAt: now,\n };\n\n // Get existing playlists component\n const existingComponent = await runtime.getComponent(\n entityId,\n PLAYLIST_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n const playlists =\n getStoredField<Playlist[]>(existingComponent, \"playlists\") ?? [];\n\n // Update or add playlist\n const index = playlists.findIndex((p) => p.id === playlistId);\n if (index >= 0) {\n playlists[index] = fullPlaylist;\n } else {\n playlists.push(fullPlaylist);\n }\n\n // Save to component\n if (existingComponent) {\n await runtime.updateComponent({\n ...existingComponent,\n data: mergeStoredField(existingComponent, \"playlists\", playlists),\n });\n } else {\n // Get room and world for component creation\n const entity = await runtime.getEntityById(entityId);\n if (!entity) {\n throw new Error(`Entity ${entityId} not found`);\n }\n\n const storageContext = await ensureAgentStorageContext(\n runtime,\n \"playlists\",\n \"music-library\",\n );\n\n await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: storageContext.roomId,\n worldId: storageContext.worldId,\n sourceEntityId: runtime.agentId,\n type: PLAYLIST_COMPONENT_TYPE,\n createdAt: now,\n data: createStoredField(\"playlists\", playlists),\n });\n }\n\n logger.debug(`Saved playlist \"${fullPlaylist.name}\" for entity ${entityId}`);\n return fullPlaylist;\n}\n\n/**\n * Load all playlists for a user\n */\nexport async function loadPlaylists(\n runtime: IAgentRuntime,\n entityId: UUID,\n): Promise<Playlist[]> {\n const component = await runtime.getComponent(\n entityId,\n PLAYLIST_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n return getStoredField<Playlist[]>(component, \"playlists\") ?? [];\n}\n\n/**\n * Delete a playlist\n */\nexport async function deletePlaylist(\n runtime: IAgentRuntime,\n entityId: UUID,\n playlistId: string,\n): Promise<boolean> {\n const component = await runtime.getComponent(\n entityId,\n PLAYLIST_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n return false;\n }\n\n const playlists = getStoredField<Playlist[]>(component, \"playlists\") ?? [];\n const filtered = playlists.filter((p) => p.id !== playlistId);\n\n await runtime.updateComponent({\n ...component,\n data: mergeStoredField(component, \"playlists\", filtered),\n });\n\n logger.debug(`Deleted playlist ${playlistId} for entity ${entityId}`);\n return true;\n}\n","import type { Component, MetadataValue } from \"@elizaos/core\";\n\ntype ComponentData = NonNullable<Component[\"data\"]>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction serializeMetadataValue(value: unknown): MetadataValue {\n if (value === undefined) {\n return undefined;\n }\n\n const serialized = JSON.stringify(value);\n if (serialized === undefined) {\n return undefined;\n }\n\n return JSON.parse(serialized) as MetadataValue;\n}\n\nexport function getStoredField<T>(\n component: Pick<Component, \"data\" | \"type\"> | null | undefined,\n field: string,\n): T | null {\n if (!component?.data) {\n return null;\n }\n\n if (!isRecord(component.data)) {\n throw new Error(\n `[Music Library] Component ${component.type} has invalid data payload`,\n );\n }\n\n const value = component.data[field];\n if (value === undefined || value === null) {\n return null;\n }\n\n return value as T;\n}\n\nexport function createStoredField(\n field: string,\n value: unknown,\n): ComponentData {\n return {\n [field]: serializeMetadataValue(value),\n };\n}\n\nexport function mergeStoredField(\n component: Pick<Component, \"data\">,\n field: string,\n value: unknown,\n): ComponentData {\n return {\n ...(component.data ?? {}),\n [field]: serializeMetadataValue(value),\n };\n}\n","import {\n createUniqueUuid,\n type IAgentRuntime,\n type Room,\n type UUID,\n} from \"@elizaos/core\";\n\ninterface RoomContext {\n room: Room;\n roomId: UUID;\n worldId: UUID;\n}\n\ninterface StorageContext {\n roomId: UUID;\n worldId: UUID;\n}\n\nexport async function requireRoomContext(\n runtime: IAgentRuntime,\n roomId: UUID,\n featureName: string,\n): Promise<RoomContext> {\n const room = await runtime.getRoom(roomId);\n if (!room) {\n throw new Error(`[${featureName}] Room ${roomId} not found`);\n }\n\n if (!room.worldId) {\n throw new Error(`[${featureName}] Room ${roomId} is missing worldId`);\n }\n\n return {\n room,\n roomId,\n worldId: room.worldId as UUID,\n };\n}\n\nexport async function ensureAgentStorageContext(\n runtime: IAgentRuntime,\n purpose: string,\n source: string,\n): Promise<StorageContext> {\n const worldId = createUniqueUuid(runtime, `${purpose}-world`);\n const roomId = createUniqueUuid(runtime, `${purpose}-room`);\n\n await runtime.ensureWorldExists({\n id: worldId,\n name: `${purpose} World`,\n agentId: runtime.agentId,\n metadata: { purpose },\n });\n\n await runtime.ensureRoomExists({\n id: roomId,\n name: `${purpose} Room`,\n source,\n type: \"GROUP\" as Room[\"type\"],\n channelId: roomId,\n serverId: roomId,\n worldId,\n metadata: { purpose },\n });\n\n return { roomId, worldId };\n}\n","import {\n type ActionExample,\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n ModelType,\n type State,\n} from \"@elizaos/core\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\nimport { parseJsonObjectResponse } from \"../utils/json\";\nimport {\n confirmationRequired,\n isConfirmed,\n mergedOptions,\n} from \"./confirmation\";\n\ninterface MusicQueryIntent {\n needsResearch: boolean;\n queryType:\n | \"first_single\"\n | \"latest_song\"\n | \"similar_artist\"\n | \"debut_album\"\n | \"popular_song\"\n | \"era\"\n | \"decade\"\n | \"year\"\n | \"genre\"\n | \"mood\"\n | \"vibe\"\n | \"activity\"\n | \"workout\"\n | \"study\"\n | \"party\"\n | \"chill\"\n | \"chart\"\n | \"top_hits\"\n | \"trending\"\n | \"album\"\n | \"album_track\"\n | \"full_album\"\n | \"movie_soundtrack\"\n | \"game_soundtrack\"\n | \"tv_theme\"\n | \"lyrics_based\"\n | \"topic\"\n | \"cover\"\n | \"remix\"\n | \"acoustic\"\n | \"live\"\n | \"specific_track\"\n | \"nth_album\"\n | \"direct_search\";\n artist?: string;\n album?: string;\n song?: string;\n genre?: string;\n mood?: string;\n decade?: string;\n year?: string;\n keywords?: string;\n searchQuery?: string;\n modifier?: \"cover\" | \"remix\" | \"acoustic\" | \"live\" | \"instrumental\";\n}\n\ninterface SearchResultSnippet {\n description?: string;\n snippet?: string;\n}\n\ninterface WebSearchService {\n search(query: string): Promise<SearchResultSnippet[]>;\n}\n\ninterface MusicQueueService {\n addTrack(\n guildId: string,\n track: {\n url: string;\n title: string;\n duration?: number;\n requestedBy: Memory[\"entityId\"];\n },\n ): Promise<void>;\n}\n\nfunction getModelText(response: unknown): string | null {\n return typeof response === \"string\" ? response : null;\n}\n\nfunction summarizeSearchResults(results: SearchResultSnippet[]): string {\n return results\n .slice(0, 3)\n .map((result) => result.description || result.snippet || \"\")\n .join(\"\\n\");\n}\n\nfunction formatPromptValue(value: unknown, depth = 0): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value.replace(/\\s+/g, \" \").trim();\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n return value\n .filter((item) => item != null)\n .slice(0, 20)\n .map((item) => {\n const rendered = formatPromptValue(item, depth + 1);\n return rendered ? `${\" \".repeat(depth)}- ${rendered}` : \"\";\n })\n .filter(Boolean)\n .join(\"\\n\");\n }\n if (typeof value === \"object\") {\n return Object.entries(value as Record<string, unknown>)\n .filter(([, entry]) => entry != null && entry !== \"\")\n .slice(0, 20)\n .map(([key, entry]) => {\n const rendered = formatPromptValue(entry, depth + 1);\n if (!rendered) return \"\";\n if (rendered.includes(\"\\n\")) {\n return `${\" \".repeat(depth)}${key}:\\n${rendered}`;\n }\n return `${\" \".repeat(depth)}${key}: ${rendered}`;\n })\n .filter(Boolean)\n .join(\"\\n\");\n }\n return String(value);\n}\n\nfunction readMusicQueryText(\n message: Memory,\n options?: Record<string, unknown>,\n): string {\n const merged = mergedOptions(options);\n const direct = merged.query ?? merged.searchQuery;\n if (typeof direct === \"string\" && direct.trim().length >= 3) {\n return direct.trim();\n }\n return message.content.text || \"\";\n}\n\n/**\n * Use LLM to understand the user's music query intent\n */\nconst analyzeMusicQuery = async (\n runtime: IAgentRuntime,\n messageText: string,\n): Promise<MusicQueryIntent | null> => {\n try {\n const prompt = `Analyze this music-related request and extract the intent. Be comprehensive - this could be any type of music query.\n\nMessage: \"${messageText}\"\n\nDetermine:\n1. Does this need research (Wikipedia/music databases) or can it be directly searched on YouTube?\n2. What type of query is this? Choose from:\n \n ARTIST-SPECIFIC:\n - \"first_single\": First/debut single of an artist\n - \"latest_song\": Most recent song\n - \"similar_artist\": Similar/related artists\n - \"debut_album\": Songs from debut album\n - \"popular_song\": Popular/hit song from artist\n - \"nth_album\": Specific album by number (2nd album, third album, etc)\n \n TEMPORAL:\n - \"era\": Music from an era (80s, 90s, 2000s, etc)\n - \"decade\": Music from a decade\n - \"year\": Music from a specific year\n \n GENRE/MOOD/VIBE:\n - \"genre\": Specific genre (jazz, rock, hip hop, etc)\n - \"mood\": Mood-based (sad, happy, angry, etc)\n - \"vibe\": Vibe-based (chill, energetic, dark, uplifting, etc)\n \n ACTIVITY:\n - \"activity\": General activity music\n - \"workout\": Workout/gym music\n - \"study\": Study/focus music\n - \"party\": Party music\n - \"chill\": Chill/relaxing music\n \n CHARTS/POPULARITY:\n - \"chart\": Chart hits (Billboard, etc)\n - \"top_hits\": Top hits\n - \"trending\": Viral/trending songs\n \n ALBUM:\n - \"album\": Play from an album\n - \"album_track\": Specific track from album\n - \"full_album\": Play entire album\n \n MEDIA:\n - \"movie_soundtrack\": From a movie\n - \"game_soundtrack\": From a video game\n - \"tv_theme\": TV show theme\n \n LYRICS/TOPIC:\n - \"lyrics_based\": Based on lyrics or themes\n - \"topic\": Songs about a topic\n \n VERSIONS:\n - \"cover\": Cover version\n - \"remix\": Remix\n - \"acoustic\": Acoustic version\n - \"live\": Live performance\n \n SPECIFIC:\n - \"specific_track\": Track by number (track 3, etc)\n - \"direct_search\": Can search directly\n\n3. Extract relevant details:\n - artist: Artist name if mentioned\n - album: Album name if mentioned\n - song: Song name if mentioned\n - genre: Genre if mentioned\n - mood: Mood if mentioned (happy, sad, energetic, chill, etc)\n - decade: Decade if mentioned (80s, 90s, 2000s, etc)\n - year: Specific year if mentioned\n - keywords: Other important keywords\n - modifier: If asking for specific version (cover, remix, acoustic, live, instrumental)\n\nRespond with JSON only:\n{\n \"needsResearch\": true,\n \"queryType\": \"direct_search\",\n \"artist\": \"artist name if mentioned\",\n \"album\": \"album name if mentioned\",\n \"song\": \"song name if mentioned\",\n \"genre\": \"genre if mentioned\",\n \"mood\": \"mood if mentioned\",\n \"decade\": \"decade if mentioned\",\n \"year\": \"year if mentioned\",\n \"keywords\": \"other important keywords\",\n \"modifier\": \"cover|remix|acoustic|live|instrumental if requested\",\n \"searchQuery\": \"query to use for direct_search\"\n}`;\n\n const rawResponse = await runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n });\n const response = getModelText(rawResponse);\n if (!response) {\n return null;\n }\n\n const parsedJson =\n parseJsonObjectResponse<Record<string, unknown>>(response);\n if (parsedJson?.queryType) {\n return {\n ...parsedJson,\n needsResearch:\n parsedJson.needsResearch === true ||\n String(parsedJson.needsResearch).toLowerCase() === \"true\",\n } as MusicQueryIntent;\n }\n\n return null;\n } catch (error) {\n logger.error(\n \"Error analyzing music query:\",\n error instanceof Error ? error.message : String(error),\n );\n return null;\n }\n};\n\n/**\n * Research music information using Wikipedia and music services\n */\nconst researchMusicInfo = async (\n runtime: IAgentRuntime,\n intent: MusicQueryIntent,\n): Promise<string | null> => {\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n const webSearchService = runtime.getService(\n \"webSearch\",\n ) as WebSearchService | null;\n\n logger.debug(\n `Researching music info: ${intent.queryType} for ${intent.artist || intent.genre || intent.mood || \"query\"}`,\n );\n\n let searchQuery: string | null = null;\n\n switch (intent.queryType) {\n case \"first_single\":\n case \"debut_album\": {\n if (!intent.artist) break;\n\n // Try to get artist info from Wikipedia\n if (musicLibrary?.getWikipediaArtistInfo) {\n const artistInfo = (await musicLibrary.getWikipediaArtistInfo(\n intent.artist,\n )) as { discography?: unknown; similarArtists?: string[] } | null;\n if (artistInfo?.discography) {\n // Use LLM to extract first single/album from discography\n const prompt = `From this artist discography, what was their first ${intent.queryType === \"first_single\" ? \"single\" : \"album\"}?\n\nDiscography:\n${formatPromptValue(artistInfo.discography).substring(0, 2000)}\n\nRespond with ONLY the song/album name, nothing else.`;\n\n const firstRelease = getModelText(\n await runtime.useModel(ModelType.TEXT_SMALL, { prompt }),\n );\n if (firstRelease) {\n searchQuery = `${intent.artist} ${firstRelease.trim()}`;\n }\n }\n }\n\n // Fallback: use web search\n if (!searchQuery && webSearchService) {\n const searchResults = await webSearchService.search(\n `${intent.artist} ${intent.queryType === \"first_single\" ? \"first single debut\" : \"debut album first album\"}`,\n );\n if (searchResults && searchResults.length > 0) {\n const prompt = `From these search results, what was ${intent.artist}'s ${intent.queryType === \"first_single\" ? \"first single\" : \"debut album\"}?\n\nResults: ${summarizeSearchResults(searchResults)}\n\nRespond with ONLY the song/album name, nothing else.`;\n\n const answer = getModelText(\n await runtime.useModel(ModelType.TEXT_SMALL, { prompt }),\n );\n if (answer) {\n searchQuery = `${intent.artist} ${answer.trim()}`;\n }\n }\n }\n break;\n }\n\n case \"nth_album\": {\n if (!intent.artist || !intent.keywords) break;\n\n // Extract album number from keywords (second, third, 2nd, 3rd, etc)\n const numberMatch = intent.keywords.match(\n /(\\d+)(?:st|nd|rd|th)|second|third|fourth|fifth/i,\n );\n if (!numberMatch) break;\n\n if (webSearchService) {\n const searchResults = await webSearchService.search(\n `${intent.artist} ${intent.keywords} album discography`,\n );\n if (searchResults && searchResults.length > 0) {\n const prompt = `From these search results, what was ${intent.artist}'s ${intent.keywords} album?\n\nResults: ${summarizeSearchResults(searchResults)}\n\nRespond with ONLY the album name, nothing else.`;\n\n const answer = getModelText(\n await runtime.useModel(ModelType.TEXT_SMALL, { prompt }),\n );\n if (answer) {\n searchQuery = `${intent.artist} ${answer.trim()}`;\n }\n }\n }\n break;\n }\n\n case \"similar_artist\": {\n if (!intent.artist) break;\n\n // Try to get similar artists from Wikipedia\n if (musicLibrary?.getWikipediaArtistInfo) {\n const artistInfo = await musicLibrary.getWikipediaArtistInfo(\n intent.artist,\n );\n if (\n artistInfo?.similarArtists &&\n artistInfo.similarArtists.length > 0\n ) {\n const similar =\n artistInfo.similarArtists[\n Math.floor(Math.random() * artistInfo.similarArtists.length)\n ];\n searchQuery = `${similar} popular song`;\n logger.info(`Found similar artist: ${similar}`);\n }\n }\n\n // Fallback: use canonical music metadata\n if (!searchQuery && musicLibrary?.getArtistInfo) {\n const artistInfo = await musicLibrary.getArtistInfo(intent.artist);\n if (\n artistInfo?.similarArtists &&\n artistInfo.similarArtists.length > 0\n ) {\n const similar = artistInfo.similarArtists[0];\n searchQuery = `${similar} popular song`;\n logger.info(`Found similar artist: ${similar}`);\n }\n }\n break;\n }\n\n case \"latest_song\": {\n if (!intent.artist) break;\n searchQuery = `${intent.artist} latest song new ${new Date().getFullYear()}`;\n break;\n }\n\n case \"popular_song\": {\n if (!intent.artist) break;\n searchQuery = `${intent.artist} most popular song hit`;\n break;\n }\n\n case \"movie_soundtrack\":\n case \"game_soundtrack\":\n case \"tv_theme\": {\n if (!intent.keywords) break;\n const mediaType = intent.queryType\n .replace(\"_soundtrack\", \"\")\n .replace(\"_theme\", \"\");\n searchQuery = `${intent.keywords} ${mediaType} ${intent.queryType.includes(\"theme\") ? \"theme\" : \"soundtrack\"}`;\n break;\n }\n\n case \"era\":\n case \"decade\": {\n const timeKeyword = intent.decade || intent.year || intent.keywords;\n if (!timeKeyword) break;\n const genrePrefix = intent.genre ? `${intent.genre} ` : \"\";\n searchQuery = `${genrePrefix}${timeKeyword} hits popular songs`;\n break;\n }\n\n case \"genre\": {\n if (!intent.genre && !intent.keywords) break;\n const genre = intent.genre || intent.keywords;\n searchQuery = `${genre} music popular`;\n break;\n }\n\n case \"mood\":\n case \"vibe\": {\n const mood = intent.mood || intent.keywords;\n if (!mood) break;\n searchQuery = `${mood} music songs`;\n break;\n }\n\n case \"workout\":\n case \"study\":\n case \"party\":\n case \"chill\":\n case \"activity\": {\n const activity =\n intent.queryType === \"activity\" ? intent.keywords : intent.queryType;\n searchQuery = `${activity} music playlist`;\n break;\n }\n\n case \"chart\":\n case \"top_hits\":\n case \"trending\": {\n const chartType = intent.keywords || intent.queryType;\n searchQuery = `${chartType} ${new Date().getFullYear()} popular songs`;\n break;\n }\n\n case \"lyrics_based\":\n case \"topic\": {\n if (!intent.keywords) break;\n searchQuery = `songs about ${intent.keywords}`;\n break;\n }\n\n case \"album_track\":\n case \"specific_track\": {\n if (!intent.album && !intent.artist) break;\n const trackInfo = intent.keywords || \"\";\n searchQuery =\n `${intent.artist || \"\"} ${intent.album || \"\"} ${trackInfo}`.trim();\n break;\n }\n\n case \"full_album\": {\n if (!intent.album && !intent.artist) break;\n searchQuery = `${intent.artist || \"\"} ${intent.album || \"\"} full album`;\n break;\n }\n }\n\n // Apply modifier if specified (cover, remix, acoustic, live)\n if (searchQuery && intent.modifier) {\n searchQuery = `${searchQuery} ${intent.modifier}`;\n }\n\n return searchQuery;\n } catch (error) {\n logger.error(\n \"Error researching music info:\",\n error instanceof Error ? error.message : String(error),\n );\n return null;\n }\n};\n\n/**\n * Smart music query action that can research and play complex queries\n */\nexport const playMusicQuerySimiles = [\n \"PLAY_MUSIC_QUERY\",\n \"SMART_PLAY\",\n \"RESEARCH_AND_PLAY\",\n \"FIND_AND_PLAY\",\n \"INTELLIGENT_MUSIC_SEARCH\",\n];\n\nexport async function validatePlayMusicQuery(\n _runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n): Promise<boolean> {\n if (message.content.source !== \"discord\") {\n return false;\n }\n\n const messageText = (message.content.text || \"\").toLowerCase();\n\n // PERFORMANCE: Skip if this is a direct YouTube URL - let playYouTubeAudio handle it (much faster)\n if (\n messageText.includes(\"youtube.com/\") ||\n messageText.includes(\"youtu.be/\")\n ) {\n return false;\n }\n\n // Check if this is a complex query that needs research\n const researchKeywords = [\n // Artist-specific\n \"first single\",\n \"debut single\",\n \"first song\",\n \"debut album\",\n \"first album\",\n \"latest song\",\n \"newest song\",\n \"recent song\",\n \"new song\",\n \"new music\",\n \"something like\",\n \"similar to\",\n \"sounds like\",\n \"reminds me of\",\n \"in the style of\",\n \"most popular\",\n \"biggest hit\",\n \"best song\",\n \"top song\",\n \"second album\",\n \"third album\",\n \"2nd album\",\n \"3rd album\",\n \"nth album\",\n\n // Temporal\n \"80s\",\n \"90s\",\n \"2000s\",\n \"70s\",\n \"60s\",\n \"from the\",\n\n // Genre/Mood\n \"genre:\",\n \"chill\",\n \"vibes\",\n \"mood\",\n \"upbeat\",\n \"sad\",\n \"happy\",\n \"energetic\",\n\n // Activity\n \"workout\",\n \"gym\",\n \"study\",\n \"focus\",\n \"party\",\n \"driving\",\n \"sleep\",\n\n // Charts\n \"top\",\n \"chart\",\n \"billboard\",\n \"trending\",\n \"viral\",\n \"popular now\",\n\n // Media\n \"soundtrack\",\n \"theme song\",\n \"theme from\",\n \"from the movie\",\n \"from the game\",\n \"from the show\",\n \"tv show\",\n\n // Lyrics/Topic\n \"songs about\",\n \"with lyrics\",\n \"that mentions\",\n\n // Versions\n \"cover of\",\n \"remix of\",\n \"acoustic version\",\n \"live version\",\n \"live at\",\n \"instrumental\",\n\n // Album\n \"from the album\",\n \"track\",\n \"entire album\",\n \"full album\",\n \"whole album\",\n ];\n\n const needsResearch = researchKeywords.some((keyword) =>\n messageText.includes(keyword),\n );\n\n // Also check for pattern \"play [genre/mood] music\"\n const simplePatterns = [\n /play\\s+(some\\s+)?(jazz|rock|pop|hip hop|rap|metal|electronic|indie|folk|country|classical|blues)/i,\n /play\\s+(something\\s+)?(chill|upbeat|sad|happy|energetic|mellow|intense)/i,\n ];\n\n return (\n needsResearch || simplePatterns.some((pattern) => pattern.test(messageText))\n );\n}\n\nexport async function handlePlayMusicQuery(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n): Promise<ActionResult | undefined> {\n if (!callback) return { success: false, error: \"Missing callback\" };\n\n const messageText = readMusicQueryText(message, options);\n const preview = `Confirmation required before resolving and queueing music for: \"${messageText}\".`;\n if (!isConfirmed(options)) {\n await callback({\n text: preview,\n source: message.content.source,\n });\n return confirmationRequired(preview, {\n op: \"play_query\",\n query: messageText,\n });\n }\n\n try {\n // Step 1: Analyze the query intent\n await callback({\n text: \"🔍 Let me figure out what you want...\",\n source: message.content.source,\n });\n\n const intent = await analyzeMusicQuery(runtime, messageText);\n if (!intent) {\n await callback({\n text: \"I couldn't understand your music request. Try being more specific?\",\n source: message.content.source,\n });\n return;\n }\n\n logger.info(`Music query intent: ${JSON.stringify(intent)}`);\n\n let finalSearchQuery: string | null = null;\n\n // Step 2: Research if needed\n if (intent.needsResearch && intent.queryType !== \"direct_search\") {\n const researchResult = await researchMusicInfo(runtime, intent);\n\n if (!researchResult) {\n await callback({\n text: \"I couldn't resolve that music query from the available research services. Try a more direct song, artist, or album request.\",\n source: message.content.source,\n });\n return;\n }\n\n finalSearchQuery = researchResult;\n } else {\n // For direct searches, construct query from intent\n if (intent.searchQuery) {\n finalSearchQuery = intent.searchQuery;\n } else {\n const parts = [\n intent.artist,\n intent.song,\n intent.album,\n intent.genre,\n intent.mood,\n intent.keywords,\n ].filter(Boolean);\n finalSearchQuery = parts.length > 0 ? parts.join(\" \") : messageText;\n\n if (intent.modifier) {\n finalSearchQuery = `${finalSearchQuery} ${intent.modifier}`;\n }\n }\n }\n\n if (!finalSearchQuery) {\n await callback({\n text: \"I couldn't figure out what to search for. Can you rephrase your request?\",\n source: message.content.source,\n });\n return;\n }\n\n logger.info(`Final search query: ${finalSearchQuery}`);\n\n // Step 3: Search YouTube for the track\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n await callback({\n text: \"YouTube search service is not available.\",\n source: message.content.source,\n });\n return;\n }\n\n const results = await musicLibrary.searchYouTube(finalSearchQuery, {\n limit: 1,\n });\n if (!results || results.length === 0) {\n await callback({\n text: `I couldn't find anything matching \"${finalSearchQuery}\". Try being more specific?`,\n source: message.content.source,\n });\n return;\n }\n\n const topResult = results[0];\n logger.info(`Found: ${topResult.title} (${topResult.url})`);\n\n // Step 4: Queue the track via music service\n const musicService = runtime.getService(\n \"music\",\n ) as MusicQueueService | null;\n if (!musicService) {\n await callback({\n text: \"Music service is not available.\",\n source: message.content.source,\n });\n return;\n }\n\n // Get Discord guild ID from room - same pattern as playAudio action\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const guildId = room?.serverId;\n if (!guildId) {\n await callback({\n text: \"Could not determine Discord server. Make sure you're messaging from a server channel.\",\n source: message.content.source,\n });\n return;\n }\n\n // Use entityId (UUID) not fromId (Discord snowflake) for requestedBy\n // WHY: fromId in metadata is the raw Discord snowflake ID for security reference\n // entityId is the proper UUID created by createUniqueUuid(runtime, discordId)\n const requestEntityId = message.entityId;\n\n await musicService.addTrack(guildId, {\n url: topResult.url,\n title: topResult.title,\n duration: topResult.duration,\n requestedBy: requestEntityId,\n });\n\n await callback({\n text: `🎵 Queued: **${topResult.title}**`,\n source: message.content.source,\n });\n return { success: true, text: `Queued: ${topResult.title}` };\n } catch (error) {\n logger.error(\n \"Error in playMusicQuery:\",\n error instanceof Error ? error.message : String(error),\n );\n await callback({\n text: \"I ran into an issue trying to find that music.\",\n source: message.content.source,\n });\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport const playMusicQueryExamples: ActionExample[][] = [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play the strokes first single\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let me look that up!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play something like radiohead\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll find a similar artist!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play some 80s synth pop\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Finding 80s synth pop for you!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play workout music\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let's get you pumped up!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play a cover of wonderwall\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Looking for a cover version!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play the Inception soundtrack\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Finding that soundtrack!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n];\n","export function parseJsonObjectResponse<T = Record<string, unknown>>(\n response: string,\n): T | null {\n try {\n const trimmed = response.trim();\n const fenced = trimmed.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/i);\n const candidate = (fenced?.[1] ?? trimmed).trim();\n const firstBrace = candidate.indexOf(\"{\");\n const lastBrace = candidate.lastIndexOf(\"}\");\n if (firstBrace < 0 || lastBrace <= firstBrace) return null;\n const parsed = JSON.parse(candidate.slice(firstBrace, lastBrace + 1));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n return parsed as T;\n } catch {\n return null;\n }\n}\n\nexport function jsonPromptBlock(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n","import {\n type ActionExample,\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\n\n/**\n * Extract search query from message text\n * Handles various natural language patterns for searching\n */\nconst extractSearchQuery = (messageText: string): string | null => {\n if (!messageText) return null;\n\n // Patterns for search requests\n const patterns = [\n /(?:find|search|look up|get|show me)(?:\\s+(?:the|a))?\\s+(?:youtube|video|song|music)?\\s*(?:link|url)?\\s+for\\s+(.+)/i,\n /(?:what's|what is|whats)\\s+(?:the\\s+)?(?:youtube|video|song)?\\s*(?:link|url)?\\s+for\\s+(.+)/i,\n /(?:can you|could you|please)\\s+(?:find|search|get|show me)\\s+(?:the\\s+)?(?:youtube|video|song)?\\s*(?:link|url)?\\s+(?:for\\s+)?(.+)/i,\n /youtube\\s+search\\s+(?:for\\s+)?(.+)/i,\n /search\\s+youtube\\s+(?:for\\s+)?(.+)/i,\n ];\n\n for (const pattern of patterns) {\n const match = messageText.match(pattern);\n if (match?.[1]) {\n const query = match[1].trim();\n // Require minimum 3 characters to avoid ambiguous searches\n if (query.length >= 3) {\n return query;\n }\n }\n }\n\n return null;\n};\n\nfunction readOptions(options: unknown): Record<string, unknown> {\n const direct =\n options && typeof options === \"object\"\n ? (options as Record<string, unknown>)\n : {};\n const params =\n direct.parameters && typeof direct.parameters === \"object\"\n ? (direct.parameters as Record<string, unknown>)\n : {};\n return { ...direct, ...params };\n}\n\nfunction readSearchQuery(messageText: string, options: unknown): string | null {\n const params = readOptions(options);\n const query = params.query ?? params.searchQuery;\n if (typeof query === \"string\" && query.trim().length >= 3) {\n return query.trim();\n }\n return extractSearchQuery(messageText);\n}\n\nfunction readLimit(options: unknown): number {\n const raw = readOptions(options).limit;\n const parsed =\n typeof raw === \"number\"\n ? raw\n : typeof raw === \"string\"\n ? Number.parseInt(raw, 10)\n : 5;\n if (!Number.isFinite(parsed)) return 5;\n return Math.min(Math.max(1, Math.floor(parsed)), 10);\n}\n\nexport const searchYouTubeSimiles = [\n \"SEARCH_YOUTUBE\",\n \"FIND_YOUTUBE\",\n \"SEARCH_YOUTUBE_VIDEO\",\n \"FIND_SONG\",\n \"SEARCH_MUSIC\",\n \"GET_YOUTUBE_LINK\",\n \"LOOKUP_YOUTUBE\",\n];\n\nexport async function validateSearchYouTube(\n _runtime: IAgentRuntime,\n message: Memory,\n _state?: State,\n options?: unknown,\n): Promise<boolean> {\n const messageText = message.content.text || \"\";\n const searchQuery = readSearchQuery(messageText, options);\n return !!searchQuery;\n}\n\nexport async function handleSearchYouTube(\n runtime: IAgentRuntime,\n message: Memory,\n _state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n): Promise<ActionResult | undefined> {\n if (!callback) return { success: false, error: \"Missing callback\" };\n\n const messageText = message.content.text || \"\";\n const searchQuery = readSearchQuery(messageText, options);\n\n if (!searchQuery) {\n await callback({\n text: \"I couldn't understand what you want me to search for. Please try asking like: 'Find the YouTube link for Surefire by Wilderado' (at least 3 characters)\",\n source: message.content.source,\n });\n return { success: false, error: \"Missing search query\" };\n }\n\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n throw new Error(\"YouTube search service is not available\");\n }\n\n logger.debug(`Searching YouTube for: ${searchQuery}`);\n\n const searchResults = await musicLibrary.searchYouTube(searchQuery, {\n limit: readLimit(options),\n });\n\n if (!searchResults || searchResults.length === 0) {\n await callback({\n text: `I couldn't find any YouTube videos for \"${searchQuery}\". Try rephrasing your search or being more specific.`,\n source: message.content.source,\n });\n return { success: false, error: \"No YouTube results found\" };\n }\n\n const topResult = searchResults[0];\n const url = topResult.url;\n const title = topResult.title;\n const channel = topResult.channel || \"Unknown Channel\";\n\n let responseText = `Found it. Here's \"${title}\" by ${channel}:\\n${url}\\n\\n`;\n\n if (searchResults.length > 1) {\n responseText += \"Other results:\\n\";\n for (let i = 1; i < Math.min(3, searchResults.length); i++) {\n const result = searchResults[i];\n const resultTitle = result.title;\n const resultChannel = result.channel || \"Unknown\";\n responseText += `${i + 1}. ${resultTitle} by ${resultChannel}\\n ${result.url}\\n`;\n }\n }\n\n await runtime.createMemory(\n {\n entityId: message.entityId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: message.content.source,\n thought: `Searched YouTube for: ${searchQuery}, found: ${title}`,\n actions: [\"MUSIC_LIBRARY\"],\n },\n metadata: {\n type: \"custom\",\n actionName: \"MUSIC_LIBRARY\",\n legacyActionName: \"SEARCH_YOUTUBE\",\n searchQuery,\n resultUrl: url,\n resultTitle: title,\n resultChannel: channel,\n },\n },\n \"messages\",\n );\n\n await callback({\n text: responseText,\n actions: [\"SEARCH_YOUTUBE_RESPONSE\"],\n source: message.content.source,\n });\n\n return {\n success: true,\n text: responseText,\n data: {\n searchQuery,\n resultUrl: url,\n resultTitle: title,\n resultChannel: channel,\n },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(\"Error searching YouTube:\", errorMessage);\n await callback({\n text: `I encountered an error while searching YouTube: ${errorMessage}.`,\n source: message.content.source,\n });\n return { success: false, error: errorMessage };\n }\n}\n\nexport const searchYouTubeExamples: ActionExample[][] = [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Find the YouTube link for Surefire by Wilderado\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll search for that on YouTube!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you find the youtube link for Never Gonna Give You Up?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Let me search YouTube for that song!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"DJynAI, search youtube for bohemian rhapsody\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll find that for you on YouTube!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"What's the YouTube link for Blinding Lights by The Weeknd?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Searching YouTube for that track!\",\n actions: [\"MUSIC_LIBRARY\"],\n },\n },\n ],\n];\n","import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { mergedOptions } from \"./confirmation\";\nimport {\n downloadMusicExamples,\n downloadMusicSimiles,\n handleDownloadMusic,\n validateDownloadMusic,\n} from \"./downloadMusic\";\nimport {\n handlePlaylistOp,\n playlistOpExamples,\n playlistOpSimiles,\n validatePlaylistOp,\n} from \"./playlistOp\";\nimport {\n handlePlayMusicQuery,\n playMusicQueryExamples,\n playMusicQuerySimiles,\n validatePlayMusicQuery,\n} from \"./playMusicQuery\";\nimport {\n handleSearchYouTube,\n searchYouTubeExamples,\n searchYouTubeSimiles,\n validateSearchYouTube,\n} from \"./searchYouTube\";\n\ntype MusicLibraryOp = \"playlist\" | \"play_query\" | \"search_youtube\" | \"download\";\n\nconst MUSIC_LIBRARY_OPS = [\n \"playlist\",\n \"play_query\",\n \"search_youtube\",\n \"download\",\n] as const;\n\nconst MUSIC_LIBRARY_CONTEXTS = [\n \"media\",\n \"automation\",\n \"knowledge\",\n \"web\",\n \"files\",\n] as const;\n\nconst PLAYLIST_SUBACTIONS = new Set([\n \"save\",\n \"load\",\n \"delete\",\n \"add\",\n \"remove\",\n \"play\",\n \"restore\",\n \"create\",\n \"store\",\n]);\n\nexport const MUSIC_LIBRARY_OP_ALIASES: Record<string, MusicLibraryOp> = {\n add_to_playlist: \"playlist\",\n delete_playlist: \"playlist\",\n download_music: \"download\",\n download_song: \"download\",\n fetch_music: \"download\",\n find_and_play: \"play_query\",\n find_song: \"search_youtube\",\n find_youtube: \"search_youtube\",\n get_music: \"download\",\n get_youtube_link: \"search_youtube\",\n grab_music: \"download\",\n intelligent_music_search: \"play_query\",\n load_playlist: \"playlist\",\n lookup_youtube: \"search_youtube\",\n music_playlist: \"playlist\",\n play: \"play_query\",\n play_music_query: \"play_query\",\n play_playlist: \"playlist\",\n play_query: \"play_query\",\n playlist_op: \"playlist\",\n remove_playlist: \"playlist\",\n research_and_play: \"play_query\",\n save_music: \"download\",\n save_playlist: \"playlist\",\n search: \"search_youtube\",\n search_music: \"search_youtube\",\n search_youtube: \"search_youtube\",\n search_youtube_video: \"search_youtube\",\n smart_play: \"play_query\",\n youtube: \"search_youtube\",\n};\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction normalizeSubaction(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const normalized = value\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\");\n return normalized.length > 0 ? normalized : null;\n}\n\nfunction normalizeMusicLibraryOp(value: unknown): MusicLibraryOp | null {\n const normalized = normalizeSubaction(value);\n if (!normalized) return null;\n if ((MUSIC_LIBRARY_OPS as readonly string[]).includes(normalized)) {\n return normalized as MusicLibraryOp;\n }\n if (PLAYLIST_SUBACTIONS.has(normalized)) return \"playlist\";\n return MUSIC_LIBRARY_OP_ALIASES[normalized] ?? null;\n}\n\nexport function readExplicitMusicLibraryOp(\n options: Record<string, unknown>,\n): MusicLibraryOp | null {\n return (\n normalizeMusicLibraryOp(options.op) ??\n normalizeMusicLibraryOp(options.action) ??\n normalizeMusicLibraryOp(options.subaction)\n );\n}\n\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nexport async function inferMusicLibraryOp(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n): Promise<MusicLibraryOp | null> {\n const explicit = readExplicitMusicLibraryOp(options);\n if (explicit) return explicit;\n if (await validatePlaylistOp(runtime, message, state, options)) {\n return \"playlist\";\n }\n if (await validateSearchYouTube(runtime, message, state, options)) {\n return \"search_youtube\";\n }\n if (await validatePlayMusicQuery(runtime, message, state)) {\n return \"play_query\";\n }\n if (await validateDownloadMusic(runtime, message, state, options)) {\n return \"download\";\n }\n return null;\n}\n\nconst musicLibraryExamples: ActionExample[][] = [\n ...playlistOpExamples,\n ...playMusicQueryExamples,\n ...searchYouTubeExamples,\n ...downloadMusicExamples,\n];\n\nexport const musicLibraryAction: Action = {\n name: \"MUSIC_LIBRARY\",\n contexts: [...MUSIC_LIBRARY_CONTEXTS],\n contextGate: { anyOf: [...MUSIC_LIBRARY_CONTEXTS] },\n roleGate: { minRole: \"USER\" },\n similes: unique([\n ...playlistOpSimiles,\n ...playMusicQuerySimiles,\n ...searchYouTubeSimiles,\n ...downloadMusicSimiles,\n ]),\n description:\n \"Consolidated music library action. Use subaction=playlist with playlistOp=save, load, delete, or add for playlist management; subaction=play_query to research and queue complex music requests; subaction=search_youtube to return YouTube links; subaction=download to fetch music into the local library. Queue changes, downloads, and playlist mutations require confirmed:true.\",\n descriptionCompressed:\n \"Music library subactions: playlist(playlistOp save/load/delete/add), play_query, search_youtube, download. Mutations require confirmed:true.\",\n parameters: [\n {\n name: \"subaction\",\n description:\n \"Music library operation: playlist, play_query, search_youtube, or download.\",\n required: true,\n schema: {\n type: \"string\",\n enum: [\"playlist\", \"play_query\", \"search_youtube\", \"download\"],\n },\n },\n {\n name: \"playlistOp\",\n description:\n \"Playlist operation when subaction=playlist: save, load, delete, or add.\",\n required: false,\n schema: { type: \"string\", enum: [\"save\", \"load\", \"delete\", \"add\"] },\n },\n {\n name: \"query\",\n description:\n \"Song, artist, album, or video query for play_query, search_youtube, download, or playlist add.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"playlistName\",\n description: \"Playlist name for playlist save, load, delete, or add.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"song\",\n description: \"Song query for playlist subaction=add.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"limit\",\n description: \"Maximum YouTube search results to inspect.\",\n required: false,\n schema: { type: \"number\", minimum: 1, maximum: 10, default: 5 },\n },\n {\n name: \"confirmed\",\n description:\n \"Must be true before queue changes, downloads, or playlist mutations.\",\n required: false,\n schema: { type: \"boolean\", default: false },\n },\n ],\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n state?: State,\n options?: Record<string, unknown>,\n ): Promise<boolean> => {\n const merged = mergedOptions(options);\n const service = runtime.getService(\"musicLibrary\");\n return Boolean(\n service &&\n (readExplicitMusicLibraryOp(merged) ||\n selectedContextMatches(state, MUSIC_LIBRARY_CONTEXTS)),\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n ): Promise<ActionResult | undefined> => {\n const merged = mergedOptions(options);\n const op = await inferMusicLibraryOp(runtime, message, state, merged);\n\n if (!op) {\n const text =\n \"Could not determine music library subaction. Use subaction=playlist, play_query, search_youtube, or download.\";\n if (callback) await callback({ text, source: message.content.source });\n return { success: false, error: text };\n }\n\n if (op === \"playlist\") {\n return handlePlaylistOp(runtime, message, state, merged, callback);\n }\n if (op === \"play_query\") {\n return handlePlayMusicQuery(runtime, message, state, merged, callback);\n }\n if (op === \"search_youtube\") {\n return handleSearchYouTube(runtime, message, state, merged, callback);\n }\n return handleDownloadMusic(runtime, message, state, merged, callback);\n },\n examples: musicLibraryExamples,\n};\n\nexport default musicLibraryAction;\n","import {\n type Action,\n type ActionExample,\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport {\n type BaseGuildVoiceChannel,\n type DiscordPluginServiceLike,\n isDiscordPluginServiceLike,\n type MusicPlayerDiscordVoiceManager,\n} from \"../discordVoice\";\nimport { MusicService } from \"../service\";\nimport { isPlaybackTransportControlOnlyMessage } from \"../utils/playbackTransportIntent\";\nimport { ProgressiveMessage } from \"../utils/progressiveMessage\";\nimport {\n confirmationRequired,\n isConfirmed,\n mergedOptions,\n} from \"./confirmation\";\nimport { MUSIC_PLAYER_ACTION_DOCS } from \"./music-player-action-docs\";\n\n// Local stubs for cross-plugin / native deps that ship without d.ts files.\n// `@elizaos/plugin-music` builds with `dts: false`, and `discord.js`\n// is an optional peer dep we don't want to take a hard type dependency on.\ninterface DetectedMusicEntity {\n confidence: number;\n name: string;\n type: \"song\" | \"artist\" | \"album\" | string;\n}\n\ninterface LibraryTrack {\n url: string;\n title: string;\n duration?: number;\n playCount?: number;\n}\n\ninterface SpotifyTrackInfo {\n name?: string;\n artists?: { name: string }[];\n}\n\ninterface SpotifyClientLike {\n isConfigured?: () => boolean;\n getTrack?: (trackId: string) => Promise<SpotifyTrackInfo | null>;\n}\n\ninterface MusicLibraryLookupService {\n addSong?(song: {\n url: string;\n title: string;\n duration?: number;\n requestedBy?: string;\n }): Promise<unknown>;\n detectEntities?(text: string): Promise<DetectedMusicEntity[] | null>;\n getLastPlayedSong?(): Promise<LibraryTrack | null>;\n getSong?(url: string): Promise<LibraryTrack | null>;\n getSongByUrl?(url: string): Promise<LibraryTrack | null>;\n searchLibrary?(query: string, limit?: number): Promise<LibraryTrack[]>;\n searchYouTube?(\n query: string,\n options?: { limit?: number; includeShorts?: boolean },\n ): Promise<\n Array<{\n url: string;\n title: string;\n duration?: number;\n channel?: string;\n views?: number;\n }>\n >;\n spotifyClient?: SpotifyClientLike;\n trackTrackRequest?(\n entityId: string,\n track: { url: string; title: string },\n roomId?: string,\n worldId?: string,\n ): Promise<void>;\n}\n\n// Discord service name constant\nconst DISCORD_SERVICE_NAME = \"discord\";\n\ntype PlayAudioOptions = Record<string, unknown> | undefined;\ntype ActionResultData = NonNullable<ActionResult[\"data\"]>;\n\nfunction getPlayAudioQuery(message: Memory, options: PlayAudioOptions): string {\n const merged = mergedOptions(options);\n const explicitQuery =\n typeof merged.query === \"string\" && merged.query.trim().length > 0\n ? merged.query.trim()\n : typeof merged.url === \"string\" && merged.url.trim().length > 0\n ? merged.url.trim()\n : \"\";\n return explicitQuery || message.content.text || \"\";\n}\n\nfunction failureResult(\n text: string,\n error: string,\n data?: ActionResultData,\n): ActionResult {\n return { success: false, text, error, data };\n}\n\nconst PLAY_AUDIO_CONTEXTS = [\"media\", \"automation\"] as const;\nconst PLAY_AUDIO_KEYWORDS = [\n \"play\",\n \"song\",\n \"track\",\n \"music\",\n \"audio\",\n \"queue\",\n \"youtube\",\n \"spotify\",\n \"soundcloud\",\n \"stream\",\n \"listen\",\n \"reproduce\",\n \"cancion\",\n \"musica\",\n \"escuchar\",\n \"jouer\",\n \"chanson\",\n \"musique\",\n \"écouter\",\n \"spielen\",\n \"lied\",\n \"musik\",\n \"ascolta\",\n \"riproduci\",\n \"canzone\",\n \"再生\",\n \"曲\",\n \"音楽\",\n \"播放\",\n \"歌曲\",\n \"音乐\",\n \"재생\",\n \"노래\",\n \"음악\",\n] as const;\n\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nfunction hasPlayAudioIntent(\n message: Memory,\n state: State | undefined,\n): boolean {\n const text = [\n typeof message.content?.text === \"string\" ? message.content.text : \"\",\n typeof state?.values?.recentMessages === \"string\"\n ? state.values.recentMessages\n : \"\",\n ]\n .join(\"\\n\")\n .toLowerCase();\n if (extractAudioUrl(text).url) return true;\n return PLAY_AUDIO_KEYWORDS.some((keyword) =>\n text.includes(keyword.toLowerCase()),\n );\n}\n\n/**\n * Extract Spotify track/album/playlist info from URL\n * Returns search query that can be used to find the track on YouTube\n */\nconst extractSpotifyInfo = (\n spotifyUrl: string,\n): { type: \"track\" | \"album\" | \"playlist\"; searchQuery: string } | null => {\n try {\n const url = new URL(spotifyUrl);\n const pathParts = url.pathname.split(\"/\").filter((p) => p);\n\n if (pathParts.length < 2) return null;\n\n const type = pathParts[0] as \"track\" | \"album\" | \"playlist\";\n const id = pathParts[1];\n\n if (![\"track\", \"album\", \"playlist\"].includes(type) || !id) return null;\n\n // For Spotify URLs, we'll need to convert to a search query\n // The actual track info will be fetched via Spotify API if available\n return { type, searchQuery: spotifyUrl }; // Return URL as search query for now\n } catch {\n return null;\n }\n};\n\n/**\n * Extract supported audio URL from the message text using regex\n * Supports:\n * - YouTube: various formats (youtube.com, youtu.be)\n * - SoundCloud: soundcloud.com URLs\n * - Spotify: open.spotify.com URLs (converted to search queries)\n * - All yt-dlp supported platforms: Twitch, Vimeo, Bandcamp, Mixcloud, TikTok, Twitter/X, Instagram, etc.\n */\nconst extractAudioUrl = (\n messageText: string,\n): {\n url: string | null;\n isSpotify: boolean;\n spotifyInfo?: { type: string; searchQuery: string };\n} => {\n if (!messageText) return { url: null, isSpotify: false };\n\n // YouTube URL patterns\n const youtubeRegex =\n /(?:https?:\\/\\/)?(?:www\\.)?(?:youtube\\.com\\/(?:watch\\?v=|embed\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/;\n const youtubeMatch = messageText.match(youtubeRegex);\n if (youtubeMatch?.[1]) {\n return {\n url: `https://www.youtube.com/watch?v=${youtubeMatch[1]}`,\n isSpotify: false,\n };\n }\n\n // Spotify URL patterns (needs special handling - convert to search)\n const spotifyRegex =\n /(?:https?:\\/\\/)?(?:open\\.)?spotify\\.com\\/(?:track|album|playlist)\\/[a-zA-Z0-9]+(?:\\?[^\\s]*)?/i;\n const spotifyMatch = messageText.match(spotifyRegex);\n if (spotifyMatch) {\n let url = spotifyMatch[0];\n if (!url.startsWith(\"http://\") && !url.startsWith(\"https://\")) {\n url = `https://${url}`;\n }\n const spotifyInfo = extractSpotifyInfo(url);\n return {\n url: null,\n isSpotify: true,\n spotifyInfo: spotifyInfo || undefined,\n };\n }\n\n // Generic URL pattern for other platforms\n // yt-dlp supports 1000+ sites, so we're permissive - if it looks like a valid URL,\n // we'll pass it to yt-dlp and let it try to extract it\n const genericUrlRegex =\n /https?:\\/\\/(?:www\\.)?(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,}(?:\\/[^\\s]*)?/i;\n const genericMatch = messageText.match(genericUrlRegex);\n if (genericMatch) {\n const url = genericMatch[0];\n try {\n const parsedUrl = new URL(url);\n const domain = parsedUrl.hostname.toLowerCase().replace(/^www\\./, \"\");\n\n // Exclude known non-media domains (email, file protocols, etc.)\n const excludedDomains = [\n \"mailto:\",\n \"file:\",\n \"data:\",\n \"javascript:\",\n \"gmail.com\",\n \"outlook.com\",\n \"yahoo.com\", // Email providers\n ];\n\n // If it's a valid HTTP/HTTPS URL and not excluded, pass it to yt-dlp\n // yt-dlp is very permissive and will attempt extraction from most URLs\n if (parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\") {\n if (!excludedDomains.some((excluded) => domain.includes(excluded))) {\n return { url, isSpotify: false };\n }\n }\n } catch {\n // Invalid URL, ignore\n }\n }\n\n return { url: null, isSpotify: false };\n};\n\nconst escapeRegExp = (value: string): string =>\n value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\nconst normalizeQuotes = (value: string): string =>\n value.replace(/[“”]/g, '\"').replace(/[‘’]/g, \"'\").replace(/\\s+/g, \" \").trim();\n\nconst SEARCH_FILLER_TOKENS = [\n \"dj\",\n \"djynai\",\n \"ai\",\n \"please\",\n \"pls\",\n \"plz\",\n \"thanks\",\n \"thankyou\",\n \"song\",\n \"track\",\n \"music\",\n \"video\",\n \"play\",\n \"queue\",\n \"add\",\n];\n\nconst stripFillerTokens = (query: string): string => {\n const tokens = query\n .split(/[\\s,]+/)\n .map((token) => token.trim())\n .filter(Boolean);\n\n const filtered = tokens.filter(\n (token) => !SEARCH_FILLER_TOKENS.includes(token.toLowerCase()),\n );\n\n if (filtered.length >= 2) {\n return filtered.join(\" \");\n }\n\n return tokens.join(\" \");\n};\n\nconst dedupeStrings = (values: string[]): string[] => {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const value of values) {\n const normalized = value.toLowerCase();\n if (normalized.length === 0) {\n continue;\n }\n if (!seen.has(normalized)) {\n seen.add(normalized);\n result.push(value);\n }\n }\n return result;\n};\n\nconst buildQueryFromEntities = (\n entities: DetectedMusicEntity[],\n fallback: string,\n): string => {\n if (!entities || entities.length === 0) {\n return fallback;\n }\n\n const sorted = [...entities].sort((a, b) => b.confidence - a.confidence);\n const songs = sorted.filter((entity) => entity.type === \"song\");\n const artists = sorted.filter((entity) => entity.type === \"artist\");\n const albums = sorted.filter((entity) => entity.type === \"album\");\n\n const candidateParts: string[] = [];\n if (songs[0]) {\n candidateParts.push(songs[0].name);\n }\n if (artists[0]) {\n candidateParts.push(artists[0].name);\n } else if (!songs[0] && albums[0]) {\n candidateParts.push(albums[0].name);\n }\n\n const combined = dedupeStrings(candidateParts).join(\" \").trim();\n if (combined.length >= 3) {\n return combined;\n }\n\n const topNames = dedupeStrings(sorted.map((entity) => entity.name))\n .slice(0, 3)\n .join(\" \")\n .trim();\n if (topNames.length >= 3) {\n return topNames;\n }\n\n return fallback;\n};\n\nconst enhanceSearchQuery = async (\n runtime: IAgentRuntime,\n baseQuery: string,\n originalText: string,\n): Promise<string> => {\n let refined = normalizeQuotes(baseQuery);\n refined = stripFillerTokens(refined);\n\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n if (musicLibrary?.detectEntities) {\n const detectionSource = originalText || baseQuery;\n const entities = await musicLibrary.detectEntities(detectionSource);\n if (entities && entities.length > 0) {\n const entityQuery = buildQueryFromEntities(entities, refined);\n if (entityQuery && entityQuery.length >= 3) {\n refined = entityQuery;\n }\n }\n }\n } catch (error) {\n logger.debug(\n `Music entity detection unavailable for query refinement: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n return refined;\n};\n\n/**\n * Check if the message is a pronoun reference (it, that, this, etc.)\n */\nconst isPronounReference = (messageText: string): boolean => {\n if (!messageText) return false;\n\n const text = messageText.toLowerCase().trim();\n\n // Common pronoun patterns\n const pronounPatterns = [\n /^(?:play|queue|add)\\s+(?:it|that|this)(?:\\s+song|\\s+one|\\s+track)?$/i,\n /^(?:it|that|this)$/i,\n /^(?:that\\s+song|this\\s+song|that\\s+one|this\\s+one)$/i,\n /^(?:play|queue)\\s+(?:the\\s+)?(?:last|previous|recent)\\s+(?:song|one|track)$/i,\n ];\n\n return pronounPatterns.some((pattern) => pattern.test(text));\n};\n\n/**\n * Extract search query from message text for playing music\n * Handles various natural language patterns\n */\nconst extractSearchQuery = (\n messageText: string,\n botName?: string,\n): string | null => {\n if (!messageText) return null;\n\n // Remove common play/queue prefixes\n let query = messageText.trim();\n\n // Remove bot name if present ANYWHERE (case insensitive, allow punctuation)\n // Use global flag to remove all occurrences\n if (botName) {\n const escapedName = escapeRegExp(botName);\n // Match bot name with optional @, followed by optional punctuation/whitespace\n const botNamePattern = new RegExp(`@?${escapedName}[\\\\s,:-]*`, \"gi\");\n query = query.replace(botNamePattern, \"\").trim();\n }\n\n // Patterns to remove (case insensitive) - order matters!\n // Using ^ anchor for start-of-string patterns\n const prefixPatterns = [\n // Greetings\n /^(?:hey|hi|yo|alright|okay|ok)[,\\s]+/i,\n // Politeness\n /^(?:please|pls|plz)[,\\s]+/i,\n // Modal verbs / requests\n /^(?:can you|could you|would you|will you|can|could|would|will)[,\\s]+/i,\n // Additional conversational phrases\n /^(?:go ahead and|i want to hear|i want|i wanna hear|i wanna|let's hear|let me hear|throw on|cue up|find and)[,\\s]+/i,\n // Action verbs with optional articles/words - be more aggressive\n /^(?:play|queue|add|put on|start|begin|load|search for|search|find|get|show me|give me)[,\\s]+(?:me\\s+)?(?:the\\s+)?(?:song|track|music|video|audio)?[,\\s]*/i,\n // Clean up any remaining common words at start\n /^(?:me|the|a|an|some)[,\\s]+/i,\n ];\n\n // Apply each pattern multiple times until no more matches\n for (const pattern of prefixPatterns) {\n let prevQuery = \"\";\n while (prevQuery !== query) {\n prevQuery = query;\n query = query.replace(pattern, \"\").trim();\n }\n }\n\n // Remove any leading/trailing commas or punctuation\n query = query.replace(/^[,\\s:-]+|[,\\s:-]+$/g, \"\").trim();\n\n // Require minimum 3 characters to avoid ambiguous searches\n if (query.length < 3) {\n return null;\n }\n\n return stripFillerTokens(query);\n};\n\n/**\n * Get the current voice channel the bot is in, or the user's voice channel\n */\nconst getCurrentVoiceChannel = async (\n discordService: DiscordPluginServiceLike,\n guildId: string,\n userId?: string,\n): Promise<BaseGuildVoiceChannel | null> => {\n if (!discordService.client) return null;\n\n try {\n const guild = await discordService.client.guilds.fetch(guildId);\n if (!guild) return null;\n\n // First, check if the bot is in a voice channel\n const botMember = guild.members.me;\n if (botMember?.voice?.channel) {\n return botMember.voice.channel as BaseGuildVoiceChannel;\n }\n\n // If not, check if the user is in a voice channel\n if (userId) {\n const member = await guild.members.fetch(userId).catch(() => null);\n if (member?.voice?.channel) {\n return member.voice.channel as BaseGuildVoiceChannel;\n }\n }\n\n return null;\n } catch (error) {\n logger.error(\n \"Error getting current voice channel:\",\n error instanceof Error ? error.message : String(error),\n );\n return null;\n }\n};\n\nexport const playAudio: Action = {\n name: \"PLAY_AUDIO\",\n contexts: [\"media\", \"automation\"],\n contextGate: { anyOf: [\"media\", \"automation\"] },\n roleGate: { minRole: \"USER\" },\n similes: [\n \"PLAY_YOUTUBE\",\n \"PLAY_YOUTUBE_AUDIO\",\n \"PLAY_VIDEO_AUDIO\",\n \"PLAY_MUSIC\",\n \"PLAY_SONG\",\n \"PLAY_TRACK\",\n \"START_MUSIC\",\n \"PLAY_THIS\",\n \"STREAM_YOUTUBE\",\n \"PLAY_FROM_YOUTUBE\",\n \"QUEUE_SONG\",\n \"ADD_TO_QUEUE\",\n ],\n description:\n \"Start playing a new song: provide a track name, artist, search words, or a media URL. \" +\n \"Requires confirmed:true before playback or queue changes. \" +\n \"Never use PLAY_AUDIO for pause, resume, stop, skip, or queue — those go through PLAYBACK_OP \" +\n \"with op=pause|resume|skip|stop|queue. Do not pass action=pause or similar params to PLAY_AUDIO. \" +\n MUSIC_PLAYER_ACTION_DOCS,\n descriptionCompressed:\n \"Play new song by name/artist/URL. Not for pause/resume/stop/skip.\",\n parameters: [\n {\n name: \"query\",\n description:\n \"Track name, artist, search phrase, or direct media URL to play.\",\n required: false,\n schema: { type: \"string\", minLength: 3 },\n },\n {\n name: \"url\",\n description:\n \"Direct media URL to play. Prefer query for standard song requests.\",\n required: false,\n schema: { type: \"string\", minLength: 8 },\n },\n {\n name: \"confirmed\",\n description: \"Must be true to play or queue the requested audio.\",\n required: false,\n schema: { type: \"boolean\", default: false },\n },\n ],\n validate: async (_runtime: IAgentRuntime, message: Memory, state: State) => {\n const text = message.content?.text || \"\";\n if (isPlaybackTransportControlOnlyMessage(text)) {\n return false;\n }\n return (\n selectedContextMatches(state, PLAY_AUDIO_CONTEXTS) ||\n hasPlayAudioIntent(message, state)\n );\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n options: Record<string, unknown> | undefined,\n callback: HandlerCallback,\n ): Promise<ActionResult | undefined> => {\n const messageText = getPlayAudioQuery(message, options);\n // Create progressive message helper for status updates\n //\n // Why use ProgressiveMessage: playAudio has a deep pipeline (library check,\n // search, fetch metadata, setup voice, queue). This can take 5-15 seconds.\n // Without feedback, users think the bot is broken. ProgressiveMessage shows\n // status updates that edit the same Discord message, keeping users informed.\n const progress = new ProgressiveMessage(\n callback,\n message.content.source || \"discord\",\n );\n\n const isDiscord = message.content.source === \"discord\";\n const maybeDiscordService = isDiscord\n ? runtime.getService(DISCORD_SERVICE_NAME)\n : null;\n const discordService = isDiscordPluginServiceLike(maybeDiscordService)\n ? maybeDiscordService\n : null;\n\n // For Discord, we need the Discord service\n if (isDiscord && !discordService?.client) {\n logger.error(\"Discord service not found or not initialized\");\n await progress.fail(\"Discord service is not available.\");\n return failureResult(\n \"Discord service is not available.\",\n \"DISCORD_SERVICE_UNAVAILABLE\",\n );\n }\n if (isPlaybackTransportControlOnlyMessage(messageText)) {\n const text =\n \"To pause, resume, stop, skip, or queue, use PLAYBACK_OP with op=pause|resume|skip|stop|queue — not PLAY_AUDIO.\";\n await callback({\n text,\n source: message.content.source || \"discord\",\n });\n return failureResult(text, \"Transport control is not PLAY_AUDIO\", {\n query: messageText,\n });\n }\n\n const preview = `Confirmation required before playing or queueing audio for: \"${messageText}\".`;\n if (!isConfirmed(options)) {\n await callback({\n text: preview,\n source: message.content.source || \"discord\",\n });\n return confirmationRequired(preview, { query: messageText });\n }\n\n // Extract audio URL from message text using regex (YouTube, SoundCloud, Spotify, etc.)\n const urlResult = extractAudioUrl(messageText);\n let audioUrl = urlResult.url;\n let videoTitle = \"Unknown Title\";\n let videoDuration: number | undefined;\n let searchQuery: string | null = null;\n\n try {\n // Initial status update (transient - will be replaced quickly)\n //\n // Why no \"important\" flag: Library check is usually instant (< 100ms).\n // Showing this on web/CLI would just be noise. On Discord it's fine\n // because it gets edited away immediately.\n progress.update(\"🔍 Looking up track...\");\n const musicLibraryLookup = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n\n // Lazy-load play-dl for video info\n const play = await import(\"@vookav2/play-dl\").then((m) => m.default || m);\n\n // Handle Spotify URLs - convert to YouTube search\n if (urlResult.isSpotify && urlResult.spotifyInfo) {\n try {\n // Try to get track info from Spotify API if available\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n let spotifyTrackInfo: SpotifyTrackInfo | null = null;\n\n if (musicLibrary?.spotifyClient) {\n const spotifyClient = musicLibrary.spotifyClient;\n if (spotifyClient.isConfigured?.()) {\n const spotifyUrl = urlResult.spotifyInfo.searchQuery;\n if (urlResult.spotifyInfo.type === \"track\") {\n const trackId = spotifyUrl.match(/track\\/([a-zA-Z0-9]+)/)?.[1];\n if (trackId && spotifyClient.getTrack) {\n spotifyTrackInfo = await spotifyClient\n .getTrack(trackId)\n .catch(() => null);\n }\n }\n }\n }\n\n // Build search query from Spotify track info or use URL\n if (spotifyTrackInfo?.name) {\n const artistNames =\n spotifyTrackInfo.artists?.map((a) => a.name).join(\" \") || \"\";\n searchQuery = `${artistNames} ${spotifyTrackInfo.name}`.trim();\n videoTitle = spotifyTrackInfo.name;\n logger.info(\n `Spotify track detected: ${videoTitle} - searching YouTube...`,\n );\n } else {\n // Fallback: extract from URL or use generic search\n searchQuery = `spotify:${urlResult.spotifyInfo.type}`;\n logger.info(\n `Spotify ${urlResult.spotifyInfo.type} detected - will search YouTube for similar content`,\n );\n }\n\n // Set audioUrl to null so we trigger YouTube search\n audioUrl = null;\n } catch (error) {\n logger.debug(`Error processing Spotify URL: ${error}`);\n // Fallback to YouTube search\n searchQuery = \"spotify track\";\n audioUrl = null;\n }\n }\n\n // If no URL found, try to search for the content\n if (!audioUrl) {\n // Check if this is a pronoun reference (it, that, this, etc.)\n if (isPronounReference(messageText)) {\n // Try to get the last played song from music library (if available)\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n if (musicLibrary?.getLastPlayedSong) {\n const lastSong = await musicLibrary.getLastPlayedSong();\n if (lastSong) {\n logger.info(\n `Pronoun reference detected, using last played song: ${lastSong.title}`,\n );\n audioUrl = lastSong.url;\n videoTitle = lastSong.title;\n videoDuration = lastSong.duration;\n searchQuery = `(reference: ${lastSong.title})`; // Mark as reference\n }\n }\n } catch (error) {\n logger.debug(\n `Music library not available for pronoun resolution: ${error}`,\n );\n }\n\n if (!audioUrl) {\n await progress.fail(\n \"I couldn't find what song you're referring to. No songs have been played recently. Please specify the song name or provide a link.\",\n );\n return failureResult(\n \"I couldn't find what song you're referring to. No songs have been played recently. Please specify the song name or provide a link.\",\n \"NO_RECENT_TRACK_REFERENCE\",\n { query: messageText },\n );\n }\n } else {\n // Extract bot name from runtime for filtering\n const botName = runtime.character.name;\n searchQuery = extractSearchQuery(messageText, botName);\n\n if (!searchQuery) {\n await progress.fail(\n \"I couldn't understand what you want me to play. Please provide a link or tell me what song to search for (at least 3 characters).\",\n );\n return failureResult(\n \"I couldn't understand what you want me to play. Please provide a link or tell me what song to search for (at least 3 characters).\",\n \"UNPARSEABLE_PLAY_REQUEST\",\n { query: messageText },\n );\n }\n }\n }\n\n if (!audioUrl && searchQuery) {\n searchQuery = await enhanceSearchQuery(\n runtime,\n searchQuery,\n messageText,\n );\n }\n\n // Track whether we found the song in the local library\n let foundInLibrary = false;\n\n // Only search if we don't already have a URL (from pronoun reference)\n if (!audioUrl && searchQuery) {\n // Step 1: Check local music library first\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n if (musicLibrary?.searchLibrary) {\n logger.debug(`Searching local music library for: ${searchQuery}`);\n const libraryResults = await musicLibrary.searchLibrary(\n searchQuery,\n 5,\n );\n\n if (libraryResults && libraryResults.length > 0) {\n // Found in library! Use the top result (sorted by play count)\n const topLibraryResult = libraryResults[0];\n audioUrl = topLibraryResult.url;\n videoTitle = topLibraryResult.title;\n videoDuration = topLibraryResult.duration;\n foundInLibrary = true;\n\n logger.info(\n `Found in library (${topLibraryResult.playCount} plays): ${videoTitle} at ${audioUrl}`,\n );\n } else {\n logger.debug(`No matches in library for: ${searchQuery}`);\n }\n }\n } catch (error) {\n logger.debug(`Music library search unavailable: ${error}`);\n // Continue to YouTube search if library search fails\n }\n\n // Step 2: If not found in library, search for track\n if (!audioUrl) {\n // Important update - show on all platforms since search can take a while\n //\n // Why \"important: true\": YouTube/web search takes 2-10 seconds. Without\n // feedback, users think the bot froze. Even on web/CLI where we can't\n // edit, this is worth showing as a new message so users know we're working.\n progress.update(\"🔍 Searching for track...\", { important: true });\n logger.debug(`Searching for: ${searchQuery}`);\n\n try {\n const searchResults = await musicLibraryLookup?.searchYouTube?.(\n searchQuery,\n { limit: 3 },\n );\n\n if (!searchResults || searchResults.length === 0) {\n await progress.fail(\n `I couldn't find any results for \"${searchQuery}\". Try being more specific or providing a direct link.`,\n );\n return failureResult(\n `I couldn't find any results for \"${searchQuery}\". Try being more specific or providing a direct link.`,\n \"NO_SEARCH_RESULTS\",\n { searchQuery },\n );\n }\n\n // Use first result\n const topResult = searchResults[0];\n audioUrl = topResult.url;\n videoTitle = topResult.title;\n videoDuration = topResult.duration;\n\n logger.info(`Found on YouTube: ${videoTitle} at ${audioUrl}`);\n } catch (error) {\n logger.error(\n \"Error searching YouTube:\",\n error instanceof Error ? error.message : String(error),\n );\n const errorMsg =\n error instanceof Error ? error.message : String(error);\n await progress.fail(\n `I had trouble searching for \"${searchQuery}\". ${\n errorMsg.includes(\"browseEndpoint\") ||\n errorMsg.includes(\"navigationEndpoint\")\n ? \"Try being more specific with song and artist names.\"\n : \"Please try again or provide a direct link.\"\n }`,\n );\n return failureResult(\n `I had trouble searching for \"${searchQuery}\". ${\n errorMsg.includes(\"browseEndpoint\") ||\n errorMsg.includes(\"navigationEndpoint\")\n ? \"Try being more specific with song and artist names.\"\n : \"Please try again or provide a direct link.\"\n }`,\n errorMsg,\n { searchQuery },\n );\n }\n }\n }\n\n // For URL-based requests, get video/track info\n if (audioUrl && !videoDuration) {\n // First, check if we have this URL in our music library\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n const getSongByUrl =\n musicLibrary?.getSongByUrl ?? musicLibrary?.getSong;\n if (musicLibrary && getSongByUrl) {\n logger.debug(`Checking library for URL: ${audioUrl}`);\n const libraryTrack = await getSongByUrl.call(\n musicLibrary,\n audioUrl,\n );\n\n if (libraryTrack) {\n // Found in library! Use cached info\n videoTitle = libraryTrack.title;\n videoDuration = libraryTrack.duration;\n foundInLibrary = true;\n logger.info(\n `Found URL in library (${libraryTrack.playCount || 0} plays): ${videoTitle}`,\n );\n }\n }\n } catch (error) {\n logger.debug(`Music library lookup by URL unavailable: ${error}`);\n // Continue to fetch video info if library lookup fails\n }\n\n // Only fetch video info if we didn't find it in the library\n if (!videoDuration) {\n // Check if it's a YouTube URL (play-dl only supports YouTube)\n const isYouTube =\n audioUrl.includes(\"youtube.com\") || audioUrl.includes(\"youtu.be\");\n\n if (isYouTube) {\n // Validate YouTube URL\n if (!play.yt_validate(audioUrl)) {\n await progress.fail(\"The provided URL is not valid.\");\n return failureResult(\n \"The provided URL is not valid.\",\n \"INVALID_URL\",\n {\n audioUrl,\n },\n );\n }\n\n // Get video info for YouTube URLs\n const videoInfo = await play.video_info(audioUrl).catch((error) => {\n logger.error(\n \"Error getting YouTube video info:\",\n error instanceof Error ? error.message : String(error),\n );\n return null;\n });\n\n if (!videoInfo) {\n await progress.fail(\n \"I could not access that content. It may be private, unavailable, or the URL may be invalid.\",\n );\n return failureResult(\n \"I could not access that content. It may be private, unavailable, or the URL may be invalid.\",\n \"INACCESSIBLE_MEDIA\",\n { audioUrl },\n );\n }\n\n videoTitle = videoInfo.video_details.title || \"Unknown Title\";\n videoDuration = videoInfo.video_details.durationInSec;\n } else {\n // For SoundCloud and other platforms, yt-dlp will handle it\n // We can try to extract title from URL or use a generic title\n // The actual title will be determined when the track is played\n const urlParts = audioUrl.split(\"/\");\n const lastPart = urlParts[urlParts.length - 1];\n videoTitle = lastPart\n ? decodeURIComponent(lastPart.replace(/[-_]/g, \" \"))\n : \"Unknown Title\";\n logger.info(`Using URL for non-YouTube platform: ${audioUrl}`);\n }\n }\n }\n\n const room = state.data?.room || (await runtime.getRoom(message.roomId));\n let currentServerId = room?.serverId;\n\n // For non-Discord platforms, create a deterministic server ID\n if (!currentServerId) {\n if (isDiscord) {\n await progress.fail(\"I could not determine which server you are in.\");\n return failureResult(\n \"I could not determine which server you are in.\",\n \"DISCORD_SERVER_UNRESOLVED\",\n );\n } else {\n // For web/CLI, use room ID as server ID\n currentServerId = `web-${message.roomId}`;\n }\n } else if (!isDiscord) {\n // Prefix with 'web-' for non-Discord platforms to avoid conflicts with Discord server IDs\n currentServerId = `web-${currentServerId}`;\n }\n // For Discord, use the server ID as-is (no prefix)\n\n // Use entityId (UUID) not fromId (Discord snowflake) for requestedBy\n // WHY: fromId in metadata is the raw Discord snowflake ID for security reference\n // entityId is the proper UUID created by createUniqueUuid(runtime, discordId)\n const requestUserId = message.entityId;\n\n // Discord-specific voice channel logic\n // For Discord, use the original server ID (without web- prefix)\n const discordServerId = isDiscord\n ? room?.serverId || currentServerId\n : null;\n let voiceManager: MusicPlayerDiscordVoiceManager | null = null;\n if (isDiscord && discordService && discordServerId) {\n // Get current voice channel\n let voiceChannel = await getCurrentVoiceChannel(\n discordService,\n discordServerId,\n requestUserId,\n );\n\n // If bot is not in a voice channel, try to join the user's voice channel\n if (!voiceChannel && requestUserId) {\n const guild =\n await discordService.client.guilds.fetch(discordServerId);\n const member = await guild.members\n .fetch(requestUserId)\n .catch(() => null);\n if (member?.voice?.channel) {\n voiceChannel = member.voice.channel as BaseGuildVoiceChannel;\n voiceManager = discordService.voiceManager ?? null;\n if (voiceManager) {\n await voiceManager.joinChannel(voiceChannel);\n }\n }\n }\n\n if (!voiceChannel) {\n await progress.fail(\n \"I'm not in a voice channel, and you don't appear to be in one either. Please join a voice channel first, or ask me to join one.\",\n );\n return failureResult(\n \"I'm not in a voice channel, and you don't appear to be in one either. Please join a voice channel first, or ask me to join one.\",\n \"VOICE_CHANNEL_REQUIRED\",\n );\n }\n\n voiceManager = discordService.voiceManager ?? null;\n if (!voiceManager) {\n await progress.fail(\n \"Voice functionality is not available at the moment.\",\n );\n return failureResult(\n \"Voice functionality is not available at the moment.\",\n \"VOICE_MANAGER_UNAVAILABLE\",\n );\n }\n\n // Get voice connection\n let connection = voiceManager.getVoiceConnection(discordServerId);\n if (!connection) {\n // Try to join the voice channel\n await voiceManager.joinChannel(voiceChannel);\n // Wait a bit for connection to establish\n await new Promise((resolve) => setTimeout(resolve, 1000));\n connection = voiceManager.getVoiceConnection(discordServerId);\n if (!connection) {\n await progress.fail(\n \"I could not establish a voice connection. Please try again.\",\n );\n return failureResult(\n \"I could not establish a voice connection. Please try again.\",\n \"VOICE_CONNECTION_FAILED\",\n );\n }\n }\n\n // Register music channel (channel 1) if not already registered\n // This ensures the channel has the right configuration for music playback\n voiceManager.emit(\"registerChannel\", {\n channel: 1,\n priority: 50,\n canPause: true,\n interruptible: true,\n volume: 1.0,\n duckVolume: 0.3,\n });\n }\n\n // Get or create music service\n let musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n musicService = new MusicService(runtime);\n // Note: Service registration would typically happen in plugin init\n // For now, we'll use it directly\n }\n\n // For Discord, ensure voice manager is set\n if (isDiscord && voiceManager) {\n musicService.setVoiceManager(voiceManager);\n }\n\n // Check queue state BEFORE adding track\n // This tells us if the track will start playing immediately or be queued\n const currentTrack = musicService.getCurrentTrack(currentServerId);\n const queueLength = musicService.getQueueList(currentServerId).length;\n const willPlayImmediately = !currentTrack && queueLength === 0;\n\n // Build response message BEFORE adding track\n let responseText: string;\n if (willPlayImmediately) {\n // Track will start playing immediately\n if (foundInLibrary) {\n responseText = `📚 Playing from your library: **${videoTitle}**`;\n } else if (searchQuery) {\n responseText = `🔎 Found and now playing: **${videoTitle}**`;\n } else {\n responseText = `Now playing: **${videoTitle}**`;\n }\n } else {\n // Track will be added to queue\n const position = queueLength + 1; // Position after adding\n if (foundInLibrary) {\n responseText = `📚 Added from your library (position ${position}): **${videoTitle}**`;\n } else if (searchQuery) {\n responseText = `🔎 Found and added to queue (position ${position}): **${videoTitle}**`;\n } else {\n responseText = `Added to queue (position ${position}): **${videoTitle}**`;\n }\n }\n\n // For non-Discord platforms, provide web streaming URLs\n if (!isDiscord) {\n const baseUrl = process.env.SERVER_URL || \"http://localhost:3000\";\n const streamUrl = `${baseUrl}/music-player/stream?guildId=${encodeURIComponent(currentServerId)}`;\n const nowPlayingUrl = `${baseUrl}/music-player/now-playing?guildId=${encodeURIComponent(currentServerId)}`;\n responseText += `\\n\\n🌐 Web streaming: ${streamUrl}`;\n responseText += `\\n📊 Track info: ${nowPlayingUrl}`;\n }\n\n // Show we're setting up playback (transient - skip on non-editing platforms)\n //\n // Why no \"important\" flag: Voice setup and queue add are fast (< 200ms).\n // This is just \"progressive polish\" for Discord - not worth showing on\n // web/CLI where it would create an extra message.\n progress.update(\"✨ Setting up playback...\");\n\n // Add track to queue (this may start playback immediately)\n const track = await musicService.addTrack(currentServerId, {\n url: audioUrl as string,\n title: videoTitle,\n duration: videoDuration,\n requestedBy: requestUserId,\n });\n\n // Send final message with track info\n logger.info(`[PLAY_AUDIO] Completing with: ${responseText}`);\n await progress.complete(responseText);\n logger.info(\"[PLAY_AUDIO] Progressive complete sent successfully\");\n\n // Now do background operations (library tracking, memory creation)\n // These don't need to block the user feedback\n\n // Persist an action-log memory so the agent remembers what it played.\n // WHY entityId = agentId: this is the agent's action, not a user\n // message. Using message.entityId (the user) made it appear as a\n // blank user bubble in the web chat because the content has\n // `thought` but no `text`.\n runtime\n .createMemory(\n {\n entityId: runtime.agentId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"action\",\n thought: searchQuery\n ? `Searched for \"${searchQuery}\" and playing: ${videoTitle}`\n : `Playing audio: ${videoTitle}`,\n actions: [\"PLAY_AUDIO\"],\n },\n metadata: {\n type: \"custom\" as const,\n kind: \"PLAY_AUDIO\",\n audioUrl: audioUrl,\n videoTitle,\n trackId: track.id,\n ...(searchQuery && { searchQuery }),\n },\n },\n \"messages\",\n )\n .catch((error) => logger.warn(`Failed to create memory: ${error}`));\n\n // Track the request in user preferences (background)\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n if (musicLibrary?.trackTrackRequest) {\n musicLibrary\n .trackTrackRequest(\n message.entityId,\n {\n url: audioUrl as string,\n title: videoTitle,\n },\n message.roomId,\n message.worldId,\n )\n .catch((error: Error) =>\n logger.warn(`Failed to track request: ${error}`),\n );\n }\n } catch (error) {\n logger.warn(`Failed to track request: ${error}`);\n }\n\n // Save to global music library for future reference (background)\n try {\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryLookupService | null;\n logger.info(\n `[PlayAudio] Music library service: ${musicLibrary ? \"found\" : \"NOT FOUND\"}`,\n );\n if (musicLibrary?.addSong) {\n logger.info(\n `[PlayAudio] Adding to music library: \"${videoTitle}\" (${audioUrl})`,\n );\n musicLibrary\n .addSong({\n url: audioUrl as string,\n title: videoTitle,\n duration: videoDuration,\n requestedBy: requestUserId,\n })\n .then(() => {\n logger.info(\n `[PlayAudio] ✅ Song added to music library: \"${videoTitle}\"`,\n );\n })\n .catch((error: Error) => {\n logger.error(\n `[PlayAudio] ❌ Failed to add song to library: ${error.message}`,\n );\n });\n } else if (musicLibrary) {\n logger.warn(\n `[PlayAudio] Music library service found but addSong method missing. Available methods: ${Object.keys(musicLibrary).join(\", \")}`,\n );\n }\n } catch (error) {\n logger.error(`[PlayAudio] Error accessing music library: ${error}`);\n }\n\n // Memories already created via callback, return success\n return {\n success: true,\n text: responseText,\n data: {\n query: messageText,\n searchQuery,\n audioUrl,\n videoTitle,\n videoDuration,\n foundInLibrary,\n willPlayImmediately,\n queueLengthBeforeAdd: queueLength,\n trackId: track.id,\n serverId: currentServerId,\n },\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(\"Error playing audio:\", errorMessage);\n\n // Provide specific error messages based on the error type\n let userMessage =\n \"❌ I encountered an error while trying to play that track.\";\n\n const errorLower = errorMessage.toLowerCase();\n if (\n errorLower.includes(\"age\") &&\n (errorLower.includes(\"verification\") ||\n errorLower.includes(\"restriction\") ||\n errorLower.includes(\"restricted\"))\n ) {\n userMessage =\n \"⚠️ This content requires age verification or authentication.\";\n } else if (\n errorLower.includes(\"region\") ||\n errorLower.includes(\"not available\") ||\n errorLower.includes(\"unavailable\")\n ) {\n userMessage =\n \"⚠️ This content is not available in your region or has been removed.\";\n } else if (\n errorLower.includes(\"private\") ||\n errorLower.includes(\"unlisted\")\n ) {\n userMessage =\n \"⚠️ This content is private or unlisted and cannot be accessed.\";\n } else if (\n errorLower.includes(\"authentication\") ||\n errorLower.includes(\"cookies\") ||\n errorLower.includes(\"login\")\n ) {\n userMessage = \"⚠️ This content requires authentication.\";\n } else if (errorLower.includes(\"unable to access\")) {\n userMessage = \"⚠️ Unable to access this track.\";\n }\n\n await progress.fail(userMessage);\n return failureResult(userMessage, errorMessage, { query: messageText });\n }\n },\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play https://www.youtube.com/watch?v=dQw4w9WgXcQ\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll play that YouTube video in the voice channel!\",\n actions: [\"PLAY_AUDIO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Play Surefire by Wilderado\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll search for that song and play it!\",\n actions: [\"PLAY_AUDIO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Can you play Bohemian Rhapsody?\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Finding and playing that now!\",\n actions: [\"PLAY_AUDIO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Queue Never Gonna Give You Up\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll search for that and add it to the queue!\",\n actions: [\"PLAY_AUDIO\"],\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"play music from youtube.com/watch?v=dQw4w9WgXcQ\",\n },\n },\n {\n name: \"{{name2}}\",\n content: {\n text: \"I'll stream that YouTube video's audio!\",\n actions: [\"PLAY_AUDIO\"],\n },\n },\n ],\n ] as ActionExample[][],\n} as Action;\n\nexport default playAudio;\n","import type { VoiceManagerLike } from \"./queue\";\n\n/**\n * Voice channel shape used by PLAY_AUDIO — only fields the action reads.\n */\nexport interface BaseGuildVoiceChannel {\n guild: { members?: { me?: { voice?: { channel: unknown } } } };\n members?: Map<string, { voice?: { channel: unknown } }>;\n}\n\nexport interface MusicVoiceRegisterConfig {\n channel: number;\n priority: number;\n canPause: boolean;\n interruptible: boolean;\n volume: number;\n duckVolume: number;\n}\n\n/** Connection / registration surface used when joining Discord voice for playback. */\nexport interface DiscordVoiceConnectionExtensions {\n getVoiceConnection(guildId: string): unknown;\n joinChannel(channel: BaseGuildVoiceChannel): Promise<void>;\n on(\n event: \"registerChannel\",\n listener: (config: MusicVoiceRegisterConfig) => void,\n ): void;\n emit(event: \"registerChannel\", config: MusicVoiceRegisterConfig): boolean;\n}\n\nexport type MusicPlayerDiscordVoiceManager = VoiceManagerLike &\n DiscordVoiceConnectionExtensions;\n\nexport interface DiscordGuildMemberVoice {\n voice?: { channel: BaseGuildVoiceChannel };\n}\n\nexport interface DiscordGuildLike {\n members: {\n me?: DiscordGuildMemberVoice | null;\n fetch(userId: string): Promise<DiscordGuildMemberVoice | null>;\n };\n}\n\nexport interface DiscordClientLike {\n guilds: {\n fetch(guildId: string): Promise<DiscordGuildLike>;\n };\n}\n\nexport interface DiscordPluginServiceLike {\n client: DiscordClientLike;\n voiceManager?: MusicPlayerDiscordVoiceManager;\n}\n\nexport function isDiscordPluginServiceLike(\n service: unknown,\n): service is DiscordPluginServiceLike {\n if (typeof service !== \"object\" || service === null) return false;\n if (!(\"client\" in service)) return false;\n const client = (service as { client: unknown }).client;\n if (typeof client !== \"object\" || client === null) return false;\n const guilds = (client as { guilds?: unknown }).guilds;\n if (typeof guilds !== \"object\" || guilds === null) return false;\n return typeof (guilds as { fetch?: unknown }).fetch === \"function\";\n}\n","import { type IAgentRuntime, logger, Service, type UUID } from \"@elizaos/core\";\nimport type { BroadcastTrackMetadata, IAudioBroadcast } from \"./contracts\";\nimport { Broadcast } from \"./core\";\nimport { MusicQueue, type QueuedTrack, type VoiceManagerLike } from \"./queue\";\nimport { AudioRouter, type AudioRoutingMode, ZoneManager } from \"./router\";\nimport { AudioCacheService } from \"./services/audioCache\";\n\nconst MUSIC_SERVICE_NAME = \"music\";\n\n// Optional integration interface for music library\ninterface MusicLibraryService {\n trackTrackPlayed(\n roomId: UUID,\n track: { url: string; title: string },\n duration: number,\n requestedBy?: { entityId: UUID; name: string },\n ): Promise<void>;\n trackTrackRequest(\n entityId: UUID,\n track: { url: string; title: string },\n roomId?: UUID,\n worldId?: UUID,\n ): Promise<void>;\n trackSkip(\n entityId: UUID,\n trackUrl: string,\n roomId?: UUID,\n worldId?: UUID,\n ): Promise<void>;\n}\n\ninterface DiscordAudioSink {\n status?: \"connected\" | \"disconnected\" | string;\n _musicServiceListenerAttached?: boolean;\n feed(stream: NodeJS.ReadableStream): Promise<unknown>;\n listenerCount?(event: \"statusChange\"): number;\n on(event: \"statusChange\", listener: (status: string) => void): void;\n}\n\ninterface DiscordMusicIntegrationService {\n clearActivity?(): Promise<void> | void;\n getAudioSink?(guildId: string): DiscordAudioSink | null;\n getDefaultRoomIdForGuild?(\n guildId: string,\n ): Promise<UUID | null> | UUID | null;\n setListeningActivity?(trackTitle: string): Promise<void> | void;\n}\n\ninterface RoutingTarget {\n id: string;\n type: string;\n guildId?: string;\n channelId?: string;\n play?: (\n stream: NodeJS.ReadableStream,\n opts?: Record<string, unknown>,\n ) => Promise<unknown>;\n playAudio?: (\n stream: NodeJS.ReadableStream,\n opts?: Record<string, unknown>,\n ) => Promise<unknown>;\n feed?: (stream: NodeJS.ReadableStream) => Promise<unknown>;\n stop?: () => Promise<unknown>;\n stopAudio?: () => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Music service that manages music queues and playback\n * Pure playback engine - analytics/preferences are optional via music-library plugin\n */\nexport class MusicService extends Service {\n static serviceType: string = MUSIC_SERVICE_NAME;\n capabilityDescription = \"Play any song on request in voice chat\";\n\n private queues: Map<string, MusicQueue> = new Map(); // key: guildId\n private broadcasts: Map<string, Broadcast> = new Map(); // key: guildId\n private voiceManager: VoiceManagerLike | null = null;\n private audioCache: AudioCacheService;\n private readonly audioRouter: AudioRouter = new AudioRouter();\n private readonly zoneManager: ZoneManager = new ZoneManager();\n\n constructor(runtime?: IAgentRuntime) {\n super(runtime);\n // Initialize audio cache with optional cache directory from settings\n const cacheDir = runtime?.getSetting(\"AUDIO_CACHE_DIR\") as\n | string\n | undefined;\n this.audioCache = new AudioCacheService(cacheDir);\n }\n\n static async start(runtime: IAgentRuntime): Promise<MusicService> {\n logger.debug(`Starting MusicService for agent ${runtime.character.name}`);\n return new MusicService(runtime);\n }\n\n async stop(): Promise<void> {\n // Stop all queues\n for (const queue of this.queues.values()) {\n await queue.stop();\n }\n this.queues.clear();\n\n // Destroy all broadcasts\n for (const broadcast of this.broadcasts.values()) {\n broadcast.destroy();\n }\n this.broadcasts.clear();\n\n await this.audioRouter.unrouteAll();\n this.zoneManager.clear();\n }\n\n /**\n * Initialize the service with voice manager\n */\n setVoiceManager(voiceManager: VoiceManagerLike): void {\n this.voiceManager = voiceManager;\n }\n\n getAudioRouter(): AudioRouter {\n return this.audioRouter;\n }\n\n getZoneManager(): ZoneManager {\n return this.zoneManager;\n }\n\n setRoutingMode(mode: AudioRoutingMode): void {\n this.audioRouter.setDefaultMode(mode);\n }\n\n getRoutingMode(): AudioRoutingMode {\n return this.audioRouter.getDefaultMode();\n }\n\n registerRoutingTargets(targets: RoutingTarget[]): void {\n this.audioRouter.registerTargets(targets);\n }\n\n unregisterRoutingTarget(targetId: string): void {\n this.audioRouter.unregisterTarget(targetId);\n }\n\n listRoutingTargets(): string[] {\n return this.audioRouter.getRegisteredTargetIds();\n }\n\n async startBroadcastRoute(\n sourceId: string,\n targetIds: string[],\n mode?: AudioRoutingMode,\n ): Promise<{\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n }> {\n const broadcast = this.getBroadcast(sourceId);\n const subscription = broadcast.subscribe(`router:${sourceId}`);\n try {\n await this.audioRouter.route(\n sourceId,\n subscription.stream,\n targetIds,\n mode,\n () => subscription.unsubscribe(),\n );\n const routeInfo = this.audioRouter.getRoute(sourceId);\n if (!routeInfo) {\n throw new Error(`Route ${sourceId} missing after routing`);\n }\n return routeInfo;\n } catch (error) {\n subscription.unsubscribe();\n throw error;\n }\n }\n\n async stopBroadcastRoute(sourceId: string): Promise<void> {\n await this.audioRouter.unroute(sourceId);\n }\n\n getRoutingStatus(): {\n mode: AudioRoutingMode;\n activeRoutes: Array<{\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n }>;\n registeredTargets: string[];\n zoneCount: number;\n } {\n return {\n mode: this.audioRouter.getDefaultMode(),\n activeRoutes: this.audioRouter.getActiveRoutes(),\n registeredTargets: this.audioRouter.getRegisteredTargetIds(),\n zoneCount: this.zoneManager.count(),\n };\n }\n\n /**\n * Get or create a queue for a guild\n */\n getQueue(guildId: string): MusicQueue {\n if (!this.queues.has(guildId)) {\n const queue = new MusicQueue(\n guildId,\n this.voiceManager ?? null,\n this.runtime,\n this.audioCache,\n );\n this.queues.set(guildId, queue);\n\n // AUTO-WIRING: Connect queue to broadcast for new architecture\n //\n // WHY: Queue needs to know where to push audio. By giving it a broadcast\n // reference, the queue can call broadcast.pushTrack() when playing tracks.\n // This separates playback logic (queue) from distribution logic (broadcast).\n const broadcast = this.getBroadcast(guildId) as Broadcast;\n queue.setBroadcast(broadcast);\n logger.debug(\n `[MusicService] Auto-wired queue to broadcast for guild ${guildId}`,\n );\n\n // AUTO-WIRING: Subscribe Discord to broadcast when tracks play\n //\n // WHY ON METADATA EVENT:\n // We can't subscribe Discord immediately (queue might not have tracks yet).\n // When a track starts, broadcast emits 'metadata' event. That's our signal\n // to check if Discord is connected and auto-subscribe it.\n //\n // WHY NOT EVERY TIME:\n // autoSubscribeDiscord() checks if already subscribed. Calling it on every\n // track is idempotent - first call subscribes, subsequent calls do nothing.\n broadcast.on(\"metadata\", async (metadata: BroadcastTrackMetadata) => {\n logger.info(\n `[MusicService] Broadcast 'metadata' event for guild ${guildId}: ${metadata.title || \"unknown\"}`,\n );\n await this.autoSubscribeDiscord(guildId, broadcast);\n });\n\n // Register track played callback for optional analytics\n queue.onTrackPlayed(async (track, duration) => {\n await this.handleTrackPlayed(guildId, track, duration);\n });\n\n // Forward queue events to service level for external listeners\n queue.on(\"track:starting\", (track) => {\n logger.debug(`[${guildId}] Track starting: ${track.title}`);\n });\n queue.on(\"track:started\", async (track) => {\n logger.debug(`[${guildId}] Track started: ${track.title}`);\n // Update Discord listening activity when track starts\n await this.updateDiscordListeningActivity(track.title);\n });\n queue.on(\"track:finished\", async (track, duration) => {\n logger.debug(\n `[${guildId}] Track finished: ${track.title} (${duration}s)`,\n );\n // Clear activity when track finishes if queue is empty\n const queueList = queue.getQueue();\n if (queueList.length === 0) {\n await this.clearDiscordActivity();\n }\n });\n queue.on(\"track:age-restricted\", async (track) => {\n logger.info(\n `[${guildId}] Age restriction detected for: ${track.title}`,\n );\n // Send notification to user if runtime is available\n if (this.runtime && track.requestedBy) {\n try {\n const roomId = await this.getRoomIdFromGuild(guildId);\n if (roomId) {\n await this.runtime.createMemory(\n {\n agentId: this.runtime.agentId,\n roomId: roomId,\n entityId: track.requestedBy,\n content: {\n source: \"discord\",\n text: \"One sec, have to find a different version...\",\n actions: [],\n },\n },\n \"messages\",\n );\n }\n } catch (error) {\n logger.debug(\n `Could not send age restriction notification: ${error}`,\n );\n }\n }\n });\n queue.on(\"track:error\", async (track, reason) => {\n logger.warn(\n `[${guildId}] Failed to play track ${track.title}: ${reason}`,\n );\n if (this.runtime && track.requestedBy) {\n try {\n const roomId = await this.getRoomIdFromGuild(guildId);\n if (roomId) {\n const normalizedReason = reason || \"Unable to fetch audio\";\n const reasonLower = normalizedReason.toLowerCase();\n let userHint = normalizedReason;\n if (\n reasonLower.includes(\"age verification\") ||\n reasonLower.includes(\"sign in\") ||\n reasonLower.includes(\"not a bot\") ||\n reasonLower.includes(\"restricted\")\n ) {\n userHint = `${normalizedReason}. Please provide YouTube cookies via YOUTUBE_COOKIES or share a different source.`;\n }\n\n await this.runtime.createMemory(\n {\n agentId: this.runtime.agentId,\n roomId,\n entityId: track.requestedBy,\n content: {\n source: \"discord\",\n text: `Couldn't play **${track.title}**: ${userHint}`,\n actions: [],\n },\n },\n \"messages\",\n );\n }\n } catch (error) {\n logger.debug(`Could not send track failure notification: ${error}`);\n }\n }\n });\n }\n const existingQueue = this.queues.get(guildId);\n if (!existingQueue) {\n throw new Error(\n `Music queue missing after initialization for ${guildId}`,\n );\n }\n return existingQueue;\n }\n\n /**\n * Get all queues (for external monitoring)\n */\n getQueues(): Map<string, MusicQueue> {\n return this.queues;\n }\n\n /**\n * Get audio cache service\n */\n getAudioCache(): AudioCacheService {\n return this.audioCache;\n }\n\n /**\n * Add a track to the queue\n */\n async addTrack(\n guildId: string,\n track: Omit<QueuedTrack, \"id\" | \"addedAt\">,\n ): Promise<QueuedTrack> {\n const queue = this.getQueue(guildId);\n const queuedTrack = await queue.addTrack(track);\n\n // Optional: Track request in music library if available\n if (this.runtime) {\n try {\n const musicLibrary = this.runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (musicLibrary?.trackTrackRequest) {\n // Try to get roomId from Discord service\n const roomId = await this.getRoomIdFromGuild(guildId);\n if (roomId && track.requestedBy) {\n await musicLibrary.trackTrackRequest(\n track.requestedBy as UUID,\n { url: track.url, title: track.title },\n roomId,\n );\n }\n }\n } catch (error) {\n logger.debug(\n `Optional music library integration not available: ${error}`,\n );\n }\n }\n\n return queuedTrack;\n }\n\n /**\n * Skip current track\n */\n async skip(guildId: string, skipperEntityId?: UUID): Promise<boolean> {\n const queue = this.queues.get(guildId);\n if (!queue) return false;\n\n const currentTrack = queue.getCurrentTrack();\n\n // Optional: Track skip in music library if available\n if (this.runtime && currentTrack && skipperEntityId) {\n try {\n const musicLibrary = this.runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (musicLibrary?.trackSkip) {\n const roomId = await this.getRoomIdFromGuild(guildId);\n if (roomId) {\n await musicLibrary.trackSkip(\n skipperEntityId,\n currentTrack.url,\n roomId,\n );\n }\n }\n } catch (error) {\n logger.debug(\n `Optional music library integration not available: ${error}`,\n );\n }\n }\n\n return queue.skip();\n }\n\n /**\n * Pause playback\n */\n async pause(guildId: string): Promise<void> {\n const queue = this.queues.get(guildId);\n if (!queue) return;\n await queue.pause();\n }\n\n /**\n * Resume playback\n */\n async resume(guildId: string): Promise<void> {\n const queue = this.queues.get(guildId);\n if (!queue) return;\n await queue.resume();\n }\n\n /**\n * Stop playback for a specific guild\n */\n async stopPlayback(guildId: string): Promise<void> {\n const queue = this.queues.get(guildId);\n if (!queue) return;\n await queue.stop();\n }\n\n /**\n * Clear queue\n */\n clear(guildId: string): void {\n const queue = this.queues.get(guildId);\n if (!queue) return;\n queue.clear();\n }\n\n /**\n * Get queue\n */\n getQueueList(guildId: string): QueuedTrack[] {\n const queue = this.queues.get(guildId);\n if (!queue) return [];\n return queue.getQueue();\n }\n\n /**\n * Get current track\n */\n getCurrentTrack(guildId: string): QueuedTrack | null {\n const queue = this.queues.get(guildId);\n if (!queue) return null;\n return queue.getCurrentTrack();\n }\n\n /**\n * Get or create a broadcast for a guild\n * Broadcasts provide a clean interface for multiple consumers (Discord, Web, etc.)\n * to subscribe to the audio stream independently.\n * @param guildId Guild/server ID\n * @returns IAudioBroadcast instance\n */\n getBroadcast(guildId: string): IAudioBroadcast {\n if (!this.broadcasts.has(guildId)) {\n const broadcast = new Broadcast(guildId);\n this.broadcasts.set(guildId, broadcast);\n logger.debug(`[MusicService] Created broadcast for guild ${guildId}`);\n }\n const broadcast = this.broadcasts.get(guildId);\n if (!broadcast) {\n throw new Error(`Broadcast missing after initialization for ${guildId}`);\n }\n return broadcast;\n }\n\n /**\n * Remove a track from queue\n */\n removeTrack(guildId: string, trackId: string): boolean {\n const queue = this.queues.get(guildId);\n if (!queue) return false;\n return queue.removeTrack(trackId);\n }\n\n /**\n * Shuffle queue\n */\n shuffle(guildId: string): void {\n const queue = this.queues.get(guildId);\n if (!queue) return;\n queue.shuffle();\n }\n\n /**\n * Get whether queue is currently playing\n */\n getIsPlaying(guildId: string): boolean {\n const queue = this.queues.get(guildId);\n if (!queue) return false;\n return queue.getIsPlaying();\n }\n\n /**\n * Get whether playback is paused\n */\n getIsPaused(guildId: string): boolean {\n const queue = this.queues.get(guildId);\n if (!queue) return false;\n return queue.getIsPaused();\n }\n\n /**\n * Handle track played event (optional analytics integration)\n */\n private async handleTrackPlayed(\n guildId: string,\n track: QueuedTrack,\n duration: number,\n ): Promise<void> {\n if (!this.runtime) return;\n\n try {\n const musicLibrary = this.runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (musicLibrary?.trackTrackPlayed) {\n const roomId = await this.getRoomIdFromGuild(guildId);\n if (roomId) {\n await musicLibrary.trackTrackPlayed(\n roomId,\n { url: track.url, title: track.title },\n duration,\n track.requestedBy\n ? { entityId: track.requestedBy as UUID, name: \"\" }\n : undefined,\n );\n }\n }\n } catch (error) {\n logger.debug(`Optional music library analytics not available: ${error}`);\n }\n }\n\n /**\n * Helper to get roomId from guildId via Discord service\n */\n private async getRoomIdFromGuild(guildId: string): Promise<UUID | null> {\n if (!this.runtime) return null;\n\n try {\n const discordService = this.runtime.getService(\n \"discord\",\n ) as DiscordMusicIntegrationService | null;\n if (discordService?.getDefaultRoomIdForGuild) {\n return discordService.getDefaultRoomIdForGuild(guildId);\n }\n } catch (error) {\n logger.debug(`Could not get roomId from guildId: ${error}`);\n }\n\n return null;\n }\n\n /**\n * Update Discord listening activity to show currently playing track\n */\n private async updateDiscordListeningActivity(\n trackTitle: string,\n ): Promise<void> {\n if (!this.runtime) return;\n\n try {\n const discordService = this.runtime.getService(\n \"discord\",\n ) as DiscordMusicIntegrationService | null;\n if (discordService?.setListeningActivity) {\n await discordService.setListeningActivity(trackTitle);\n logger.debug(`Updated Discord activity: Listening to \"${trackTitle}\"`);\n }\n } catch (error) {\n logger.debug(`Could not update Discord listening activity: ${error}`);\n }\n }\n\n /**\n * Clear Discord activity when music stops\n */\n private async clearDiscordActivity(): Promise<void> {\n if (!this.runtime) return;\n\n try {\n const discordService = this.runtime.getService(\n \"discord\",\n ) as DiscordMusicIntegrationService | null;\n if (discordService?.clearActivity) {\n await discordService.clearActivity();\n logger.debug(\"Cleared Discord activity\");\n }\n } catch (error) {\n logger.debug(`Could not clear Discord activity: ${error}`);\n }\n }\n\n /**\n * Auto-subscribe Discord to broadcast (enables new architecture without requiring radio plugin)\n *\n * WHY AUTO-WIRING:\n * Design goal: music-player + discord should work together WITHOUT needing radio plugin.\n * Radio can add advanced features, but shouldn't be required for basic playback.\n *\n * THE CHALLENGE:\n * - MusicQueue pushes audio to broadcast\n * - Discord needs to subscribe to broadcast\n * - But music-player shouldn't import discord plugin (circular dependency)\n *\n * THE SOLUTION:\n * When a track starts, MusicService (which has access to runtime) checks:\n * 1. Is discord service available? (optional dependency)\n * 2. Is Discord connected to this guild?\n * 3. If yes, auto-subscribe Discord to the broadcast\n * 4. If not yet connected, attach listener to subscribe when it connects\n *\n * This happens transparently - user just plays music, and it works.\n *\n * WHY HANDLE RECONNECTS HERE:\n * Discord will emit status changes (connected/disconnected). MusicService listens\n * and auto-resubscribes on reconnect. This keeps the reconnection logic in one place.\n *\n * GRACEFUL DEGRADATION:\n * If discord service doesn't exist, or doesn't support IAudioSink, this silently\n * does nothing. Old architecture keeps working.\n */\n private async autoSubscribeDiscord(\n guildId: string,\n broadcast: Broadcast,\n ): Promise<void> {\n logger.info(\n `[MusicService] autoSubscribeDiscord called for guild ${guildId}`,\n );\n\n if (!this.runtime) {\n logger.debug(\n `[MusicService] No runtime available, skipping auto-subscribe`,\n );\n return;\n }\n\n try {\n // Check if Discord service is available\n const discordService = this.runtime.getService(\n \"discord\",\n ) as DiscordMusicIntegrationService | null;\n if (!discordService) {\n logger.debug(\n `[MusicService] Discord service not available for guild ${guildId}`,\n );\n return;\n }\n if (!discordService.getAudioSink) {\n logger.debug(\n `[MusicService] Discord service doesn't have getAudioSink method`,\n );\n return;\n }\n\n // Get Discord audio sink for this guild\n const sink = discordService.getAudioSink(guildId);\n if (!sink) {\n logger.warn(\n `[MusicService] No Discord sink available for guild ${guildId}`,\n );\n return;\n }\n\n logger.info(\n `[MusicService] Got sink for guild ${guildId}, status: ${sink.status}`,\n );\n\n const subscriptionId = `discord-${guildId}`;\n\n // Helper to subscribe to broadcast\n const doSubscribe = async () => {\n logger.info(`[MusicService] doSubscribe called for guild ${guildId}`);\n const alreadySubscribed = broadcast.isSubscribed(subscriptionId);\n logger.info(\n `[MusicService] isSubscribed(${subscriptionId}) = ${alreadySubscribed}`,\n );\n\n if (alreadySubscribed) {\n logger.warn(\n `[MusicService] Already subscribed for guild ${guildId}, skipping`,\n );\n // Already subscribed, nothing to do\n return;\n }\n\n try {\n logger.info(\n `[MusicService] Auto-subscribing Discord to broadcast for guild ${guildId}`,\n );\n const subscription = broadcast.subscribe(subscriptionId);\n logger.info(\n `[MusicService] Got subscription for guild ${guildId}, stream readable: ${subscription.stream?.readable}, feeding to sink...`,\n );\n await sink.feed(subscription.stream);\n logger.info(\n `[MusicService] ✅ Discord successfully subscribed to broadcast for guild ${guildId}`,\n );\n } catch (error) {\n logger.error(\n `[MusicService] ❌ Error subscribing Discord to broadcast: ${error}`,\n );\n }\n };\n\n // ALWAYS attach status change listener\n //\n // WHY ALWAYS:\n // Previously, we only attached the listener if the initial subscription succeeded.\n // This caused a race condition: if sink wasn't connected when 'metadata' fired,\n // we'd skip subscription AND skip attaching the listener, so when sink DID\n // connect later, no one would know to subscribe.\n //\n // WHY CHECK listenerCount:\n // autoSubscribeDiscord() is called on every 'metadata' event (every track).\n // We only want ONE listener per sink, not N listeners for N tracks.\n if (\n sink.listenerCount?.(\"statusChange\") === 0 ||\n !sink._musicServiceListenerAttached\n ) {\n sink._musicServiceListenerAttached = true;\n\n sink.on(\"statusChange\", async (status: string) => {\n if (status === \"connected\") {\n // Discord connected/reconnected - subscribe to broadcast\n logger.info(\n `[MusicService] Discord connected for guild ${guildId}, subscribing to broadcast`,\n );\n\n // Unsubscribe old subscription if exists (for reconnection case)\n if (broadcast.isSubscribed(subscriptionId)) {\n const oldSub = broadcast.subscribe(subscriptionId);\n oldSub.unsubscribe();\n }\n\n // Subscribe with fresh stream\n const newSubscription = broadcast.subscribe(subscriptionId);\n await sink.feed(newSubscription.stream);\n } else if (status === \"disconnected\") {\n // Discord disconnected - unsubscribe to clean up\n if (broadcast.isSubscribed(subscriptionId)) {\n logger.debug(\n `[MusicService] Discord disconnected for guild ${guildId}, unsubscribing from broadcast`,\n );\n const sub = broadcast.subscribe(subscriptionId);\n sub.unsubscribe();\n }\n }\n });\n\n logger.debug(\n `[MusicService] Attached status listener for sink ${guildId}`,\n );\n }\n\n // If already connected, subscribe now\n if (sink.status === \"connected\") {\n logger.info(\n `[MusicService] Sink connected, calling doSubscribe for guild ${guildId}`,\n );\n await doSubscribe();\n } else {\n // Not connected yet - listener will handle it when sink connects\n logger.warn(\n `[MusicService] Discord sink not connected for guild ${guildId} (status: ${sink.status}), will subscribe when connected`,\n );\n }\n } catch (error) {\n logger.debug(`[MusicService] Could not auto-subscribe Discord: ${error}`);\n // Non-fatal - old architecture will still work\n }\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport type { Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\nimport type {\n AudioSubscription,\n BroadcastState,\n BroadcastTrackMetadata,\n IAudioBroadcast,\n} from \"../contracts\";\nimport { StreamCore, type TrackMetadata } from \"./streamCore\";\nimport { StreamMultiplexer } from \"./streamMultiplexer\";\n\n/**\n * Broadcast - Implements IAudioBroadcast contract\n *\n * WHY THIS FACADE:\n * Consumers (Discord, Web) shouldn't care about internal complexity (StreamCore,\n * StreamMultiplexer, silence generation, backpressure policies). They just want:\n * - subscribe() → get audio stream\n * - unsubscribe() → stop receiving audio\n * - events → know what's playing\n *\n * This class is the ONLY public interface. Everything else (core components) is internal.\n *\n * ARCHITECTURE LAYERS:\n * ```\n * Consumer Layer: Discord, Web, etc. → call subscribe()\n * ↓\n * Facade Layer: Broadcast (this class) → implements IAudioBroadcast\n * ↓\n * Generation Layer: StreamCore → produces audio (tracks or silence)\n * ↓\n * Distribution Layer: StreamMultiplexer → fans out to N consumers\n * ```\n *\n * WHY WIRE THEM IN CONSTRUCTOR:\n * StreamCore → StreamMultiplexer wiring happens once. The output stream is long-lived\n * (never closes). Tracks come and go, but the pipeline stays connected.\n *\n * This is the public interface that consumers (Discord, Web, etc.) interact with.\n * Internally uses StreamCore for audio generation and StreamMultiplexer for fan-out.\n */\nexport class Broadcast extends EventEmitter implements IAudioBroadcast {\n readonly guildId: string;\n private streamCore: StreamCore;\n private multiplexer: StreamMultiplexer;\n private _state: BroadcastState = \"stopped\";\n\n constructor(guildId: string) {\n super();\n this.guildId = guildId;\n\n // Create internal components\n this.streamCore = new StreamCore();\n this.multiplexer = new StreamMultiplexer({\n policy: \"LIVE_DROP\", // Radio-style: drop frames for slow consumers\n bufferSize: 128 * 1024, // 128KB per consumer\n logDrops: false, // Enable for debugging\n });\n\n // Wire up StreamCore output to Multiplexer\n const coreOutput = this.streamCore.getOutputStream();\n this.multiplexer.setSource(coreOutput);\n\n // Forward state changes from StreamCore\n this.streamCore.on(\"stateChange\", (coreState) => {\n const broadcastState = this.mapCoreStateToBroadcastState(coreState);\n this._state = broadcastState;\n this.emit(\"stateChange\", broadcastState);\n });\n\n // Forward metadata changes from StreamCore\n this.streamCore.on(\"metadata\", (metadata: TrackMetadata) => {\n const broadcastMetadata: BroadcastTrackMetadata = {\n title: metadata.title,\n url: metadata.url,\n duration: metadata.duration,\n requestedBy: metadata.requestedBy,\n };\n this.emit(\"metadata\", broadcastMetadata);\n });\n\n // Forward track:ended event from StreamCore\n // WHY: MusicQueue needs to know when a track finishes to play the next one\n this.streamCore.on(\"track:ended\", (metadata: TrackMetadata) => {\n this.emit(\"track:ended\", metadata);\n });\n\n logger.debug(`[Broadcast] Created broadcast for guild ${guildId}`);\n }\n\n /**\n * Get current broadcast state\n */\n get state(): BroadcastState {\n return this._state;\n }\n\n /**\n * Subscribe to this broadcast\n * @param consumerId Unique identifier for the consumer\n * @returns AudioSubscription with stream and unsubscribe method\n */\n subscribe(consumerId: string): AudioSubscription {\n logger.info(\n `[Broadcast:${this.guildId}] Consumer ${consumerId} subscribing`,\n );\n\n // Get a consumer stream from the multiplexer\n const stream = this.multiplexer.addConsumer(consumerId);\n logger.info(\n `[Broadcast:${this.guildId}] Got stream for ${consumerId}, readable: ${stream?.readable}`,\n );\n\n // Create subscription object\n const subscription: AudioSubscription = {\n consumerId,\n stream,\n unsubscribe: () => {\n logger.debug(\n `[Broadcast:${this.guildId}] Consumer ${consumerId} unsubscribing`,\n );\n this.multiplexer.removeConsumer(consumerId);\n this.emit(\"subscriberRemoved\", consumerId);\n },\n };\n\n this.emit(\"subscriberAdded\", consumerId);\n return subscription;\n }\n\n /**\n * Get current subscriber count\n */\n getSubscriberCount(): number {\n return this.multiplexer.getConsumerCount();\n }\n\n /**\n * Check if a consumer is subscribed\n */\n isSubscribed(consumerId: string): boolean {\n const hasConsumer = this.multiplexer.hasConsumer(consumerId);\n logger.debug(\n `[Broadcast:${this.guildId}] isSubscribed(${consumerId}) = ${hasConsumer}`,\n );\n return hasConsumer;\n }\n\n /**\n * Push a track stream into the broadcast (internal API for MusicQueue)\n * @param stream Audio stream\n * @param metadata Track metadata\n */\n async pushTrack(\n stream: Readable,\n metadata: BroadcastTrackMetadata,\n ): Promise<void> {\n const trackMetadata: TrackMetadata = {\n title: metadata.title,\n url: metadata.url,\n duration: metadata.duration,\n requestedBy: metadata.requestedBy,\n };\n\n await this.streamCore.pushTrackStream(stream, trackMetadata);\n }\n\n /**\n * Start silence generation (internal API)\n */\n startSilence(): void {\n this.streamCore.startSilence();\n }\n\n /**\n * Stop the broadcast (internal API)\n */\n stop(): void {\n logger.debug(`[Broadcast:${this.guildId}] Stopping broadcast`);\n this.streamCore.stop();\n }\n\n /**\n * Destroy the broadcast and clean up resources\n */\n destroy(): void {\n logger.debug(`[Broadcast:${this.guildId}] Destroying broadcast`);\n\n // Stop core\n this.streamCore.stop();\n this.streamCore.destroy();\n\n // Destroy multiplexer\n this.multiplexer.destroy();\n\n // Remove all event listeners\n this.removeAllListeners();\n }\n\n /**\n * Get statistics about the broadcast\n */\n getStats(): {\n state: BroadcastState;\n subscriberCount: number;\n consumerStats: Map<string, { droppedFrames: number; totalFrames: number }>;\n } {\n return {\n state: this._state,\n subscriberCount: this.getSubscriberCount(),\n consumerStats: this.multiplexer.getAllStats(),\n };\n }\n\n /**\n * Map StreamCore state to BroadcastState\n */\n private mapCoreStateToBroadcastState(coreState: string): BroadcastState {\n switch (coreState) {\n case \"playing\":\n return \"live\";\n case \"silence\":\n return \"silence\";\n default:\n return \"stopped\";\n }\n }\n}\n","import type { ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { EventEmitter } from \"node:events\";\nimport { PassThrough, type Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\nimport {\n normalizeOpusBroadcastStream,\n OPUS_NORMALIZE_FFMPEG,\n OPUS_NORMALIZE_INPUT,\n} from \"../utils/opusBroadcastNormalize\";\n\nexport type StreamCoreState = \"stopped\" | \"playing\" | \"silence\";\n\nexport interface TrackMetadata {\n title: string;\n url: string;\n duration?: number;\n requestedBy?: string;\n}\n\ninterface StreamCoreHandlers {\n onEnd: () => void;\n onError: (error: Error) => void;\n}\n\ntype StreamCoreSource = Readable & {\n _streamCoreHandlers?: StreamCoreHandlers;\n};\n\nfunction isWritableStream(value: unknown): value is NodeJS.WritableStream {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as { write?: unknown }).write === \"function\" &&\n typeof (value as { end?: unknown }).end === \"function\"\n );\n}\n\n/**\n * StreamCore - The heart of the audio broadcast system\n *\n * WHY THIS EXISTS:\n * Audio streams have a lifecycle: track plays → track ends → silence → next track.\n * Without explicit silence handling, when the queue empties:\n * - Discord voice connection times out and disconnects\n * - Web listeners get \"connection closed\" errors\n * - Reconnecting users hear nothing until the next track\n *\n * THE SOLUTION:\n * StreamCore ensures the output stream NEVER ends. It continuously emits frames:\n * - When a track is playing → emit track audio frames\n * - When queue is empty → emit silence frames (20ms intervals)\n *\n * This keeps all connections alive (\"dead air\" instead of \"dead connection\").\n *\n * WHY 20MS SILENCE FRAMES:\n * Discord/Opus operates on 20ms frames. Emitting silence at the same rate as\n * real audio makes the transition seamless - consumers don't know the difference\n * between intentional silence and a quiet song.\n *\n * WHY PASSTHROUGH OUTPUT:\n * The output stream never closes - it's piped to the multiplexer which fans it out.\n * We swap the SOURCE (track stream vs silence generator) but keep the output alive.\n *\n * Responsibilities:\n * - Manage the canonical audio stream\n * - Generate silence when no track is playing\n * - Emit consistent PCM/Opus frames\n * - Track broadcast state\n *\n * This is an internal class used by the Broadcast class.\n */\nexport class StreamCore extends EventEmitter {\n private state: StreamCoreState = \"stopped\";\n private currentSource: Readable | null = null;\n private outputStream: PassThrough;\n private silenceInterval: NodeJS.Timeout | null = null;\n private currentMetadata: TrackMetadata | null = null;\n\n // Silence generation\n private readonly SILENCE_FRAME_SIZE = 3840; // 20ms of silence at 48kHz stereo 16-bit PCM\n private readonly SILENCE_INTERVAL_MS = 20; // 20ms frames\n\n constructor() {\n super();\n this.outputStream = new PassThrough({\n highWaterMark: 128 * 1024, // 128KB buffer\n });\n }\n\n /**\n * Get the output stream that emits audio data\n */\n getOutputStream(): PassThrough {\n return this.outputStream;\n }\n\n /**\n * Get current state\n */\n getState(): StreamCoreState {\n return this.state;\n }\n\n /**\n * Get current track metadata\n */\n getCurrentMetadata(): TrackMetadata | null {\n return this.currentMetadata;\n }\n\n /**\n * Push a track stream into the core\n * @param stream Audio stream (PCM or Opus)\n * @param metadata Track metadata\n */\n async pushTrackStream(\n stream: Readable,\n metadata: TrackMetadata,\n ): Promise<void> {\n // Stop silence if playing\n this.stopSilence();\n\n // Clean up old source if exists\n if (this.currentSource) {\n this.detachCurrentSource();\n }\n\n const media = normalizeOpusBroadcastStream(stream);\n this.currentSource = media;\n this.currentMetadata = metadata;\n this.state = \"playing\";\n\n logger.debug(`[StreamCore] Playing track: ${metadata.title}`);\n this.emit(\"stateChange\", this.state);\n this.emit(\"metadata\", metadata);\n\n // Pipe stream to output (possibly via ffmpeg Ogg Opus remux for web clients)\n media.pipe(this.outputStream, { end: false });\n\n // Handle stream events\n const onError = (error: Error) => {\n logger.error(`[StreamCore] Track stream error: ${error.message}`);\n this.handleTrackEnd();\n };\n\n const onEnd = () => {\n logger.debug(`[StreamCore] Track stream ended`);\n this.handleTrackEnd();\n };\n\n media.on(\"error\", onError);\n media.on(\"end\", onEnd);\n media.on(\"close\", onEnd);\n\n // Store handlers for cleanup\n (media as StreamCoreSource)._streamCoreHandlers = { onError, onEnd };\n }\n\n /**\n * Start generating silence frames\n * Used when queue is empty to keep connections alive\n *\n * WHY SILENCE GENERATION:\n * When the queue empties, if we stop emitting audio frames:\n * - Discord voice timeout (30-60s) → disconnects\n * - Web listeners get EOF → connection closes\n * - Users have to manually reconnect\n *\n * Instead, we emit silence frames (zeros) at the same rate as real audio.\n * To Discord and browsers, it's just a quiet moment, not a closed stream.\n *\n * WHY 20MS INTERVALS:\n * Discord/Opus operates on 20ms frames (960 samples at 48kHz).\n * Matching this interval makes transitions seamless:\n * Track ends → 20ms silence → 20ms silence → new track starts\n *\n * No gap, no pop, no reconnection needed.\n *\n * WHY BUFFER SIZE 3840:\n * 48kHz sample rate × 2 channels (stereo) × 2 bytes (16-bit) × 0.02s = 3840 bytes\n * This is one frame of PCM audio. We allocate zeros to create silence.\n */\n startSilence(): void {\n if (this.silenceInterval) {\n return; // Already generating silence\n }\n\n this.state = \"silence\";\n logger.debug(\"[StreamCore] Starting silence generation\");\n this.emit(\"stateChange\", this.state);\n\n // Generate silence frames at regular intervals\n this.silenceInterval = setInterval(() => {\n const silenceFrame = Buffer.alloc(this.SILENCE_FRAME_SIZE);\n\n // Try to write silence frame\n // WHY CHECK destroyed/writableEnded:\n // If output stream is destroyed (service stopping), don't write.\n // Prevents errors during shutdown.\n if (!this.outputStream.destroyed && !this.outputStream.writableEnded) {\n this.outputStream.write(silenceFrame);\n }\n }, this.SILENCE_INTERVAL_MS);\n\n // Emit metadata indicating silence\n this.currentMetadata = {\n title: \"(Silence - Queue Empty)\",\n url: \"\",\n };\n this.emit(\"metadata\", this.currentMetadata);\n }\n\n /**\n * Stop generating silence frames\n */\n stopSilence(): void {\n if (this.silenceInterval) {\n clearInterval(this.silenceInterval);\n this.silenceInterval = null;\n logger.debug(\"[StreamCore] Stopped silence generation\");\n }\n }\n\n /**\n * Stop the stream core completely\n */\n stop(): void {\n logger.debug(\"[StreamCore] Stopping stream core\");\n\n this.stopSilence();\n this.detachCurrentSource();\n\n this.state = \"stopped\";\n this.currentMetadata = null;\n this.emit(\"stateChange\", this.state);\n\n // Don't end the output stream - keep it open for reconnections\n // Just stop feeding it data\n }\n\n /**\n * Handle track end (switch to silence or wait for next track)\n */\n private handleTrackEnd(): void {\n // Emit track:ended event before cleanup\n // WHY: MusicQueue listens for this to know when to play the next track\n this.emit(\"track:ended\", this.currentMetadata);\n\n this.detachCurrentSource();\n\n // DO NOT auto-start silence generation\n // WHY: Track audio is opus-encoded, but silence frames are raw PCM.\n // Writing PCM to an opus stream corrupts it, causing Discord to fail/stop early.\n // The MusicQueue is waiting for VoiceManager's audio:finished event, which fires\n // when Discord actually finishes playing (based on the buffered opus data).\n // Silence generation is only useful for web streaming where we control the format.\n //\n // For web streaming, call startSilence() explicitly from the orchestrator layer\n // AFTER ensuring format consistency (e.g., transcoding all audio to PCM first).\n this.state = \"stopped\";\n this.emit(\"stateChange\", this.state);\n }\n\n /**\n * Detach and clean up current source stream\n */\n private detachCurrentSource(): void {\n if (!this.currentSource) {\n return;\n }\n\n const src = this.currentSource;\n const ff = (\n src as { [OPUS_NORMALIZE_FFMPEG]?: ChildProcessWithoutNullStreams }\n )[OPUS_NORMALIZE_FFMPEG];\n const upstream = (src as { [OPUS_NORMALIZE_INPUT]?: Readable })[\n OPUS_NORMALIZE_INPUT\n ];\n\n src.unpipe(this.outputStream);\n\n const handlers = (\n src as {\n _streamCoreHandlers?: {\n onError: (e: Error) => void;\n onEnd: () => void;\n };\n }\n )._streamCoreHandlers;\n if (handlers) {\n src.removeListener(\"error\", handlers.onError);\n src.removeListener(\"end\", handlers.onEnd);\n src.removeListener(\"close\", handlers.onEnd);\n delete (src as { _streamCoreHandlers?: unknown })._streamCoreHandlers;\n }\n\n if (ff) {\n try {\n if (upstream) {\n upstream.unpipe(ff.stdin);\n }\n } catch {\n /* ignore */\n }\n if (!ff.killed) {\n ff.kill(\"SIGKILL\");\n }\n try {\n if (upstream && !upstream.destroyed) {\n upstream.destroy();\n }\n } catch {\n /* ignore */\n }\n } else if (upstream) {\n try {\n if (isWritableStream(src)) {\n upstream.unpipe(src);\n }\n } catch {\n /* ignore */\n }\n try {\n if (!upstream.destroyed) {\n upstream.destroy();\n }\n } catch {\n /* ignore */\n }\n }\n\n if (!src.destroyed) {\n src.destroy();\n }\n\n this.currentSource = null;\n }\n\n /**\n * Destroy the stream core and clean up all resources\n */\n destroy(): void {\n logger.debug(\"[StreamCore] Destroying stream core\");\n\n this.stopSilence();\n this.detachCurrentSource();\n\n if (!this.outputStream.destroyed) {\n this.outputStream.end();\n this.outputStream.destroy();\n }\n\n this.removeAllListeners();\n }\n}\n","import { type ChildProcessWithoutNullStreams, spawn } from \"node:child_process\";\nimport { PassThrough, type Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\nimport {\n augmentEnvWithFfmpegTools,\n resolveFfmpegBinaryPath,\n} from \"./ffmpegEnv\";\nimport { formatMusicDebugCommand, musicDebug } from \"./musicDebug\";\n\n/** Attached to ffmpeg stdout when normalize is active. */\nexport const OPUS_NORMALIZE_FFMPEG = Symbol(\"elizaOpusNormalizeFfmpeg\");\n/** Upstream readable fed into ffmpeg stdin. */\nexport const OPUS_NORMALIZE_INPUT = Symbol(\"elizaOpusNormalizeInput\");\n\n/** Ogg page sync: \"OggS\" */\nfunction isOggOpusPrefix(buf: Buffer): boolean {\n return (\n buf.length >= 4 &&\n buf[0] === 0x4f &&\n buf[1] === 0x67 &&\n buf[2] === 0x67 &&\n buf[3] === 0x53\n );\n}\n\n/** WebM/Matroska EBML header */\nfunction isWebmPrefix(buf: Buffer): boolean {\n return (\n buf.length >= 4 &&\n buf[0] === 0x1a &&\n buf[1] === 0x45 &&\n buf[2] === 0xdf &&\n buf[3] === 0xa3\n );\n}\n\nfunction isBroadcastNormalizeEnabled(): boolean {\n const v = process.env.ELIZA_MUSIC_BROADCAST_NORMALIZE?.trim().toLowerCase();\n if (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") {\n return false;\n }\n return true;\n}\n\n/**\n * Spawn ffmpeg to:\n * 1. `-re` — read input at native frame rate (real-time throttle)\n * 2. `-c:a copy -f opus` — remux to Ogg Opus without re-encoding\n *\n * This keeps the broadcast alive for the full track duration instead of\n * dumping file data in a burst. `-re` is how Icecast/Shoutcast relays work.\n */\nfunction spawnFfmpegRealtimeRelay(\n bufferedPrefix: Buffer,\n source: Readable,\n output: PassThrough,\n inputFormat: string | null,\n): void {\n const ffmpegPath = resolveFfmpegBinaryPath();\n const args: string[] = [\"-hide_banner\", \"-loglevel\", \"error\", \"-re\"];\n if (inputFormat) {\n args.push(\"-f\", inputFormat);\n }\n args.push(\"-i\", \"pipe:0\", \"-vn\", \"-c:a\", \"copy\", \"-f\", \"ogg\", \"pipe:1\");\n\n let ff: ChildProcessWithoutNullStreams;\n try {\n ff = spawn(ffmpegPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: augmentEnvWithFfmpegTools(),\n }) as ChildProcessWithoutNullStreams;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n musicDebug(\"broadcast ffmpeg spawn threw\", {\n command: formatMusicDebugCommand(ffmpegPath, args),\n error: msg,\n });\n logger.warn(\n `[music] ffmpeg broadcast relay spawn failed (${msg}); falling back to raw pipe`,\n );\n if (bufferedPrefix.length > 0) {\n output.write(bufferedPrefix);\n }\n source.pipe(output);\n (output as Readable & { [OPUS_NORMALIZE_INPUT]?: Readable })[\n OPUS_NORMALIZE_INPUT\n ] = source;\n return;\n }\n\n musicDebug(\"broadcast ffmpeg -re relay spawn\", {\n command: formatMusicDebugCommand(ffmpegPath, args),\n inputFormat: inputFormat ?? \"auto-probe\",\n });\n\n let stderrAcc = \"\";\n ff.stderr?.on(\"data\", (ch: Buffer) => {\n stderrAcc += ch.toString();\n });\n\n ff.on(\"error\", (err) => {\n logger.error(`[music] ffmpeg relay error: ${err.message}`);\n musicDebug(\"broadcast ffmpeg relay error\", {\n message: err.message,\n command: formatMusicDebugCommand(ffmpegPath, args),\n stderr: stderrAcc,\n });\n if (!source.destroyed) {\n source.destroy();\n }\n if (!output.destroyed) {\n output.destroy(err);\n }\n });\n\n ff.on(\"exit\", (code, signal) => {\n if (signal === \"SIGKILL\") {\n return;\n }\n const stderr = stderrAcc.trimEnd();\n if (code !== 0 && code !== null) {\n const tail = stderr.slice(-400);\n logger.warn(\n `[music] ffmpeg relay exited code=${code}: ${tail || \"(no stderr)\"}`,\n );\n }\n if ((code !== 0 && code !== null) || stderr.length > 0) {\n musicDebug(\"broadcast ffmpeg relay exit\", {\n code,\n signal: signal ?? undefined,\n command: formatMusicDebugCommand(ffmpegPath, args),\n stderr: stderrAcc,\n });\n }\n });\n\n (\n output as Readable & {\n [OPUS_NORMALIZE_FFMPEG]?: ChildProcessWithoutNullStreams;\n [OPUS_NORMALIZE_INPUT]?: Readable;\n }\n )[OPUS_NORMALIZE_FFMPEG] = ff;\n (output as Readable & { [OPUS_NORMALIZE_INPUT]?: Readable })[\n OPUS_NORMALIZE_INPUT\n ] = source;\n\n if (bufferedPrefix.length > 0) {\n ff.stdin.write(bufferedPrefix);\n }\n source.pipe(ff.stdin);\n ff.stdin.on(\"error\", () => {\n /* EPIPE when upstream ends is normal */\n });\n\n ff.stdout.pipe(output);\n ff.stdout.on(\"end\", () => {\n if (!output.writableEnded) {\n output.end();\n }\n });\n ff.stdout.on(\"error\", (err: Error) => {\n if (!output.destroyed) {\n output.destroy(err);\n }\n });\n}\n\n/**\n * Normalize + throttle audio for the broadcast pipeline.\n *\n * Every source — Ogg Opus (yt-dlp cache/temp) **and** WebM/Opus (play-dl) — is\n * piped through `ffmpeg -re -c:a copy -f opus pipe:1`. The `-re` flag makes ffmpeg\n * read the input at native frame rate, so the broadcast emits data at real-time\n * playback speed instead of dumping the whole file in a burst.\n *\n * Without `-re`, file-backed streams (e.g. a 52-second cached Opus) complete in\n * milliseconds of wall time, leaving HTTP subscribers with a data burst they can't\n * decode as a live stream and causing `[StreamCore] Track stream ended` immediately.\n *\n * Disable with `ELIZA_MUSIC_BROADCAST_NORMALIZE=0` if you need raw passthrough\n * (only useful for Discord-only setups where the voiceManager controls pacing).\n */\nexport function normalizeOpusBroadcastStream(source: Readable): Readable {\n if (!isBroadcastNormalizeEnabled()) {\n return source;\n }\n\n const output = new PassThrough({ highWaterMark: 128 * 1024 });\n const chunks: Buffer[] = [];\n let decided = false;\n\n const cleanupListeners = (): void => {\n source.removeListener(\"data\", onData);\n source.removeListener(\"end\", onEnd);\n source.removeListener(\"error\", onErr);\n };\n\n const onData = (chunk: Buffer): void => {\n if (decided) {\n return;\n }\n chunks.push(chunk);\n const buf = Buffer.concat(chunks);\n if (buf.length < 4) {\n return;\n }\n\n decided = true;\n cleanupListeners();\n\n const inputFormat = isOggOpusPrefix(buf)\n ? \"ogg\"\n : isWebmPrefix(buf)\n ? \"webm\"\n : null;\n\n musicDebug(\"broadcast normalize relay\", {\n inputFormat: inputFormat ?? \"auto-probe\",\n prefixBytes: buf.length,\n });\n\n spawnFfmpegRealtimeRelay(buf, source, output, inputFormat);\n };\n\n const onEnd = (): void => {\n if (decided) {\n return;\n }\n decided = true;\n cleanupListeners();\n const buf = Buffer.concat(chunks);\n musicDebug(\"broadcast normalize EOF before 4-byte magic\", {\n buffered: buf.length,\n });\n if (buf.length > 0) {\n output.write(buf);\n }\n if (!output.writableEnded) {\n output.end();\n }\n (output as Readable & { [OPUS_NORMALIZE_INPUT]?: Readable })[\n OPUS_NORMALIZE_INPUT\n ] = source;\n };\n\n const onErr = (err: Error): void => {\n if (decided) {\n return;\n }\n decided = true;\n cleanupListeners();\n output.destroy(err);\n };\n\n source.on(\"data\", onData);\n source.on(\"end\", onEnd);\n source.on(\"error\", onErr);\n\n queueMicrotask(() => {\n if (source.readableEnded && !decided) {\n onEnd();\n }\n });\n\n return output;\n}\n","import { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { delimiter, dirname } from \"node:path\";\n\nconst require = createRequire(import.meta.url);\n\nfunction tryFfmpegStaticPath(): string | null {\n try {\n const p = require(\"ffmpeg-static\") as unknown;\n if (typeof p === \"string\" && p.length > 0 && existsSync(p)) return p;\n } catch {\n /* optional */\n }\n return null;\n}\n\nfunction tryFfprobeStaticPath(): string | null {\n try {\n const mod = require(\"ffprobe-static\") as { path?: string } | string;\n const p = typeof mod === \"string\" ? mod : mod?.path;\n if (typeof p === \"string\" && p.length > 0 && existsSync(p)) return p;\n } catch {\n /* optional */\n }\n return null;\n}\n\n/** Prefer FFMPEG_PATH, then ffmpeg-static, then plain `ffmpeg` on PATH. */\nexport function resolveFfmpegBinaryPath(): string {\n const env = process.env.FFMPEG_PATH?.trim();\n if (env && existsSync(env)) {\n return env;\n }\n const staticFfmpeg = tryFfmpegStaticPath();\n if (staticFfmpeg) {\n return staticFfmpeg;\n }\n return \"ffmpeg\";\n}\n\n/** Prefer FFPROBE_PATH, then ffprobe-static, then plain `ffprobe` on PATH. */\nexport function resolveFfprobeBinaryPath(): string {\n const env = process.env.FFPROBE_PATH?.trim();\n if (env && existsSync(env)) {\n return env;\n }\n return tryFfprobeStaticPath() ?? \"ffprobe\";\n}\n\n/**\n * Directories to prepend to PATH so yt-dlp finds `ffmpeg` and `ffprobe`.\n *\n * Resolution order:\n * - `FFMPEG_LOCATION` — directory containing both binaries (yt-dlp `--ffmpeg-location` style)\n * - `FFMPEG_PATH` / `FFPROBE_PATH` — explicit binary paths; their parent dirs are added\n * - `ffmpeg-static` / `ffprobe-static` npm packages (prebuilt binaries per platform)\n */\nexport function getFfmpegToolSearchDirs(): string[] {\n const dirs = new Set<string>();\n\n const loc = process.env.FFMPEG_LOCATION?.trim();\n if (loc && existsSync(loc)) {\n dirs.add(loc);\n }\n\n const ffmpegEnv = process.env.FFMPEG_PATH?.trim();\n if (ffmpegEnv && existsSync(ffmpegEnv)) {\n dirs.add(dirname(ffmpegEnv));\n }\n\n const ffprobeEnv = process.env.FFPROBE_PATH?.trim();\n if (ffprobeEnv && existsSync(ffprobeEnv)) {\n dirs.add(dirname(ffprobeEnv));\n }\n\n const staticFfmpeg = tryFfmpegStaticPath();\n if (staticFfmpeg) {\n dirs.add(dirname(staticFfmpeg));\n }\n\n const staticFfprobe = tryFfprobeStaticPath();\n if (staticFfprobe) {\n dirs.add(dirname(staticFfprobe));\n }\n\n return [...dirs];\n}\n\n/** Merge ffmpeg/ffprobe dirs into PATH (and `Path` on Windows). */\nexport function augmentEnvWithFfmpegTools(\n base: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const toolDirs = getFfmpegToolSearchDirs();\n if (toolDirs.length === 0) {\n return { ...base };\n }\n\n const prefix = toolDirs.join(delimiter);\n const currentPath = base.PATH ?? base.Path ?? \"\";\n const merged = currentPath ? `${prefix}${delimiter}${currentPath}` : prefix;\n\n const out: NodeJS.ProcessEnv = { ...base, PATH: merged };\n if (process.platform === \"win32\") {\n out.Path = merged;\n }\n return out;\n}\n","import { logger } from \"@elizaos/core\";\n\n/** Set `ELIZA_MUSIC_DEBUG=1` for verbose music-player / cache / stream logs. */\nexport function isMusicDebugEnabled(): boolean {\n const v = process.env.ELIZA_MUSIC_DEBUG?.trim().toLowerCase();\n return v === \"1\" || v === \"true\" || v === \"yes\";\n}\n\n/** Max chars for stderr/stdout embedded in debug JSON (avoid huge log lines). */\nconst MUSIC_DEBUG_MAX_CLI_CHARS = 16_384;\n\nfunction truncateCliText(s: string): string {\n const t = s.trimEnd();\n if (t.length <= MUSIC_DEBUG_MAX_CLI_CHARS) {\n return t;\n }\n return `${t.slice(0, MUSIC_DEBUG_MAX_CLI_CHARS)}…[truncated ${t.length - MUSIC_DEBUG_MAX_CLI_CHARS} chars]`;\n}\n\nfunction sanitizeMeta(meta: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = { ...meta };\n for (const key of [\"stderr\", \"stdout\"] as const) {\n const v = out[key];\n if (typeof v === \"string\" && v.length > 0) {\n out[key] = truncateCliText(v);\n }\n }\n for (const key of [\"command\", \"cmdline\"] as const) {\n const v = out[key];\n if (typeof v === \"string\" && v.length > MUSIC_DEBUG_MAX_CLI_CHARS) {\n out[key] = truncateCliText(v);\n }\n }\n return out;\n}\n\n/** Pretty-print argv for debug logs (quote args that need it). */\nexport function formatMusicDebugCommand(\n bin: string,\n args: readonly string[],\n): string {\n return [\n bin,\n ...args.map((a) => (/[\\s\"'\\\\]/.test(a) ? JSON.stringify(a) : a)),\n ].join(\" \");\n}\n\nexport function musicDebug(\n message: string,\n meta?: Record<string, unknown>,\n): void {\n if (!isMusicDebugEnabled()) {\n return;\n }\n if (meta && Object.keys(meta).length > 0) {\n logger.debug(\n `[eliza][music] ${message} ${JSON.stringify(sanitizeMeta(meta))}`,\n );\n } else {\n logger.debug(`[eliza][music] ${message}`);\n }\n}\n","import { PassThrough, type Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\n\n/**\n * Stream Multiplexer - The Backpressure Problem Solver\n *\n * THE PROBLEM:\n * Node.js streams have backpressure - if a consumer can't keep up, the producer slows down.\n * This is great for file copying, but terrible for live audio:\n *\n * Discord: reading at 128kbps ✓\n * Web Client 1: reading at 128kbps ✓\n * Web Client 2: reading at 10kbps (slow connection) ✗\n *\n * Without this multiplexer, Client 2's slow connection would cause ALL consumers\n * (including Discord) to receive choppy audio.\n *\n * THE SOLUTION:\n * Each consumer gets an independent PassThrough stream. The multiplexer writes to all\n * consumers simultaneously. If a consumer's buffer is full (backpressure), we don't wait -\n * we drop that frame for THAT consumer only. Discord keeps playing smoothly.\n *\n * WHY THREE POLICIES:\n * - LIVE_DROP: For radio (default) - if you can't keep up, tough luck, stay live\n * - BUFFER_THEN_DROP: For tolerance - give slow clients a chance, then drop\n * - BLOCKING: For recording - accuracy matters more than latency\n */\n\n/**\n * Consumer backpressure policy\n * - LIVE_DROP: Drop frames for slow consumers (radio-style, keeps broadcast live)\n * - BUFFER_THEN_DROP: Buffer some frames, then drop if still slow\n * - BLOCKING: Block source if consumer can't keep up (for recording)\n */\nexport type BackpressurePolicy = \"LIVE_DROP\" | \"BUFFER_THEN_DROP\" | \"BLOCKING\";\n\nexport interface StreamMultiplexerOptions {\n /** Backpressure policy (default: LIVE_DROP) */\n policy?: BackpressurePolicy;\n\n /** Buffer size per consumer in bytes (default: 64KB) */\n bufferSize?: number;\n\n /** Log dropped frames (default: false) */\n logDrops?: boolean;\n}\n\ninterface Consumer {\n id: string;\n stream: PassThrough;\n droppedFrames: number;\n totalFrames: number;\n /** True once the Ogg header has been written to this consumer. */\n headerSent: boolean;\n}\n\n/**\n * StreamMultiplexer - Duplicates a single source stream to multiple consumers\n * with non-blocking behavior to protect the broadcast from slow consumers.\n *\n * Key features:\n * - Source writes to all consumers independently\n * - Slow consumers don't block source or other consumers\n * - Configurable backpressure policies\n * - Per-consumer statistics tracking\n */\nexport class StreamMultiplexer {\n private consumers: Map<string, Consumer> = new Map();\n private source: Readable | null = null;\n private policy: BackpressurePolicy;\n private bufferSize: number;\n private logDrops: boolean;\n private isActive = false;\n private sourceErrorHandler: ((error: Error) => void) | null = null;\n private sourceEndHandler: (() => void) | null = null;\n\n /**\n * Ogg header cache: the first N bytes containing OpusHead + OpusTags pages.\n * Every Ogg Opus stream begins with these two mandatory pages. Late-joining\n * subscribers need them to initialise the decoder; without them the browser\n * reports MEDIA_ERR_DECODE.\n */\n private oggHeaderBuf: Buffer | null = null;\n private oggHeaderComplete = false;\n private oggPagesSeen = 0;\n\n constructor(options: StreamMultiplexerOptions = {}) {\n this.policy = options.policy || \"LIVE_DROP\";\n this.bufferSize = options.bufferSize || 64 * 1024; // 64KB default\n this.logDrops = options.logDrops || false;\n }\n\n /**\n * Set the source stream to multiplex\n */\n setSource(stream: Readable): void {\n // Clean up old source if exists\n if (this.source) {\n this.detachSource();\n }\n\n this.source = stream;\n this.isActive = true;\n\n // Reset Ogg header cache for the new source\n this.oggHeaderBuf = null;\n this.oggHeaderComplete = false;\n this.oggPagesSeen = 0;\n\n // Handle source data\n stream.on(\"data\", (chunk: Buffer) => {\n this.accumulateOggHeaders(chunk);\n this.handleChunk(chunk);\n });\n\n // Handle source errors\n this.sourceErrorHandler = (error: Error) => {\n logger.error(`[StreamMultiplexer] Source error: ${error.message}`);\n this.handleSourceEnd();\n };\n stream.on(\"error\", this.sourceErrorHandler);\n\n // Handle source end\n this.sourceEndHandler = () => {\n this.handleSourceEnd();\n };\n stream.on(\"end\", this.sourceEndHandler);\n stream.on(\"close\", this.sourceEndHandler);\n }\n\n /**\n * Add a consumer to receive multiplexed stream\n * @returns PassThrough stream for the consumer\n */\n addConsumer(id: string): PassThrough {\n const existingConsumer = this.consumers.get(id);\n if (existingConsumer) {\n logger.warn(\n `[StreamMultiplexer] Consumer ${id} already exists, returning existing stream`,\n );\n return existingConsumer.stream;\n }\n\n const stream = new PassThrough({\n highWaterMark: this.bufferSize,\n });\n\n const consumer: Consumer = {\n id,\n stream,\n droppedFrames: 0,\n totalFrames: 0,\n headerSent: false,\n };\n\n this.consumers.set(id, consumer);\n logger.debug(\n `[StreamMultiplexer] Added consumer: ${id} (total: ${this.consumers.size})`,\n );\n\n // Replay cached Ogg headers so late joiners can initialise the decoder.\n if (this.oggHeaderBuf && this.oggHeaderComplete) {\n stream.write(this.oggHeaderBuf);\n consumer.headerSent = true;\n logger.debug(\n `[StreamMultiplexer] Replayed ${this.oggHeaderBuf.length}-byte Ogg header to ${id}`,\n );\n }\n\n // Handle consumer errors and cleanup\n stream.on(\"error\", (error) => {\n logger.debug(\n `[StreamMultiplexer] Consumer ${id} error: ${error.message}`,\n );\n this.removeConsumer(id);\n });\n\n stream.on(\"close\", () => {\n logger.debug(`[StreamMultiplexer] Consumer ${id} closed`);\n this.removeConsumer(id);\n });\n\n return stream;\n }\n\n /**\n * Remove a consumer\n */\n removeConsumer(id: string): void {\n const consumer = this.consumers.get(id);\n if (!consumer) {\n return;\n }\n\n // Log stats if frames were dropped\n if (consumer.droppedFrames > 0) {\n const dropRate = (\n (consumer.droppedFrames / consumer.totalFrames) *\n 100\n ).toFixed(2);\n logger.info(\n `[StreamMultiplexer] Consumer ${id} stats: ${consumer.droppedFrames}/${consumer.totalFrames} frames dropped (${dropRate}%)`,\n );\n }\n\n // End and destroy the stream\n if (!consumer.stream.destroyed) {\n consumer.stream.end();\n consumer.stream.destroy();\n }\n\n this.consumers.delete(id);\n logger.debug(\n `[StreamMultiplexer] Removed consumer: ${id} (remaining: ${this.consumers.size})`,\n );\n }\n\n /**\n * Get current consumer count\n */\n getConsumerCount(): number {\n return this.consumers.size;\n }\n\n /**\n * Check if a consumer exists\n */\n hasConsumer(id: string): boolean {\n return this.consumers.has(id);\n }\n\n /**\n * Get stats for a specific consumer\n */\n getConsumerStats(\n id: string,\n ): { droppedFrames: number; totalFrames: number } | null {\n const consumer = this.consumers.get(id);\n if (!consumer) {\n return null;\n }\n return {\n droppedFrames: consumer.droppedFrames,\n totalFrames: consumer.totalFrames,\n };\n }\n\n /**\n * Get stats for all consumers\n */\n getAllStats(): Map<string, { droppedFrames: number; totalFrames: number }> {\n const stats = new Map();\n for (const [id, consumer] of this.consumers.entries()) {\n stats.set(id, {\n droppedFrames: consumer.droppedFrames,\n totalFrames: consumer.totalFrames,\n });\n }\n return stats;\n }\n\n /**\n * Accumulate the first two Ogg pages (OpusHead + OpusTags) so they can be\n * replayed to late-joining subscribers. Scans for the \"OggS\" sync pattern\n * to count page boundaries. After seeing two complete pages, stops.\n */\n private accumulateOggHeaders(chunk: Buffer): void {\n if (this.oggHeaderComplete) return;\n\n if (!this.oggHeaderBuf) {\n this.oggHeaderBuf = chunk;\n } else {\n this.oggHeaderBuf = Buffer.concat([this.oggHeaderBuf, chunk]);\n }\n\n // Count Ogg pages in the accumulated buffer.\n // Each page starts with \"OggS\" (0x4f 0x67 0x67 0x53).\n let pages = 0;\n for (let i = 0; i < this.oggHeaderBuf.length - 3; i++) {\n if (\n this.oggHeaderBuf[i] === 0x4f &&\n this.oggHeaderBuf[i + 1] === 0x67 &&\n this.oggHeaderBuf[i + 2] === 0x67 &&\n this.oggHeaderBuf[i + 3] === 0x53\n ) {\n pages++;\n if (pages === 3) {\n // Third page starts here — header is everything before it\n this.oggHeaderBuf = this.oggHeaderBuf.subarray(0, i);\n this.oggHeaderComplete = true;\n this.oggPagesSeen = 2;\n logger.debug(\n `[StreamMultiplexer] Cached Ogg header (${this.oggHeaderBuf.length} bytes, 2 pages)`,\n );\n return;\n }\n }\n }\n this.oggPagesSeen = Math.max(this.oggPagesSeen, pages);\n\n // Safety: if we've accumulated >64KB without finding 3 pages, give up\n if (this.oggHeaderBuf.length > 64 * 1024) {\n this.oggHeaderComplete = true;\n this.oggHeaderBuf = null;\n logger.warn(\n \"[StreamMultiplexer] Could not detect Ogg header pages; header replay disabled\",\n );\n }\n }\n\n /**\n * Handle incoming chunk from source\n *\n * WHY THIS IS CRITICAL:\n * This is where backpressure isolation happens. Standard stream.pipe() would block\n * if ANY consumer can't keep up. We intentionally don't use pipe() - we manually\n * write to each consumer and handle backpressure per-consumer.\n *\n * THE ALGORITHM:\n * For each chunk from source:\n * For each consumer:\n * Try to write chunk\n * If consumer's buffer is full (returns false):\n * - LIVE_DROP: Drop frame immediately (radio-style)\n * - BUFFER_THEN_DROP: Drop only if buffer >90% full\n * - BLOCKING: Wait (not implemented fully - would need async)\n *\n * WHY TRACK STATS:\n * Knowing drop rates helps debug. If Discord shows 50% drops, it's a Discord\n * voice quality issue, not our code. If web client shows 50% drops, they have\n * a slow connection - that's expected and acceptable.\n */\n private handleChunk(chunk: Buffer): void {\n if (this.consumers.size === 0) {\n // No consumers, drop the chunk\n // WHY: Broadcast continues even with zero listeners (radio keeps broadcasting)\n return;\n }\n\n for (const consumer of this.consumers.values()) {\n consumer.totalFrames++;\n\n switch (this.policy) {\n case \"LIVE_DROP\":\n // Try to write, if backpressure, drop the frame\n if (!consumer.stream.write(chunk)) {\n consumer.droppedFrames++;\n if (this.logDrops) {\n logger.debug(\n `[StreamMultiplexer] Dropped frame for consumer ${consumer.id} (backpressure)`,\n );\n }\n }\n break;\n\n case \"BUFFER_THEN_DROP\":\n // Try to write, if backpressure and buffer full, drop\n if (!consumer.stream.write(chunk)) {\n // Check if buffer is full\n const bufferLength = consumer.stream.writableLength;\n if (bufferLength >= this.bufferSize * 0.9) {\n // Buffer > 90% full, start dropping\n consumer.droppedFrames++;\n if (this.logDrops) {\n logger.debug(\n `[StreamMultiplexer] Dropped frame for consumer ${consumer.id} (buffer full)`,\n );\n }\n }\n }\n break;\n\n case \"BLOCKING\":\n // Wait for drain before continuing (blocks source)\n if (!consumer.stream.write(chunk)) {\n // In blocking mode, we'd need to pause source\n // This is complex, so for now just log and continue\n logger.warn(\n `[StreamMultiplexer] Consumer ${consumer.id} causing backpressure in BLOCKING mode`,\n );\n }\n break;\n }\n }\n }\n\n /**\n * Handle source stream end\n */\n private handleSourceEnd(): void {\n logger.debug(\"[StreamMultiplexer] Source stream ended\");\n this.isActive = false;\n\n // End all consumer streams\n for (const consumer of this.consumers.values()) {\n if (!consumer.stream.destroyed) {\n consumer.stream.end();\n }\n }\n\n this.detachSource();\n }\n\n /**\n * Detach and clean up source stream handlers\n */\n private detachSource(): void {\n if (!this.source) {\n return;\n }\n\n if (this.sourceErrorHandler) {\n this.source.removeListener(\"error\", this.sourceErrorHandler);\n this.sourceErrorHandler = null;\n }\n\n if (this.sourceEndHandler) {\n this.source.removeListener(\"end\", this.sourceEndHandler);\n this.source.removeListener(\"close\", this.sourceEndHandler);\n this.sourceEndHandler = null;\n }\n\n this.source = null;\n }\n\n /**\n * Clean up all resources\n */\n destroy(): void {\n logger.debug(\"[StreamMultiplexer] Destroying multiplexer\");\n\n // Remove all consumers\n const consumerIds = Array.from(this.consumers.keys());\n for (const id of consumerIds) {\n this.removeConsumer(id);\n }\n\n // Detach source\n this.detachSource();\n\n this.isActive = false;\n }\n\n /**\n * Get active status\n */\n isStreamActive(): boolean {\n return this.isActive;\n }\n}\n","import { EventEmitter } from \"node:events\";\nimport { PassThrough, type Readable } from \"node:stream\";\nimport { type IAgentRuntime, logger } from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport type { Broadcast } from \"./core/broadcast\";\nimport type { AudioCacheService } from \"./services/audioCache\";\nimport { createAudioStream } from \"./utils/streamFallback\";\n\ninterface VoicePlaybackHandle {\n finished?: Promise<void>;\n}\n\n/** Minimal voice pipeline from plugin-discord (avoids hard dependency here). */\nexport interface VoiceManagerLike {\n stopAudio(guildId: string, channel: number): Promise<void>;\n pauseAudio(guildId: string, channel: number): Promise<void>;\n resumeAudio(guildId: string, channel: number): Promise<void>;\n isPlaying(guildId: string, channel: number): Promise<boolean>;\n on(\n event: \"audio:finished\",\n handler: (data: { guildId: string; channel: number }) => void,\n ): void;\n off(\n event: \"audio:finished\",\n handler: (data: { guildId: string; channel: number }) => void,\n ): void;\n playAudio(\n stream: Readable,\n options: {\n guildId: string;\n channel: number;\n volume?: number;\n metadata?: Record<string, unknown>;\n },\n ): Promise<VoicePlaybackHandle>;\n}\n\ninterface DiscordServiceLike {\n voiceManager?: VoiceManagerLike;\n}\n\n/**\n * Represents a track in the music queue\n */\nexport interface QueuedTrack {\n id: string;\n url: string;\n title: string;\n duration?: number;\n requestedBy?: string;\n dedicatedTo?: string;\n dedicationMessage?: string;\n addedAt: number;\n stream?: Readable;\n audioMetadata?: {\n bitrate?: number;\n format?: string;\n sampleRate?: number;\n channels?: number;\n size?: number;\n };\n}\n\n/**\n * Options for cross-fading between tracks\n */\nexport interface CrossFadeOptions {\n enabled: boolean;\n duration: number; // Duration in milliseconds (default: 3000ms)\n}\n\n/**\n * Events emitted by the music queue\n */\nexport interface MusicQueueEvents {\n \"track:starting\": (track: QueuedTrack) => void;\n \"track:started\": (track: QueuedTrack) => void;\n \"track:finished\": (track: QueuedTrack, duration: number) => void;\n \"track:error\": (track: QueuedTrack, reason: string) => void;\n}\n\n/**\n * Music queue manager with cross-fading support\n */\nexport class MusicQueue extends EventEmitter {\n private queue: QueuedTrack[] = [];\n private currentTrack: QueuedTrack | null = null;\n private currentPlaybackHandle: VoicePlaybackHandle | null = null;\n private isPlaying = false;\n private isPaused = false;\n private guildId: string;\n private voiceManager: VoiceManagerLike | null;\n private crossFadeOptions: CrossFadeOptions = {\n enabled: true,\n duration: 3000,\n };\n private nextTrackPreBuffered: QueuedTrack | null = null;\n private readonly MUSIC_CHANNEL = 1;\n\n // Continuous stream for multiplexing\n private continuousStream: PassThrough | null = null;\n private nextTrackStream: Readable | null = null;\n private preBufferTimeout: NodeJS.Timeout | null = null;\n private trackPlayedCallback:\n | ((track: QueuedTrack, duration: number) => Promise<void>)\n | null = null;\n private audioCache: AudioCacheService | null;\n private runtime: IAgentRuntime | null = null;\n private broadcast: Broadcast | null = null; // Reference to broadcast for new architecture\n\n constructor(\n guildId: string,\n voiceManager: VoiceManagerLike | null,\n runtime?: IAgentRuntime,\n audioCache?: AudioCacheService,\n ) {\n super();\n this.guildId = guildId;\n this.voiceManager = voiceManager;\n this.audioCache = audioCache || null;\n this.runtime = runtime || null;\n this.continuousStream = new PassThrough();\n }\n\n /**\n * Set the broadcast for this queue (new architecture)\n * @param broadcast Broadcast instance to push audio to\n */\n setBroadcast(broadcast: Broadcast): void {\n this.broadcast = broadcast;\n logger.debug(`[MusicQueue:${this.guildId}] Broadcast reference set`);\n }\n\n /**\n * Register callback for when a track finishes playing\n */\n onTrackPlayed(\n callback: (track: QueuedTrack, duration: number) => Promise<void>,\n ): void {\n this.trackPlayedCallback = callback;\n }\n\n /**\n * Set cross-fade options\n */\n setCrossFadeOptions(options: Partial<CrossFadeOptions>): void {\n this.crossFadeOptions = { ...this.crossFadeOptions, ...options };\n }\n\n /**\n * Add a track to the queue\n */\n async addTrack(\n track: Omit<QueuedTrack, \"id\" | \"addedAt\">,\n ): Promise<QueuedTrack> {\n // Note: We skip pre-validation to avoid downloading the track twice\n // Playback validates the canonical stream path once the track reaches the head of queue.\n logger.debug(`Adding track to queue: ${track.title}`);\n\n const queuedTrack: QueuedTrack = {\n ...track,\n id: v4(),\n addedAt: Date.now(),\n };\n\n this.queue.push(queuedTrack);\n logger.debug(\n `Added track to queue: ${queuedTrack.title} (${queuedTrack.id})`,\n );\n\n // If nothing is playing, start playback in the background so the caller\n // (e.g. PLAY_AUDIO action handler) returns immediately and doesn't block\n // the chat while the track streams.\n if (!this.isPlaying && !this.currentTrack) {\n this.playNext().catch((err) => {\n logger.error(`[MusicQueue:${this.guildId}] playNext failed: ${err}`);\n });\n } else if (\n this.isPlaying &&\n !this.nextTrackPreBuffered &&\n this.queue.length === 1\n ) {\n this.preBufferNextTrack().catch((err) => {\n logger.debug(`[MusicQueue:${this.guildId}] preBuffer failed: ${err}`);\n });\n }\n\n return queuedTrack;\n }\n\n /**\n * Remove a track from the queue\n */\n removeTrack(trackId: string): boolean {\n const index = this.queue.findIndex((t) => t.id === trackId);\n if (index >= 0) {\n const track = this.queue[index];\n this.queue.splice(index, 1);\n logger.debug(`Removed track from queue: ${track.title}`);\n\n // If we removed the pre-buffered track, clear it\n if (this.nextTrackPreBuffered?.id === trackId) {\n this.nextTrackPreBuffered = null;\n this.nextTrackStream = null;\n }\n\n return true;\n }\n return false;\n }\n\n /**\n * Skip the current track.\n * Works for both legacy voiceManager and broadcast architectures.\n */\n async skip(): Promise<boolean> {\n if (!this.currentTrack) {\n return false;\n }\n\n logger.debug(`Skipping track: ${this.currentTrack.title}`);\n\n // Notify about track completion (with 0 duration since skipped)\n if (this.trackPlayedCallback) {\n await this.trackPlayedCallback(this.currentTrack, 0);\n }\n\n // Stop current playback on legacy path\n if (this.currentPlaybackHandle && this.voiceManager) {\n await this.voiceManager.stopAudio(this.guildId, this.MUSIC_CHANNEL);\n this.currentPlaybackHandle = null;\n }\n\n // Stop current broadcast track so playNext() can push the next one\n if (this.broadcast) {\n this.broadcast.stop();\n }\n\n // Play next track\n await this.playNext();\n\n return true;\n }\n\n /**\n * Pause playback.\n * For Discord voice: delegates to voiceManager.pauseAudio.\n * For broadcast (web): stops the broadcast StreamCore so HTTP clients\n * stop receiving new audio frames (they stay connected but hear silence).\n */\n async pause(): Promise<void> {\n if (!this.isPlaying || this.isPaused) {\n return;\n }\n\n this.isPaused = true;\n if (this.voiceManager) {\n await this.voiceManager.pauseAudio(this.guildId, this.MUSIC_CHANNEL);\n }\n if (this.broadcast) {\n this.broadcast.stop();\n }\n logger.debug(\"Playback paused\");\n }\n\n /**\n * Resume playback.\n * For Discord voice: delegates to voiceManager.resumeAudio.\n * For broadcast (web): re-pushes the current track metadata so clients\n * know playback resumed. The actual audio stream is still wired; calling\n * broadcast.startSilence() unblocks the pipeline and the next pushTrack\n * will pick it back up.\n */\n async resume(): Promise<void> {\n if (!this.isPaused) {\n return;\n }\n\n this.isPaused = false;\n if (this.voiceManager) {\n await this.voiceManager.resumeAudio(this.guildId, this.MUSIC_CHANNEL);\n }\n if (this.broadcast) {\n this.broadcast.startSilence();\n }\n logger.debug(\"Playback resumed\");\n }\n\n /**\n * Stop playback and clear queue.\n * Stops both legacy voiceManager and broadcast paths so all consumers\n * (Discord voice + HTTP stream clients) stop receiving audio.\n */\n async stop(): Promise<void> {\n this.isPlaying = false;\n this.isPaused = false;\n\n if (this.currentPlaybackHandle && this.voiceManager) {\n await this.voiceManager.stopAudio(this.guildId, this.MUSIC_CHANNEL);\n this.currentPlaybackHandle = null;\n }\n\n if (this.broadcast) {\n this.broadcast.stop();\n }\n\n if (this.preBufferTimeout) {\n clearTimeout(this.preBufferTimeout);\n this.preBufferTimeout = null;\n }\n\n this.currentTrack = null;\n this.nextTrackStream = null;\n this.nextTrackPreBuffered = null;\n\n logger.debug(\"Playback stopped\");\n }\n\n /**\n * Clear the queue\n */\n clear(): void {\n this.queue = [];\n this.cleanupPreBufferedStream();\n logger.debug(\"Queue cleared\");\n }\n\n /**\n * Shuffle the queue\n */\n shuffle(): void {\n for (let i = this.queue.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.queue[i], this.queue[j]] = [this.queue[j], this.queue[i]];\n }\n logger.debug(\"Queue shuffled\");\n }\n\n /**\n * Get the queue\n */\n getQueue(): QueuedTrack[] {\n return [...this.queue];\n }\n\n /**\n * Get the current track\n */\n getCurrentTrack(): QueuedTrack | null {\n return this.currentTrack;\n }\n\n /**\n * Get whether playback is active\n */\n getIsPlaying(): boolean {\n return this.isPlaying;\n }\n\n /**\n * Get whether playback is paused\n */\n getIsPaused(): boolean {\n return this.isPaused;\n }\n\n /**\n * Get the continuous stream\n */\n getContinuousStream(): PassThrough | null {\n return this.continuousStream;\n }\n\n /**\n * Get audio stream for a track (cached or download)\n * Tries cache first (title-based, then URL-based), then downloads if not cached.\n * @param track - Track to get stream for\n * @param shouldCache - Whether to cache downloaded streams (default: true)\n * @returns Audio stream or null if failed\n */\n private async getAudioStreamForTrack(\n track: QueuedTrack,\n shouldCache: boolean = true,\n notifyOnFailure: boolean = true,\n ): Promise<Readable | null> {\n let stream: Readable | null = null;\n let lastErrorMessage: string | null = null;\n // Try cache first if available\n if (this.audioCache) {\n // First try with exact cache key (title-based)\n const cacheKey = {\n song: track.title,\n quality: \"high\" as const,\n url: track.url,\n };\n stream = await this.audioCache.getCachedAudio(cacheKey);\n\n // If not found by title, try finding by URL (in case title changed)\n if (!stream) {\n logger.debug(\n `Cache miss with title \"${track.title}\", trying URL lookup...`,\n );\n stream = await this.audioCache.findCachedAudioByUrl(track.url, \"high\");\n }\n\n if (stream) {\n logger.debug(`Using cached audio: ${track.title}`);\n }\n }\n\n // If still not cached, download using the canonical stream path\n if (!stream) {\n try {\n const streamResult = await createAudioStream(track.url);\n stream = streamResult.stream;\n\n logger.debug(\n `Stream created using ${streamResult.source} for: ${track.title}`,\n );\n\n // Cache for next time if cache available and shouldCache is true\n if (shouldCache && this.audioCache && stream) {\n const cacheKey = {\n song: track.title,\n quality: \"high\" as const,\n url: track.url,\n };\n // Download and cache in background (don't block playback)\n this.audioCache.downloadAndCache(cacheKey, track.url).catch((err) => {\n logger.debug(`Background caching failed: ${err}`);\n });\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n lastErrorMessage = errorMessage;\n if (!stream) {\n logger.error(\n `Error creating stream for ${track.url}: ${errorMessage}`,\n );\n }\n }\n }\n\n if (!stream && notifyOnFailure) {\n this.emit(\n \"track:error\",\n track,\n lastErrorMessage || \"No playable source found\",\n );\n }\n\n return stream;\n }\n\n /**\n * Clean up pre-buffered stream\n */\n private cleanupPreBufferedStream(): void {\n if (this.nextTrackStream) {\n try {\n // Remove all listeners to prevent error propagation\n this.nextTrackStream.removeAllListeners();\n this.nextTrackStream = null;\n logger.debug(\"Cleaned up pre-buffered stream\");\n } catch (error) {\n logger.debug(`Error cleaning up pre-buffered stream: ${error}`);\n }\n }\n this.nextTrackPreBuffered = null;\n }\n\n /**\n * Pre-buffer the next track for cross-fading\n */\n private async preBufferNextTrack(): Promise<void> {\n if (this.queue.length === 0 || this.nextTrackPreBuffered) {\n return;\n }\n\n const nextTrack = this.queue[0];\n logger.debug(`Pre-buffering next track: ${nextTrack.title}`);\n\n try {\n // Don't cache pre-buffered streams (they'll be cached when actually played)\n const stream = await this.getAudioStreamForTrack(nextTrack, false, false);\n\n if (stream) {\n // Add error handler to catch premature close errors\n stream.on(\"error\", (error) => {\n // Only log if this stream is still the current pre-buffered stream\n if (this.nextTrackStream === stream) {\n logger.warn(\n `Pre-buffered stream error for ${nextTrack.title}: ${error.message}`,\n );\n }\n });\n\n this.nextTrackStream = stream;\n this.nextTrackPreBuffered = nextTrack;\n logger.debug(`Successfully pre-buffered: ${nextTrack.title}`);\n }\n } catch (error) {\n logger.error(`Error pre-buffering track: ${error}`);\n this.cleanupPreBufferedStream();\n }\n }\n\n /**\n * Play the next track in the queue\n */\n private async playNext(): Promise<void> {\n // ALWAYS emit track:finished for the current track first\n // WHY: Radio service listens to this event to refill the queue.\n // If we check queue.length before emitting, and queue is empty,\n // we return early and radio never gets a chance to refill.\n if (this.currentTrack) {\n const finishedTrack = this.currentTrack;\n logger.debug(`Track finished: ${finishedTrack.title}`);\n this.emit(\"track:finished\", finishedTrack, 0); // Duration calculated elsewhere\n if (this.trackPlayedCallback) {\n await this.trackPlayedCallback(finishedTrack, 0);\n }\n }\n\n // If queue is empty, wait briefly for external services (like radio) to refill\n // WHY: The track:finished event above is async - radio service needs time to add tracks\n if (this.queue.length === 0) {\n logger.debug(\"Queue empty, waiting for external refill...\");\n\n // Wait up to 3 seconds for queue to be refilled by external services\n const maxWaitMs = 3000;\n const checkIntervalMs = 200;\n let waitedMs = 0;\n\n while (waitedMs < maxWaitMs && this.queue.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, checkIntervalMs));\n waitedMs += checkIntervalMs;\n }\n\n // Check again after waiting\n if (this.queue.length === 0) {\n logger.warn(\n \"Queue still empty after waiting for refill, stopping playback\",\n );\n this.isPlaying = false;\n this.currentTrack = null;\n\n // Clean up pre-buffered stream if it exists\n this.cleanupPreBufferedStream();\n return;\n }\n\n logger.info(\n `Queue refilled while waiting (now has ${this.queue.length} tracks)`,\n );\n }\n\n let trackToPlay: QueuedTrack;\n let stream: Readable | null = null;\n\n // Use pre-buffered track if available\n if (\n this.nextTrackPreBuffered &&\n this.nextTrackStream &&\n this.queue[0]?.id === this.nextTrackPreBuffered.id\n ) {\n const nextTrack = this.queue.shift();\n if (!nextTrack) {\n logger.error(\n `[MusicQueue:${this.guildId}] Pre-buffered track missing from queue`,\n );\n this.cleanupPreBufferedStream();\n this.isPlaying = false;\n return;\n }\n trackToPlay = nextTrack;\n stream = this.nextTrackStream;\n this.nextTrackPreBuffered = null;\n this.nextTrackStream = null;\n logger.debug(`Using pre-buffered track: ${trackToPlay.title}`);\n\n // Trigger background caching for pre-buffered tracks\n // WHY: Pre-buffering disables caching to avoid double-download, but we still\n // want to cache for next time. Trigger caching now that we're actually playing.\n if (this.audioCache) {\n const cacheKey = {\n song: trackToPlay.title,\n quality: \"high\" as const,\n url: trackToPlay.url,\n };\n this.audioCache\n .downloadAndCache(cacheKey, trackToPlay.url)\n .catch((err) => {\n logger.debug(\n `Background caching of pre-buffered track failed: ${err}`,\n );\n });\n }\n } else {\n // Get next track from queue\n const nextTrack = this.queue.shift();\n if (!nextTrack) {\n logger.warn(\n `[MusicQueue:${this.guildId}] Queue emptied before playback could start`,\n );\n this.isPlaying = false;\n return;\n }\n trackToPlay = nextTrack;\n\n // Get audio stream (handles cache lookup, download, and caching)\n stream = await this.getAudioStreamForTrack(trackToPlay, true);\n\n // If still no stream, try next track\n if (!stream) {\n logger.error(`Failed to create stream for track: ${trackToPlay.title}`);\n await this.playNext();\n return;\n }\n }\n\n if (!stream) {\n logger.error(`Failed to create stream for track: ${trackToPlay.title}`);\n await this.playNext();\n return;\n }\n\n // NOTE: track:finished is now emitted at the START of playNext() to ensure\n // radio service can refill the queue before we check if it's empty.\n\n this.currentTrack = trackToPlay;\n\n // Emit track:starting event BEFORE pushing to broadcast\n // This allows DJ intro to announce BEFORE music starts playing\n this.emit(\"track:starting\", trackToPlay);\n logger.info(`Track starting: ${trackToPlay.title}`);\n\n // Wait for DJ intro TTS to complete before starting music\n // WHY: DJ intro announces the track, so music should start after the announcement\n if (this.runtime) {\n try {\n const discordService = this.runtime.getService(\n \"discord\",\n ) as DiscordServiceLike | null;\n if (discordService?.voiceManager) {\n // Poll TTS channel (0) until it's not playing\n const maxWaitMs = 15000; // Max 15 seconds\n const pollIntervalMs = 500; // Check every 500ms\n let waitedMs = 0;\n\n while (waitedMs < maxWaitMs) {\n const isTTSPlaying = await discordService.voiceManager.isPlaying(\n this.guildId,\n 0,\n );\n if (!isTTSPlaying) {\n logger.debug(\n `[MusicQueue:${this.guildId}] TTS finished, starting music`,\n );\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));\n waitedMs += pollIntervalMs;\n }\n\n if (waitedMs >= maxWaitMs) {\n logger.warn(\n `[MusicQueue:${this.guildId}] TTS still playing after ${maxWaitMs}ms, starting music anyway`,\n );\n }\n }\n } catch (error) {\n logger.debug(\n `[MusicQueue:${this.guildId}] Could not check TTS status: ${error}`,\n );\n // Fall back to delay\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n } else {\n // No runtime, use delay\n await new Promise((resolve) => setTimeout(resolve, 3000));\n }\n\n this.isPlaying = true;\n\n // ARCHITECTURE DECISION: Broadcast vs Legacy\n //\n // WHY ONLY ONE PATH:\n // A Node.js stream can only be consumed once. If we push to broadcast AND\n // call voiceManager.playAudio(), both try to read the same stream, causing\n // race conditions and \"stream ended\" errors.\n //\n // If broadcast is available, use it exclusively. The auto-wiring in MusicService\n // subscribes Discord to the broadcast, so audio flows:\n // Stream → Broadcast → StreamCore → StreamMultiplexer → DiscordAudioSink → Discord\n //\n // Legacy path only used when broadcast is NOT available (backward compatibility).\n\n if (this.broadcast) {\n // NEW ARCHITECTURE: Feed audio into broadcast\n logger.info(\n `[MusicQueue:${this.guildId}] Using broadcast architecture for: ${trackToPlay.title}`,\n );\n try {\n // Push track to broadcast AFTER announcing - DJ intro can pause if needed\n await this.broadcast.pushTrack(stream, {\n title: trackToPlay.title,\n url: trackToPlay.url,\n duration: trackToPlay.duration,\n requestedBy: trackToPlay.requestedBy,\n });\n\n logger.info(\n `[MusicQueue:${this.guildId}] Track pushed to broadcast, waiting for Discord subscription...`,\n );\n\n // Emit track:started event after music actually starts\n this.emit(\"track:started\", trackToPlay);\n logger.info(`Now playing: ${trackToPlay.title}`);\n\n // Pre-buffer next track for smooth transitions\n if (this.queue.length > 0) {\n const timeUntilPreBuffer =\n Math.max(0, (trackToPlay.duration || 180) - 30) * 1000;\n this.preBufferTimeout = setTimeout(() => {\n this.preBufferNextTrack();\n }, timeUntilPreBuffer);\n }\n\n // Wait for playback to finish.\n // Discord: wait for VoiceManager audio:finished (real-time playback).\n // Non-Discord (HTTP streaming): wait for the broadcast track:ended\n // event which fires when the source stream is consumed, then hold for\n // the track duration so HTTP clients receive the full audio.\n if (this.voiceManager) {\n const voiceManager = this.voiceManager;\n await new Promise<void>((resolve) => {\n const onFinished = (data: { guildId: string; channel: number }) => {\n if (\n data.guildId === this.guildId &&\n data.channel === this.MUSIC_CHANNEL\n ) {\n voiceManager.off(\"audio:finished\", onFinished);\n resolve();\n }\n };\n voiceManager.on(\"audio:finished\", onFinished);\n });\n } else {\n // No voiceManager — wait for broadcast track:ended then hold for\n // the track duration so HTTP streaming clients get the full audio.\n await new Promise<void>((resolve) => {\n const onTrackEnded = () => {\n this.broadcast?.off(\"track:ended\", onTrackEnded);\n const holdMs = (trackToPlay.duration || 180) * 1000;\n setTimeout(resolve, holdMs);\n };\n this.broadcast?.on(\"track:ended\", onTrackEnded);\n });\n }\n\n // Track finished, play next\n logger.debug(`Track finished: ${trackToPlay.title}`);\n await this.playNext();\n } catch (error) {\n logger.error(\n `[MusicQueue:${this.guildId}] Error in broadcast playback: ${error}`,\n );\n this.isPlaying = false;\n await this.playNext();\n }\n } else if (this.voiceManager) {\n // LEGACY ARCHITECTURE: Direct playback through voice manager\n // Only used when broadcast is not available and voiceManager exists\n try {\n this.currentPlaybackHandle = await this.voiceManager.playAudio(stream, {\n guildId: this.guildId,\n channel: this.MUSIC_CHANNEL,\n });\n\n // Emit track:started event (after playback begins)\n this.emit(\"track:started\", trackToPlay);\n logger.info(`Now playing: ${trackToPlay.title}`);\n\n // Pre-buffer next track for smooth transitions\n if (this.queue.length > 0) {\n const timeUntilPreBuffer =\n Math.max(0, (trackToPlay.duration || 180) - 30) * 1000;\n this.preBufferTimeout = setTimeout(() => {\n this.preBufferNextTrack();\n }, timeUntilPreBuffer);\n }\n\n // Wait for track to finish\n if (this.currentPlaybackHandle?.finished) {\n await this.currentPlaybackHandle.finished;\n }\n\n // Track finished, play next\n logger.debug(`Track finished: ${trackToPlay.title}`);\n await this.playNext();\n } catch (error) {\n logger.error(`Error during playback: ${error}`);\n this.isPlaying = false;\n await this.playNext();\n }\n } else {\n // No broadcast and no voiceManager — nothing can consume the stream.\n logger.warn(\n `[MusicQueue:${this.guildId}] No broadcast or voiceManager — track \"${trackToPlay.title}\" cannot be played.`,\n );\n this.emit(\"track:error\", trackToPlay, \"No playback target available\");\n this.isPlaying = false;\n await this.playNext();\n }\n }\n}\n","import type { Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\nimport { createYtdlpStream } from \"./ytdlpFallback\";\n\nexport interface StreamCreationResult {\n stream: Readable;\n source: \"yt-dlp\";\n}\n\n/**\n * Canonical audio stream creation path.\n *\n * Streaming now relies on yt-dlp only so playback fails closed instead of\n * silently cascading across different extractors with different behavior.\n *\n * @param url - YouTube URL to stream\n * @returns Stream creation result with the stream and which tool succeeded\n * @throws Error if yt-dlp cannot create a playable stream\n */\nexport async function createAudioStream(\n url: string,\n): Promise<StreamCreationResult> {\n try {\n logger.debug(`[stream] Attempting yt-dlp for: ${url}`);\n const stream = await createYtdlpStream(url);\n if (!stream) {\n throw new Error(`yt-dlp returned no playable stream for ${url}`);\n }\n\n logger.info(`[stream] Success with yt-dlp`);\n return {\n stream,\n source: \"yt-dlp\",\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`[stream] yt-dlp failed for ${url}: ${errorMessage}`);\n throw new Error(\n `Failed to create audio stream from ${url}: ${errorMessage}\\n` +\n \"Ensure yt-dlp is installed and the source is accessible.\",\n );\n }\n}\n\n/**\n * Check if a URL is a YouTube URL\n */\nexport function isYouTubeUrl(url: string): boolean {\n try {\n const urlObj = new URL(url);\n return (\n (urlObj.hostname === \"youtube.com\" ||\n urlObj.hostname === \"www.youtube.com\" ||\n urlObj.hostname === \"youtu.be\" ||\n urlObj.hostname === \"m.youtube.com\") &&\n (urlObj.pathname.includes(\"/watch\") ||\n urlObj.pathname.includes(\"/v/\") ||\n urlObj.pathname.startsWith(\"/\") ||\n urlObj.searchParams.has(\"v\"))\n );\n } catch {\n return false;\n }\n}\n","import { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport { createReadStream, existsSync, unlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { PassThrough, type Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\nimport { augmentEnvWithFfmpegTools } from \"./ffmpegEnv\";\nimport { formatMusicDebugCommand, musicDebug } from \"./musicDebug\";\nimport { getYtdlpPath, YTDLP_INSTALL_INSTRUCTIONS } from \"./ytdlpCheck\";\nimport { getYtdlpJsRuntimeCliArgs } from \"./ytdlpCli\";\nimport {\n getYoutubeExtractorArgsValue,\n getYtdlpEjsFailureHint,\n isYoutubeStreamUrl,\n YTDLP_STDERR_SNIPPET_LEN,\n} from \"./ytdlpYoutube\";\n\ninterface YtdlpStreamError extends NodeJS.ErrnoException {\n ageVerificationDetected?: boolean;\n downloadBlocked?: boolean;\n stderrBuffer?: string[];\n}\n\nfunction annotateYtdlpError(\n error: Error,\n details: Pick<\n YtdlpStreamError,\n \"ageVerificationDetected\" | \"downloadBlocked\" | \"stderrBuffer\"\n > & { code?: number | string | null },\n): YtdlpStreamError {\n const annotated = error as YtdlpStreamError;\n annotated.code =\n details.code !== null && details.code !== undefined\n ? String(details.code)\n : undefined;\n annotated.stderrBuffer = details.stderrBuffer;\n annotated.ageVerificationDetected = details.ageVerificationDetected;\n annotated.downloadBlocked = details.downloadBlocked;\n return annotated;\n}\n\nfunction getYtdlpErrorMetadata(error: unknown): Partial<YtdlpStreamError> {\n return error && typeof error === \"object\"\n ? (error as Partial<YtdlpStreamError>)\n : {};\n}\n\n/**\n * Get YouTube cookies file path from environment or return null\n */\nfunction getYouTubeCookiesPath(): string | null {\n // Check environment variable first\n const cookiesPath = process.env.YOUTUBE_COOKIES || process.env.YTDLP_COOKIES;\n if (cookiesPath && existsSync(cookiesPath)) {\n logger.debug(`Using YouTube cookies file: ${cookiesPath}`);\n return cookiesPath;\n }\n return null;\n}\n\n/**\n * Get proxy URL from environment or return null\n * Supports HTTP, HTTPS, and SOCKS proxies\n */\nfunction getProxyUrl(): string | null {\n // Check multiple environment variable names for proxy\n const proxyUrl =\n process.env.YOUTUBE_PROXY ||\n process.env.YTDLP_PROXY ||\n process.env.HTTP_PROXY ||\n process.env.HTTPS_PROXY ||\n process.env.http_proxy ||\n process.env.https_proxy;\n\n if (proxyUrl) {\n // Validate proxy URL format\n try {\n const url = new URL(proxyUrl);\n if ([\"http:\", \"https:\", \"socks4:\", \"socks5:\"].includes(url.protocol)) {\n logger.debug(`Using proxy: ${proxyUrl}`);\n return proxyUrl;\n } else {\n logger.warn(\n `Invalid proxy protocol: ${url.protocol}. Supported: http, https, socks4, socks5`,\n );\n }\n } catch (_error) {\n logger.warn(`Invalid proxy URL format: ${proxyUrl}`);\n }\n }\n return null;\n}\n\n/**\n * Create a yt-dlp stream with the specified format selector\n *\n * Downloads to a temporary file first (bypasses YouTube stdout streaming restrictions),\n * then streams from that file. The temp file is automatically cleaned up after streaming.\n */\nasync function createYtdlpStreamWithFormat(\n url: string,\n formatSelector: string,\n ytdlpPath: string,\n cookiesPath: string | null,\n proxyUrl: string | null,\n): Promise<{\n stream: Readable;\n stderrBuffer: string[];\n ageVerificationDetected: boolean;\n downloadBlocked: boolean;\n}> {\n return new Promise((resolve, reject) => {\n // Generate unique temp file path\n const tempFileName = `ytdlp_${randomBytes(8).toString(\"hex\")}.opus`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n logger.debug(`[ytdlp] Downloading to temp file: ${tempFilePath}`);\n\n // Build yt-dlp command arguments\n const args: string[] = [\n ...getYtdlpJsRuntimeCliArgs(),\n \"-f\",\n formatSelector,\n \"-x\", // Extract audio only\n \"--audio-format\",\n \"opus\", // Convert to opus (required for Discord voice streaming)\n \"--audio-quality\",\n \"0\", // Best quality (VBR)\n \"--no-playlist\",\n ];\n\n if (isYoutubeStreamUrl(url)) {\n const extractorArgs = getYoutubeExtractorArgsValue();\n if (extractorArgs) {\n args.push(\"--extractor-args\", extractorArgs);\n logger.debug(`[ytdlp] YouTube extractor-args: ${extractorArgs}`);\n }\n }\n\n // Add proxy if available\n if (proxyUrl) {\n args.push(\"--proxy\", proxyUrl);\n logger.debug(`[ytdlp] Using proxy: ${proxyUrl}`);\n }\n\n // Add cookies if available\n if (cookiesPath) {\n args.push(\"--cookies\", cookiesPath);\n logger.debug(\n `[ytdlp] Using cookies file for authentication: ${cookiesPath}`,\n );\n }\n\n // Download to temp file instead of stdout (bypasses YouTube streaming restrictions)\n args.push(\"-o\", tempFilePath);\n args.push(url);\n\n const ytdlp = spawn(ytdlpPath, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: augmentEnvWithFfmpegTools(),\n });\n\n logger.debug(\n `[ytdlp] yt-dlp spawned with format selector: ${formatSelector}, PID: ${ytdlp.pid}`,\n );\n\n let resolved = false;\n const stderrBuffer: string[] = [];\n let ageVerificationDetected = false;\n let downloadBlocked = false;\n\n ytdlp.stdout.on(\"data\", (data) => {\n // Log any stdout (usually progress info)\n const msg = data.toString();\n if (msg.includes(\"[download]\")) {\n logger.debug(`[ytdlp] ${msg.trim()}`);\n }\n });\n\n ytdlp.stderr.on(\"data\", (data) => {\n const msg = data.toString();\n stderrBuffer.push(msg);\n\n const msgLower = msg.toLowerCase();\n if (\n msgLower.includes(\"age verification\") ||\n msgLower.includes(\"restricted in your region\") ||\n msgLower.includes(\"sign in to confirm\") ||\n msgLower.includes(\"not a bot\")\n ) {\n ageVerificationDetected = true;\n }\n\n if (\n msg.includes(\"[download]\") &&\n msg.includes(\"0.0%\") &&\n msg.includes(\"ETA Unknown\")\n ) {\n downloadBlocked = true;\n }\n\n if (msg.includes(\"[info]\")) {\n logger.debug(`[ytdlp] yt-dlp info: ${msg.substring(0, 300)}`);\n }\n });\n\n ytdlp.on(\"error\", (error) => {\n if (!resolved) {\n resolved = true;\n const stderrText = stderrBuffer.join(\"\").trimEnd();\n musicDebug(\"ytdlp spawn/process error\", {\n url,\n formatSelector,\n command: formatMusicDebugCommand(ytdlpPath, args),\n message: error instanceof Error ? error.message : String(error),\n stderr: stderrText.length > 0 ? stderrText : undefined,\n });\n // Clean up temp file on error\n try {\n if (existsSync(tempFilePath)) {\n unlinkSync(tempFilePath);\n }\n } catch (cleanupError) {\n logger.debug(`[ytdlp] Failed to cleanup temp file: ${cleanupError}`);\n }\n const wrapped =\n error instanceof Error ? error : new Error(String(error));\n (\n wrapped as NodeJS.ErrnoException & { stderrBuffer?: string[] }\n ).stderrBuffer = stderrBuffer;\n reject(wrapped);\n }\n });\n\n // Use `close` (not `exit`) so stderr/stdout pipes are flushed before we read the result.\n ytdlp.on(\"close\", (code, signal) => {\n if (!resolved) {\n resolved = true;\n\n if (code === 0) {\n // Download succeeded, check if file exists\n if (!existsSync(tempFilePath)) {\n const stderrText = stderrBuffer.join(\"\").trimEnd();\n musicDebug(\"ytdlp exit 0 but temp file missing\", {\n url,\n formatSelector,\n tempFilePath,\n command: formatMusicDebugCommand(ytdlpPath, args),\n stderr: stderrText.length > 0 ? stderrText : undefined,\n });\n const error = new Error(\n \"Download completed but temp file not found\",\n );\n reject(\n annotateYtdlpError(error, {\n code,\n stderrBuffer,\n ageVerificationDetected,\n downloadBlocked,\n }),\n );\n return;\n }\n\n // Create read stream from temp file\n try {\n const fileStream = createReadStream(tempFilePath, {\n highWaterMark: 64 * 1024, // 64KB buffer\n });\n\n // Create a PassThrough to control the stream lifecycle\n const stream = new PassThrough();\n stream.setMaxListeners(20);\n\n // Pipe file to output stream\n fileStream.pipe(stream);\n\n // Clean up temp file when streaming is done or errored\n const cleanup = () => {\n try {\n if (existsSync(tempFilePath)) {\n unlinkSync(tempFilePath);\n logger.debug(`[ytdlp] Cleaned up temp file: ${tempFilePath}`);\n }\n } catch (cleanupError) {\n logger.debug(\n `[ytdlp] Failed to cleanup temp file: ${cleanupError}`,\n );\n }\n };\n\n stream.on(\"end\", cleanup);\n stream.on(\"close\", cleanup);\n stream.on(\"error\", (error) => {\n logger.error(`[ytdlp] Stream error: ${error}`);\n cleanup();\n });\n\n // Handle file stream errors\n fileStream.on(\"error\", (error) => {\n logger.error(`[ytdlp] File stream error: ${error}`);\n stream.destroy(error);\n });\n\n logger.debug(\n `[ytdlp] Successfully created stream from temp file: ${tempFilePath}`,\n );\n musicDebug(\"ytdlp temp file stream ready\", {\n tempFilePath,\n formatSelector,\n });\n resolve({\n stream,\n stderrBuffer,\n ageVerificationDetected,\n downloadBlocked,\n });\n } catch (streamError) {\n // Clean up temp file on stream creation error\n try {\n if (existsSync(tempFilePath)) {\n unlinkSync(tempFilePath);\n }\n } catch (cleanupError) {\n logger.debug(\n `[ytdlp] Failed to cleanup temp file: ${cleanupError}`,\n );\n }\n reject(streamError);\n }\n } else {\n // Clean up temp file\n try {\n if (existsSync(tempFilePath)) {\n unlinkSync(tempFilePath);\n }\n } catch (cleanupError) {\n logger.debug(\n `[ytdlp] Failed to cleanup temp file: ${cleanupError}`,\n );\n }\n\n const stderrText = stderrBuffer.join(\"\").trim();\n if (stderrText) {\n logger.error(\n `[ytdlp] yt-dlp stderr (exit ${code ?? \"null\"}, signal ${signal ?? \"none\"}):\\n${stderrText.substring(0, YTDLP_STDERR_SNIPPET_LEN)}`,\n );\n }\n\n musicDebug(\"ytdlp exit failure\", {\n url,\n formatSelector,\n code: code ?? undefined,\n signal: signal ?? undefined,\n command: formatMusicDebugCommand(ytdlpPath, args),\n stderr: stderrText,\n });\n\n const msg =\n signal && (code === null || code === undefined)\n ? `yt-dlp terminated by signal ${signal}`\n : `yt-dlp exited with code ${code}`;\n const error = new Error(msg);\n (error as NodeJS.ErrnoException & { stderrBuffer?: string[] }).code =\n code != null ? String(code) : undefined;\n (error as { stderrBuffer?: string[] }).stderrBuffer = stderrBuffer;\n (\n error as { ageVerificationDetected?: boolean }\n ).ageVerificationDetected = ageVerificationDetected;\n (error as { downloadBlocked?: boolean }).downloadBlocked =\n downloadBlocked;\n reject(error);\n }\n }\n });\n\n // Timeout after 60 seconds (increased from 15s since we're downloading to file)\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n const stderrText = stderrBuffer.join(\"\").trimEnd();\n musicDebug(\"ytdlp timeout\", {\n url,\n formatSelector,\n command: formatMusicDebugCommand(ytdlpPath, args),\n stderr: stderrText.length > 0 ? stderrText : undefined,\n });\n ytdlp.kill();\n // Clean up temp file on timeout\n try {\n if (existsSync(tempFilePath)) {\n unlinkSync(tempFilePath);\n }\n } catch (cleanupError) {\n logger.debug(`[ytdlp] Failed to cleanup temp file: ${cleanupError}`);\n }\n reject(new Error(\"yt-dlp timeout\"));\n }\n }, 60000);\n });\n}\n\n/**\n * Fallback to yt-dlp when play-dl fails\n * This handles videos that play-dl can't stream (like certain restricted videos)\n *\n * Note: yt-dlp outputs opus format directly, which Discord.js can handle via demuxProbe\n *\n * Uses a retry mechanism: if the preferred format fails, tries with a more permissive format\n */\nexport async function createYtdlpStream(url: string): Promise<Readable> {\n logger.debug(`Using yt-dlp fallback for: ${url}`);\n\n // Verify yt-dlp is available before attempting to use it\n let ytdlpPath: string;\n try {\n ytdlpPath = await getYtdlpPath();\n logger.debug(`Using yt-dlp at: ${ytdlpPath}`);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`yt-dlp not available: ${errorMessage}`);\n throw new Error(\n `yt-dlp is required for audio playback but was not found.\\n${YTDLP_INSTALL_INSTRUCTIONS}`,\n );\n }\n\n // Check for YouTube cookies for authentication\n const cookiesPath = getYouTubeCookiesPath();\n\n // Check for proxy configuration\n const proxyUrl = getProxyUrl();\n\n // Try with preferred format first (opus audio)\n const preferredFormat =\n \"bestaudio[ext=webm][acodec*=opus]/bestaudio[acodec*=opus]/bestaudio[ext=webm]/bestaudio\";\n\n try {\n const result = await createYtdlpStreamWithFormat(\n url,\n preferredFormat,\n ytdlpPath,\n cookiesPath,\n proxyUrl,\n );\n logger.debug(`[ytdlp] Successfully created stream with preferred format`);\n return result.stream;\n } catch (error: unknown) {\n const ytdlpError = getYtdlpErrorMetadata(error);\n // Check if this is a format availability issue (not age restriction or region block)\n const stderrOutput = (ytdlpError.stderrBuffer || []).join(\"\").toLowerCase();\n const isFormatNotAvailable =\n stderrOutput.includes(\"requested format is not available\") ||\n stderrOutput.includes(\"no suitable formats found\");\n const isFormatIssue =\n (ytdlpError.code === \"0\" &&\n !ytdlpError.ageVerificationDetected &&\n !ytdlpError.downloadBlocked) ||\n (isFormatNotAvailable &&\n !ytdlpError.ageVerificationDetected &&\n !ytdlpError.downloadBlocked);\n\n if (isFormatIssue) {\n logger.info(\n `[ytdlp] Preferred format failed, retrying with any available format...`,\n );\n\n // Try with permissive format - prefer audio formats, then any video format\n // The -x flag will extract audio from video formats\n // We limit height to reduce bandwidth and ensure faster processing\n const permissiveFormat =\n \"bestaudio/best[height<=720]/best[height<=480]/best\";\n\n try {\n const result = await createYtdlpStreamWithFormat(\n url,\n permissiveFormat,\n ytdlpPath,\n cookiesPath,\n proxyUrl,\n );\n logger.warn(\n `[ytdlp] Using permissive format fallback - this may result in lower quality or compatibility issues`,\n );\n logger.info(\n `[ytdlp] Successfully created stream with permissive format`,\n );\n return result.stream;\n } catch (retryError: unknown) {\n const retryYtdlpError = getYtdlpErrorMetadata(retryError);\n // Both attempts failed - provide helpful error message\n const stderrOutput = (retryYtdlpError.stderrBuffer || [])\n .join(\"\")\n .trim();\n const stderrLower = stderrOutput.toLowerCase();\n\n let helpfulHint = \"\";\n if (\n retryYtdlpError.ageVerificationDetected ||\n retryYtdlpError.downloadBlocked ||\n stderrLower.includes(\"age verification\") ||\n stderrLower.includes(\"restricted in your region\") ||\n stderrLower.includes(\"sign in to confirm\") ||\n stderrLower.includes(\"not a bot\")\n ) {\n helpfulHint =\n \"\\n\\n⚠️ This video requires age verification, authentication, or is restricted.\\n\\n\" +\n \"To fix this:\\n\" +\n \"1. Export your YouTube cookies using a browser extension:\\n\" +\n ' - Install \"Get cookies.txt LOCALLY\" or \"cookies.txt\" extension\\n' +\n \" - Export cookies from youtube.com to a file (e.g., youtube_cookies.txt)\\n\" +\n \" - Set environment variable: export YOUTUBE_COOKIES=/path/to/youtube_cookies.txt\\n\" +\n \" - Or: export YTDLP_COOKIES=/path/to/youtube_cookies.txt\\n\\n\" +\n \"2. Alternatively, use yt-dlp's built-in browser authentication:\\n\" +\n \" yt-dlp --cookies-from-browser chrome\\n\" +\n ' (replace \"chrome\" with your browser: chrome, firefox, edge, safari, etc.)\\n\\n' +\n \"3. If the video is region-restricted, you may need a VPN or proxy:\\n\" +\n \" export YOUTUBE_PROXY=http://proxy:port\";\n } else {\n helpfulHint =\n \"\\n\\nPossible causes:\\n\" +\n \"- Video requires authentication (set YOUTUBE_COOKIES)\\n\" +\n \"- Video requires age verification or is region-restricted\\n\" +\n \"- No suitable format available\\n\" +\n \"- Video is private or restricted\";\n }\n\n helpfulHint += getYtdlpEjsFailureHint(stderrOutput);\n\n const errorMessage = `yt-dlp failed to extract audio from URL: ${url}${stderrOutput ? `\\n\\nStderr output:\\n${stderrOutput.substring(0, YTDLP_STDERR_SNIPPET_LEN)}` : \"\"}${helpfulHint}`;\n logger.error(`[ytdlp] ${errorMessage}`);\n throw new Error(errorMessage);\n }\n } else {\n // Complete block (age verification, etc.) - don't retry, just provide helpful message\n const stderrOutput = (ytdlpError.stderrBuffer || []).join(\"\").trim();\n const stderrLower = stderrOutput.toLowerCase();\n\n // Check if it's a format error that we somehow missed\n const isFormatError =\n stderrLower.includes(\"requested format is not available\") ||\n stderrLower.includes(\"no suitable formats found\");\n\n let helpfulHint = \"\";\n if (isFormatError) {\n helpfulHint =\n \"\\n\\nℹ️ The requested audio format is not available for this video.\\n\" +\n \"This can happen with:\\n\" +\n \"- Live streams that haven't started yet\\n\" +\n \"- Videos that are being processed\\n\" +\n \"- Videos with unusual encoding\\n\" +\n \"Try again later or try a different video.\";\n } else if (\n ytdlpError.ageVerificationDetected ||\n ytdlpError.downloadBlocked ||\n stderrLower.includes(\"age verification\") ||\n stderrLower.includes(\"restricted in your region\") ||\n stderrLower.includes(\"sign in to confirm\") ||\n stderrLower.includes(\"not a bot\")\n ) {\n helpfulHint =\n \"\\n\\n⚠️ This video requires age verification, authentication, or is restricted.\\n\\n\" +\n \"To fix this:\\n\" +\n \"1. Export your YouTube cookies using a browser extension:\\n\" +\n ' - Install \"Get cookies.txt LOCALLY\" or \"cookies.txt\" extension\\n' +\n \" - Export cookies from youtube.com to a file (e.g., youtube_cookies.txt)\\n\" +\n \" - Set environment variable: export YOUTUBE_COOKIES=/path/to/youtube_cookies.txt\\n\" +\n \" - Or: export YTDLP_COOKIES=/path/to/youtube_cookies.txt\\n\\n\" +\n \"2. Alternatively, use yt-dlp's built-in browser authentication:\\n\" +\n \" yt-dlp --cookies-from-browser chrome\\n\" +\n ' (replace \"chrome\" with your browser: chrome, firefox, edge, safari, etc.)\\n\\n' +\n \"3. If the video is region-restricted, you may need a VPN or proxy:\\n\" +\n \" export YOUTUBE_PROXY=http://proxy:port\";\n }\n\n helpfulHint += getYtdlpEjsFailureHint(stderrOutput);\n\n const errorMessage = `yt-dlp failed to extract audio from URL: ${url}${stderrOutput ? `\\n\\nStderr output:\\n${stderrOutput.substring(0, YTDLP_STDERR_SNIPPET_LEN)}` : \"\"}${helpfulHint}`;\n logger.error(`[ytdlp] ${errorMessage}`);\n throw new Error(errorMessage);\n }\n }\n}\n","import { execSync } from \"node:child_process\";\nimport { accessSync, constants, existsSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { logger } from \"@elizaos/core\";\n\nexport const YTDLP_INSTALL_INSTRUCTIONS = `\nyt-dlp is not installed or not found in PATH.\n\nInstallation instructions:\n\n📦 Using pip in a venv (recommended on Debian/Ubuntu):\n python3 -m venv ~/venv && ~/venv/bin/pip install yt-dlp\n Then set: export YT_DLP_PATH=~/venv/bin/yt-dlp\n\n📦 Using pipx (isolated installation):\n pipx install yt-dlp\n\n📦 Using Homebrew (macOS):\n brew install yt-dlp\n\n📦 Using apt (Debian/Ubuntu):\n sudo apt update && sudo apt install yt-dlp\n\n📦 Using pacman (Arch Linux):\n sudo pacman -S yt-dlp\n\n📦 Manual installation:\n 1. Download from: https://github.com/yt-dlp/yt-dlp/releases/latest\n 2. Make executable: chmod +x yt-dlp\n 3. Move to PATH: sudo mv yt-dlp /usr/local/bin/\n\n📖 Full documentation: https://github.com/yt-dlp/yt-dlp#installation\n\nAfter installation, verify with: yt-dlp --version\n`;\n\nexport interface YtdlpCheckResult {\n found: boolean;\n path: string | null;\n error?: string;\n}\n\n/** Env var to force a specific yt-dlp executable (e.g. from a venv: /root/venv/bin/yt-dlp) */\nconst YT_DLP_PATH_ENV = \"YT_DLP_PATH\";\n\n/** Cache the resolved path so the expensive check only runs once per process. */\nlet cachedPath: string | null | undefined;\n\nfunction resolveYtdlpPathFromEnv(): string | null {\n const path = process.env[YT_DLP_PATH_ENV]?.trim();\n if (!path) return null;\n if (existsSync(path)) {\n return path;\n }\n return null;\n}\n\nfunction isExecutable(filePath: string): boolean {\n try {\n accessSync(filePath, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Build the list of candidate paths to check.\n * Searches workspace scripts/bin, common system paths, and PATH.\n */\nfunction getCandidatePaths(): string[] {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const candidates: string[] = [];\n const seen = new Set<string>();\n const add = (p: string) => {\n if (!seen.has(p)) {\n seen.add(p);\n candidates.push(p);\n }\n };\n\n // Walk up from module dir (handles both source and bundled dist layouts)\n for (let depth = 2; depth <= 5; depth++) {\n add(\n resolve(\n moduleDir,\n ...Array(depth).fill(\"..\"),\n \"scripts\",\n \"bin\",\n \"yt-dlp\",\n ),\n );\n }\n add(resolve(process.cwd(), \"scripts\", \"bin\", \"yt-dlp\"));\n\n add(\"/usr/local/bin/yt-dlp\");\n add(\"/usr/bin/yt-dlp\");\n add(\"/opt/homebrew/bin/yt-dlp\");\n\n return candidates;\n}\n\n/**\n * Check if yt-dlp is available and return its path.\n *\n * Uses `existsSync` + `accessSync(X_OK)` instead of running `--version`,\n * because the macOS PyInstaller binary has a ~10 s cold start that\n * exceeds reasonable `execSync` timeouts.\n */\nexport async function checkYtdlpAvailable(): Promise<YtdlpCheckResult> {\n if (cachedPath !== undefined) {\n return cachedPath\n ? { found: true, path: cachedPath }\n : { found: false, path: null, error: \"yt-dlp executable not found\" };\n }\n\n // 1. Env override\n const envPath = resolveYtdlpPathFromEnv();\n if (envPath && isExecutable(envPath)) {\n logger.debug(`Using yt-dlp from YT_DLP_PATH: ${envPath}`);\n cachedPath = envPath;\n return { found: true, path: envPath };\n }\n\n // 2. Known filesystem paths — just check existence + execute bit\n for (const candidate of getCandidatePaths()) {\n if (existsSync(candidate) && isExecutable(candidate)) {\n logger.debug(`Found yt-dlp at: ${candidate}`);\n cachedPath = candidate;\n return { found: true, path: candidate };\n }\n }\n\n // 3. Try PATH via `command -v` (fast, no cold-start penalty)\n try {\n const commandPath = execSync(\"command -v yt-dlp\", {\n encoding: \"utf-8\",\n timeout: 3000,\n }).trim();\n if (commandPath && existsSync(commandPath)) {\n logger.debug(`Found yt-dlp via PATH: ${commandPath}`);\n cachedPath = commandPath;\n return { found: true, path: commandPath };\n }\n } catch {\n // not on PATH\n }\n\n cachedPath = null;\n return {\n found: false,\n path: null,\n error: \"yt-dlp executable not found in PATH\",\n };\n}\n\n/**\n * Get yt-dlp path or throw error with installation instructions\n */\nexport async function getYtdlpPath(): Promise<string> {\n const check = await checkYtdlpAvailable();\n\n if (!check.found || !check.path) {\n throw new Error(\n `yt-dlp is required for audio playback but was not found.\\n${YTDLP_INSTALL_INSTRUCTIONS}`,\n );\n }\n\n return check.path;\n}\n\n/**\n * Synchronous variant for quick checks.\n */\nexport function checkYtdlpAvailableSync(): YtdlpCheckResult {\n if (cachedPath !== undefined) {\n return cachedPath\n ? { found: true, path: cachedPath }\n : { found: false, path: null, error: \"yt-dlp executable not found\" };\n }\n\n const envPath = resolveYtdlpPathFromEnv();\n if (envPath && isExecutable(envPath)) {\n cachedPath = envPath;\n return { found: true, path: envPath };\n }\n\n for (const candidate of getCandidatePaths()) {\n if (existsSync(candidate) && isExecutable(candidate)) {\n cachedPath = candidate;\n return { found: true, path: candidate };\n }\n }\n\n try {\n const commandPath = execSync(\"command -v yt-dlp\", {\n encoding: \"utf-8\",\n timeout: 3000,\n }).trim();\n if (commandPath && existsSync(commandPath)) {\n cachedPath = commandPath;\n return { found: true, path: commandPath };\n }\n } catch {\n // not found\n }\n\n cachedPath = null;\n return {\n found: false,\n path: null,\n error: \"yt-dlp executable not found in PATH\",\n };\n}\n","import { basename, dirname } from \"node:path\";\n\n/**\n * yt-dlp YouTube \"n challenge\" / EJS solving needs a JS engine. Official builds only\n * enable **deno** by default; Eliza usually runs under **Bun** or **Node**, which must\n * be passed explicitly via `--js-runtimes`.\n *\n * @see https://github.com/yt-dlp/yt-dlp/wiki/EJS\n *\n * Override: comma-separated `RUNTIME[:PATH]` values, e.g.\n * `YTDLP_JS_RUNTIMES=bun:/opt/homebrew/bin,node:/usr/local/bin`\n * Disable auto-detection: `YTDLP_JS_RUNTIMES=off`\n */\nexport function getYtdlpJsRuntimeCliArgs(): string[] {\n const out: string[] = [];\n const envRaw = process.env.YTDLP_JS_RUNTIMES?.trim();\n if (envRaw) {\n const lower = envRaw.toLowerCase();\n if (\n lower === \"off\" ||\n lower === \"none\" ||\n lower === \"0\" ||\n lower === \"false\"\n ) {\n return out;\n }\n for (const part of envRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)) {\n out.push(\"--js-runtimes\", part);\n }\n return out;\n }\n\n const execPath = process.execPath;\n const name = basename(execPath).toLowerCase();\n if (name === \"bun\" || name === \"bun.exe\") {\n out.push(\"--js-runtimes\", `bun:${dirname(execPath)}`);\n return out;\n }\n if (name === \"node\" || name === \"node.exe\") {\n out.push(\"--js-runtimes\", `node:${dirname(execPath)}`);\n return out;\n }\n return out;\n}\n\nfunction shellDoubleQuote(s: string): string {\n if (!/[\\s\\\\\"'$`!]/.test(s)) {\n return s;\n }\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/** Fragment to append after the yt-dlp binary path in shell `exec()` strings. */\nexport function getYtdlpJsRuntimeShellFragment(): string {\n const pairs = getYtdlpJsRuntimeCliArgs();\n if (pairs.length === 0) {\n return \"\";\n }\n let s = \"\";\n for (let i = 0; i < pairs.length - 1; i += 2) {\n const flag = pairs[i];\n const val = pairs[i + 1];\n if (flag === undefined || val === undefined) {\n break;\n }\n s += ` ${flag} ${shellDoubleQuote(val)}`;\n }\n return s;\n}\n","/**\n * YouTube-specific yt-dlp options. Centralized so streaming and cache use the same behavior.\n */\n\n/** Max stderr bytes to attach to thrown errors / logs (full context for bot / format failures). */\nexport const YTDLP_STDERR_SNIPPET_LEN = 4000;\n\nexport function isYoutubeStreamUrl(url: string): boolean {\n try {\n const u = new URL(url);\n const h = u.hostname.toLowerCase();\n return (\n h === \"youtube.com\" ||\n h === \"www.youtube.com\" ||\n h === \"m.youtube.com\" ||\n h === \"music.youtube.com\" ||\n h === \"youtu.be\"\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Optional `youtube:…` value for `yt-dlp --extractor-args`.\n *\n * **Default (unset / empty):** do not pass `--extractor-args` — yt-dlp uses its own\n * `player_client` defaults (`android_vr,web_safari`, cookies-aware variants, etc.).\n * Overriding with invalid/obsolete clients (e.g. `tv_embedded`) breaks extraction.\n *\n * Set when you need PO tokens or a specific client list, e.g.\n * `YTDLP_YOUTUBE_EXTRACTOR_ARGS=youtube:player_client=ios,web_safari`\n *\n * Use `default` to force passing `youtube:player_client=default` if you ever need that\n * explicitly (rare).\n */\nexport function getYoutubeExtractorArgsValue(): string | null {\n const custom = process.env.YTDLP_YOUTUBE_EXTRACTOR_ARGS?.trim();\n if (!custom) {\n return null;\n }\n if (custom.toLowerCase() === \"default\") {\n return \"youtube:player_client=default\";\n }\n return custom;\n}\n\nfunction shellDoubleQuoteForExec(s: string): string {\n if (!/[\\s\\\\\"'$`!]/.test(s)) {\n return s;\n }\n return `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/** Shell fragment for audio-cache `exec()` commands (after binary path). */\nexport function getYoutubeExtractorShellFragment(url: string): string {\n if (!isYoutubeStreamUrl(url)) {\n return \"\";\n }\n const ex = getYoutubeExtractorArgsValue();\n if (!ex) {\n return \"\";\n }\n return ` --extractor-args ${shellDoubleQuoteForExec(ex)}`;\n}\n\n/** Extra hint when stderr shows EJS / n-challenge issues (after js-runtimes fix). */\nexport function getYtdlpEjsFailureHint(stderr: string): string {\n const s = stderr.toLowerCase();\n if (\n !s.includes(\"challenge solving\") &&\n !s.includes(\"javascript runtime\") &&\n !s.includes(\"js challenge\")\n ) {\n return \"\";\n }\n return (\n \"\\n\\nYouTube JS challenge (EJS): yt-dlp needs a JavaScript runtime for some formats. \" +\n \"Eliza adds `--js-runtimes` for Bun or Node when the API process uses them. \" +\n \"If errors persist, install Deno or set YTDLP_JS_RUNTIMES (see https://github.com/yt-dlp/yt-dlp/wiki/EJS).\"\n );\n}\n\n/** Whether a failed yt-dlp run is worth retrying with a more permissive `-f` selector. */\nexport function shouldRetryYtdlpWithPermissiveFormat(\n stderr: string,\n message: string,\n): boolean {\n const s = `${stderr}\\n${message}`.toLowerCase();\n if (\n s.includes(\"sign in to confirm\") ||\n s.includes(\"not a bot\") ||\n s.includes(\"private video\") ||\n s.includes(\"video unavailable\")\n ) {\n return false;\n }\n return (\n s.includes(\"requested format is not available\") ||\n s.includes(\"no suitable formats found\")\n );\n}\n","import { PassThrough, type Readable } from \"node:stream\";\nimport { logger } from \"@elizaos/core\";\n\n/** Minimal VoiceTarget shape — avoids hard dep on @elizaos/plugin-discord. */\ninterface VoiceTarget {\n id: string;\n type: string;\n guildId?: string;\n channelId?: string;\n play?: (stream: Readable, opts?: Record<string, unknown>) => Promise<unknown>;\n playAudio?: (\n stream: Readable,\n opts?: Record<string, unknown>,\n ) => Promise<unknown>;\n feed?: (stream: Readable) => Promise<unknown>;\n stop?: () => Promise<unknown>;\n stopAudio?: () => Promise<unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Audio routing mode\n */\nexport type AudioRoutingMode = \"simulcast\" | \"independent\";\n\n/**\n * Configuration for audio routing\n */\nexport interface AudioRouteConfig {\n sourceId: string;\n targetIds: string[];\n mode?: AudioRoutingMode;\n}\n\n/**\n * Active audio route state\n */\ninterface ActiveRoute {\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n sourceStream: Readable;\n streams: Map<string, PassThrough>; // target ID -> cloned stream\n cleanup: () => void;\n}\n\n/**\n * AudioRouter manages routing of audio streams to multiple voice targets\n * Supports both simulcast (same stream to all) and independent (separate streams) modes\n */\nexport class AudioRouter {\n private routes: Map<string, ActiveRoute> = new Map(); // sourceId -> route\n private defaultMode: AudioRoutingMode = \"simulcast\";\n private targetRegistry: Map<string, VoiceTarget> = new Map();\n\n constructor(defaultMode: AudioRoutingMode = \"simulcast\") {\n this.defaultMode = defaultMode;\n }\n\n /**\n * Register voice targets for routing\n */\n registerTargets(targets: VoiceTarget[]): void {\n for (const target of targets) {\n this.targetRegistry.set(target.id, target);\n logger.debug(`[AudioRouter] Registered target: ${target.id}`);\n }\n }\n\n /**\n * Unregister voice targets\n */\n unregisterTarget(targetId: string): void {\n this.targetRegistry.delete(targetId);\n logger.debug(`[AudioRouter] Unregistered target: ${targetId}`);\n }\n\n /**\n * Get registered target by ID\n */\n getTarget(targetId: string): VoiceTarget | undefined {\n return this.targetRegistry.get(targetId);\n }\n\n /**\n * Route an audio stream to multiple targets\n * @param sourceId Unique identifier for the audio source\n * @param stream The audio stream to route\n * @param targetIds Array of target IDs to route to\n * @param mode Routing mode (defaults to configured default)\n */\n async route(\n sourceId: string,\n stream: Readable,\n targetIds: string[],\n mode?: AudioRoutingMode,\n onCleanup?: () => void,\n ): Promise<void> {\n const routingMode = mode || this.defaultMode;\n\n // Clean up existing route if any\n await this.unroute(sourceId);\n\n logger.log(\n `[AudioRouter] Routing ${sourceId} to ${targetIds.length} target(s) in ${routingMode} mode`,\n );\n\n // Validate targets exist\n const validTargets = targetIds\n .map((id) => this.targetRegistry.get(id))\n .filter((t): t is VoiceTarget => t !== undefined);\n\n if (validTargets.length === 0) {\n throw new Error(`No valid targets found for route ${sourceId}`);\n }\n\n if (validTargets.length < targetIds.length) {\n logger.warn(\n `[AudioRouter] Some targets not found. Requested: ${targetIds.length}, Found: ${validTargets.length}`,\n );\n }\n\n if (routingMode === \"simulcast\") {\n await this.routeSimulcast(sourceId, stream, validTargets, onCleanup);\n } else {\n await this.routeIndependent(sourceId, stream, validTargets, onCleanup);\n }\n }\n\n /**\n * Simulcast mode: Clone single stream to all targets\n * Uses PassThrough streams to multiplex\n */\n private async routeSimulcast(\n sourceId: string,\n sourceStream: Readable,\n targets: VoiceTarget[],\n onCleanup?: () => void,\n ): Promise<void> {\n const streams = new Map<string, PassThrough>();\n const playbackPromises: Promise<void>[] = [];\n\n // Create PassThrough stream for each target\n for (const target of targets) {\n const clonedStream = new PassThrough();\n streams.set(target.id, clonedStream);\n\n // Pipe source to cloned stream\n sourceStream.pipe(clonedStream);\n\n // Start playback on target\n playbackPromises.push(\n this.startTargetPlayback(target, clonedStream).catch((error) => {\n logger.error(\n `[AudioRouter] Playback failed on ${target.id}: ${error}`,\n );\n }),\n );\n }\n\n // Wait for all playback to start\n await Promise.all(playbackPromises);\n\n // Store route state\n const cleanup = () => {\n for (const stream of streams.values()) {\n sourceStream.unpipe(stream);\n stream.end();\n stream.destroy();\n }\n onCleanup?.();\n };\n\n this.routes.set(sourceId, {\n sourceId,\n targetIds: targets.map((t) => t.id),\n mode: \"simulcast\",\n sourceStream,\n streams,\n cleanup,\n });\n\n logger.log(\n `[AudioRouter] Simulcast route ${sourceId} established to ${targets.length} target(s)`,\n );\n }\n\n /**\n * Independent mode: Separate streams per target\n * Note: This requires the source to provide multiple independent streams\n * For now, this will use the same stream but track separately\n */\n private async routeIndependent(\n sourceId: string,\n sourceStream: Readable,\n targets: VoiceTarget[],\n onCleanup?: () => void,\n ): Promise<void> {\n const streams = new Map<string, PassThrough>();\n const playbackPromises: Promise<void>[] = [];\n\n // In independent mode, each target gets its own stream clone\n // This allows different playback states per target\n for (const target of targets) {\n const independentStream = new PassThrough();\n streams.set(target.id, independentStream);\n\n // Pipe source to each independent stream\n sourceStream.pipe(independentStream);\n\n // Start playback on target\n playbackPromises.push(\n this.startTargetPlayback(target, independentStream).catch((error) => {\n logger.error(\n `[AudioRouter] Independent playback failed on ${target.id}: ${error}`,\n );\n }),\n );\n }\n\n // Wait for all playback to start\n await Promise.all(playbackPromises);\n\n // Store route state\n const cleanup = () => {\n for (const stream of streams.values()) {\n sourceStream.unpipe(stream);\n stream.end();\n stream.destroy();\n }\n onCleanup?.();\n };\n\n this.routes.set(sourceId, {\n sourceId,\n targetIds: targets.map((t) => t.id),\n mode: \"independent\",\n sourceStream,\n streams,\n cleanup,\n });\n\n logger.log(\n `[AudioRouter] Independent route ${sourceId} established to ${targets.length} target(s)`,\n );\n }\n\n /**\n * Stop routing for a source\n */\n async unroute(sourceId: string): Promise<void> {\n const route = this.routes.get(sourceId);\n if (!route) {\n return;\n }\n\n logger.log(`[AudioRouter] Unrouting ${sourceId}`);\n\n // Stop playback on all targets\n const stopPromises = route.targetIds.map(async (targetId) => {\n const target = this.targetRegistry.get(targetId);\n if (target) {\n try {\n await this.stopTargetPlayback(target);\n } catch (error) {\n logger.error(`[AudioRouter] Failed to stop ${targetId}: ${error}`);\n }\n }\n });\n\n await Promise.all(stopPromises);\n\n // Cleanup streams\n route.cleanup();\n\n // Remove route\n this.routes.delete(sourceId);\n\n logger.log(`[AudioRouter] Route ${sourceId} removed`);\n }\n\n /**\n * Stop all active routes\n */\n async unrouteAll(): Promise<void> {\n const sourceIds = Array.from(this.routes.keys());\n await Promise.all(sourceIds.map((id) => this.unroute(id)));\n }\n\n /**\n * Get active routes\n */\n getActiveRoutes(): Array<{\n sourceId: string;\n targetIds: string[];\n mode: AudioRoutingMode;\n }> {\n return Array.from(this.routes.values()).map((route) => ({\n sourceId: route.sourceId,\n targetIds: route.targetIds,\n mode: route.mode,\n }));\n }\n\n /**\n * Set default routing mode\n */\n setDefaultMode(mode: AudioRoutingMode): void {\n this.defaultMode = mode;\n logger.log(`[AudioRouter] Default mode set to: ${mode}`);\n }\n\n /**\n * Get default routing mode\n */\n getDefaultMode(): AudioRoutingMode {\n return this.defaultMode;\n }\n\n /**\n * Get all registered routing target IDs\n */\n getRegisteredTargetIds(): string[] {\n return Array.from(this.targetRegistry.keys());\n }\n\n /**\n * Check if a source is currently routed\n */\n isRouted(sourceId: string): boolean {\n return this.routes.has(sourceId);\n }\n\n /**\n * Get route info for a source\n */\n getRoute(sourceId: string) {\n const route = this.routes.get(sourceId);\n if (!route) return undefined;\n\n return {\n sourceId: route.sourceId,\n targetIds: route.targetIds,\n mode: route.mode,\n };\n }\n\n /**\n * Add target to existing route\n */\n async addTargetToRoute(sourceId: string, targetId: string): Promise<void> {\n const route = this.routes.get(sourceId);\n if (!route) {\n throw new Error(`Route ${sourceId} not found`);\n }\n\n const target = this.targetRegistry.get(targetId);\n if (!target) {\n throw new Error(`Target ${targetId} not found`);\n }\n\n if (route.targetIds.includes(targetId)) {\n logger.warn(\n `[AudioRouter] Target ${targetId} already in route ${sourceId}`,\n );\n return;\n }\n\n // Create new stream for this target\n const newStream = new PassThrough();\n\n route.sourceStream.pipe(newStream);\n await this.startTargetPlayback(target, newStream);\n\n route.targetIds.push(targetId);\n route.streams.set(targetId, newStream);\n logger.log(`[AudioRouter] Added target ${targetId} to route ${sourceId}`);\n }\n\n /**\n * Remove target from existing route\n */\n async removeTargetFromRoute(\n sourceId: string,\n targetId: string,\n ): Promise<void> {\n const route = this.routes.get(sourceId);\n if (!route) {\n throw new Error(`Route ${sourceId} not found`);\n }\n\n const targetIndex = route.targetIds.indexOf(targetId);\n if (targetIndex === -1) {\n logger.warn(`[AudioRouter] Target ${targetId} not in route ${sourceId}`);\n return;\n }\n\n // Stop playback on this target\n const target = this.targetRegistry.get(targetId);\n if (target) {\n await this.stopTargetPlayback(target);\n }\n\n // Clean up stream\n const stream = route.streams.get(targetId);\n if (stream) {\n stream.end();\n stream.destroy();\n route.streams.delete(targetId);\n }\n\n // Remove from route\n route.targetIds.splice(targetIndex, 1);\n\n logger.log(\n `[AudioRouter] Removed target ${targetId} from route ${sourceId}`,\n );\n\n // If no targets left, unroute completely\n if (route.targetIds.length === 0) {\n await this.unroute(sourceId);\n }\n }\n\n private async startTargetPlayback(\n target: VoiceTarget,\n stream: Readable,\n ): Promise<void> {\n if (typeof target.play === \"function\") {\n await target.play(stream);\n return;\n }\n if (typeof target.playAudio === \"function\") {\n await target.playAudio(stream);\n return;\n }\n if (typeof target.feed === \"function\") {\n await target.feed(stream);\n return;\n }\n throw new Error(\n `Target ${target.id} does not expose play(), playAudio(), or feed()`,\n );\n }\n\n private async stopTargetPlayback(target: VoiceTarget): Promise<void> {\n if (typeof target.stop === \"function\") {\n await target.stop();\n return;\n }\n if (typeof target.stopAudio === \"function\") {\n await target.stopAudio();\n }\n }\n}\n","import { type IAgentRuntime, logger } from \"@elizaos/core\";\nimport type { AudioRoutingMode } from \"./audioRouter\";\n\n/**\n * Configuration for a mix session\n */\nexport interface MixConfig {\n name: string;\n zones: Record<string, string[]>; // zoneName -> targetIds\n routing?: {\n mode: AudioRoutingMode;\n mappings: Record<string, string[]>; // sourceId -> zoneNames\n };\n autoCleanup?: boolean;\n cleanupDelay?: number; // milliseconds\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Active mix session state\n */\nexport interface MixSession {\n id: string;\n config: MixConfig;\n startedAt: number;\n cleanupTimeout?: NodeJS.Timeout;\n}\n\n/**\n * MixSessionManager handles runtime mixing configurations\n * Manages temporary bot assignments, routing, and auto-cleanup\n */\nexport class MixSessionManager {\n private sessions: Map<string, MixSession> = new Map();\n private runtime: IAgentRuntime;\n\n constructor(runtime: IAgentRuntime) {\n this.runtime = runtime;\n }\n\n /**\n * Start a new mix session\n * @param config Mix configuration\n * @returns Created session\n */\n async start(config: MixConfig): Promise<MixSession> {\n const sessionId = `mix-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n logger.log(\n `[MixSessionManager] Starting session: ${config.name} (${sessionId})`,\n );\n\n const session: MixSession = {\n id: sessionId,\n config,\n startedAt: Date.now(),\n };\n\n // Set up auto-cleanup if enabled\n if (config.autoCleanup && config.cleanupDelay) {\n session.cleanupTimeout = setTimeout(() => {\n this.end(sessionId).catch((error) => {\n logger.error(\n `[MixSessionManager] Auto-cleanup failed for ${sessionId}: ${error}`,\n );\n });\n }, config.cleanupDelay);\n\n logger.log(\n `[MixSessionManager] Auto-cleanup scheduled for ${sessionId} in ${config.cleanupDelay}ms`,\n );\n }\n\n this.sessions.set(sessionId, session);\n\n // Store in agent memory for persistence\n await this.saveToMemory(session);\n\n logger.log(\n `[MixSessionManager] Session ${sessionId} started with ${Object.keys(config.zones).length} zone(s)`,\n );\n\n return session;\n }\n\n /**\n * End a mix session\n * @param sessionId Session ID to end\n */\n async end(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n logger.warn(`[MixSessionManager] Session ${sessionId} not found`);\n return;\n }\n\n logger.log(\n `[MixSessionManager] Ending session: ${session.config.name} (${sessionId})`,\n );\n\n // Clear cleanup timeout if exists\n if (session.cleanupTimeout) {\n clearTimeout(session.cleanupTimeout);\n }\n\n // Remove from active sessions\n this.sessions.delete(sessionId);\n\n // Remove from agent memory\n await this.removeFromMemory(sessionId);\n\n logger.log(`[MixSessionManager] Session ${sessionId} ended`);\n }\n\n /**\n * Update an existing session\n * @param sessionId Session ID to update\n * @param config Partial configuration to update\n */\n async update(\n sessionId: string,\n config: Partial<MixConfig>,\n ): Promise<MixSession> {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new Error(`Session ${sessionId} not found`);\n }\n\n logger.log(`[MixSessionManager] Updating session: ${sessionId}`);\n\n // Merge configuration\n session.config = {\n ...session.config,\n ...config,\n zones: { ...session.config.zones, ...config.zones },\n routing: config.routing\n ? { ...session.config.routing, ...config.routing }\n : session.config.routing,\n metadata: { ...session.config.metadata, ...config.metadata },\n };\n\n // Update in memory\n await this.saveToMemory(session);\n\n logger.log(`[MixSessionManager] Session ${sessionId} updated`);\n\n return session;\n }\n\n /**\n * Get a session by ID\n */\n get(sessionId: string): MixSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n /**\n * Get all active sessions\n */\n list(): MixSession[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Find sessions by name\n */\n findByName(name: string): MixSession[] {\n return Array.from(this.sessions.values()).filter(\n (s) => s.config.name === name,\n );\n }\n\n /**\n * End all sessions\n */\n async endAll(): Promise<void> {\n logger.log(\"[MixSessionManager] Ending all sessions\");\n const sessionIds = Array.from(this.sessions.keys());\n await Promise.all(sessionIds.map((id) => this.end(id)));\n }\n\n /**\n * Check if a session exists\n */\n exists(sessionId: string): boolean {\n return this.sessions.has(sessionId);\n }\n\n /**\n * Get active session count\n */\n count(): number {\n return this.sessions.size;\n }\n\n /**\n * Extend session cleanup timer\n * @param sessionId Session ID\n * @param additionalTime Additional time in milliseconds\n */\n extendSession(sessionId: string, additionalTime: number): void {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new Error(`Session ${sessionId} not found`);\n }\n\n if (!session.cleanupTimeout) {\n logger.warn(\n `[MixSessionManager] Session ${sessionId} has no cleanup timer`,\n );\n return;\n }\n\n // Clear old timeout\n clearTimeout(session.cleanupTimeout);\n\n // Set new timeout\n session.cleanupTimeout = setTimeout(() => {\n this.end(sessionId).catch((error) => {\n logger.error(\n `[MixSessionManager] Auto-cleanup failed for ${sessionId}: ${error}`,\n );\n });\n }, additionalTime);\n\n logger.log(\n `[MixSessionManager] Extended session ${sessionId} by ${additionalTime}ms`,\n );\n }\n\n /**\n * Cancel auto-cleanup for a session\n */\n cancelAutoCleanup(sessionId: string): void {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new Error(`Session ${sessionId} not found`);\n }\n\n if (session.cleanupTimeout) {\n clearTimeout(session.cleanupTimeout);\n delete session.cleanupTimeout;\n logger.log(\n `[MixSessionManager] Cancelled auto-cleanup for session ${sessionId}`,\n );\n }\n }\n\n /**\n * Save session to agent memory\n * @private\n */\n private async saveToMemory(session: MixSession): Promise<void> {\n try {\n await this.runtime.createMemory(\n {\n id: this.runtime.agentId,\n agentId: this.runtime.agentId,\n entityId: this.runtime.agentId,\n roomId: this.runtime.agentId,\n content: {\n text: `Mix session: ${session.config.name}`,\n metadata: {\n type: \"custom\" as const,\n kind: \"mixSession\",\n sessionId: session.id,\n config: JSON.parse(JSON.stringify(session.config)),\n startedAt: session.startedAt,\n },\n },\n createdAt: Date.now(),\n },\n \"mixSessions\",\n );\n } catch (error) {\n logger.error(\n `[MixSessionManager] Failed to save session to memory: ${error}`,\n );\n }\n }\n\n /**\n * Remove session from agent memory\n * @private\n */\n private async removeFromMemory(sessionId: string): Promise<void> {\n try {\n // Note: This is a simplified implementation\n // In a real implementation, we'd query and delete the specific memory\n logger.debug(\n `[MixSessionManager] Removing session ${sessionId} from memory`,\n );\n } catch (error) {\n logger.error(\n `[MixSessionManager] Failed to remove session from memory: ${error}`,\n );\n }\n }\n\n /**\n * Load sessions from agent memory on startup\n */\n async loadFromMemory(): Promise<void> {\n try {\n logger.log(\"[MixSessionManager] Loading sessions from memory\");\n // Note: This is a simplified implementation\n // In a real implementation, we'd query the database for saved sessions\n } catch (error) {\n logger.error(\n `[MixSessionManager] Failed to load sessions from memory: ${error}`,\n );\n }\n }\n\n /**\n * Get session duration in milliseconds\n */\n getSessionDuration(sessionId: string): number {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new Error(`Session ${sessionId} not found`);\n }\n return Date.now() - session.startedAt;\n }\n\n /**\n * Get session info summary\n */\n getSessionInfo(sessionId: string):\n | {\n id: string;\n name: string;\n duration: number;\n zoneCount: number;\n hasAutoCleanup: boolean;\n }\n | undefined {\n const session = this.sessions.get(sessionId);\n if (!session) return undefined;\n\n return {\n id: session.id,\n name: session.config.name,\n duration: this.getSessionDuration(sessionId),\n zoneCount: Object.keys(session.config.zones).length,\n hasAutoCleanup: !!session.cleanupTimeout,\n };\n }\n}\n","import { logger } from \"@elizaos/core\";\n\n/**\n * A named group of voice targets\n */\nexport interface Zone {\n name: string;\n targetIds: string[];\n createdAt: number;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * ZoneManager manages logical groupings of voice targets\n * Zones allow routing audio to named collections of targets\n */\nexport class ZoneManager {\n private zones: Map<string, Zone> = new Map();\n\n /**\n * Create a new zone\n * @param name Zone name\n * @param targetIds Array of target IDs in this zone\n * @param metadata Optional metadata for the zone\n */\n create(\n name: string,\n targetIds: string[],\n metadata?: Record<string, unknown>,\n ): Zone {\n if (this.zones.has(name)) {\n throw new Error(`Zone ${name} already exists`);\n }\n\n const zone: Zone = {\n name,\n targetIds: [...targetIds], // Clone array\n createdAt: Date.now(),\n metadata,\n };\n\n this.zones.set(name, zone);\n logger.log(\n `[ZoneManager] Created zone: ${name} with ${targetIds.length} target(s)`,\n );\n\n return zone;\n }\n\n /**\n * Delete a zone\n */\n delete(name: string): boolean {\n const deleted = this.zones.delete(name);\n if (deleted) {\n logger.log(`[ZoneManager] Deleted zone: ${name}`);\n }\n return deleted;\n }\n\n /**\n * Get a zone by name\n */\n get(name: string): Zone | undefined {\n return this.zones.get(name);\n }\n\n /**\n * Get all zones\n */\n list(): Zone[] {\n return Array.from(this.zones.values());\n }\n\n /**\n * Check if a zone exists\n */\n exists(name: string): boolean {\n return this.zones.has(name);\n }\n\n /**\n * Add target to a zone\n */\n addTarget(zoneName: string, targetId: string): void {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n\n if (zone.targetIds.includes(targetId)) {\n logger.warn(\n `[ZoneManager] Target ${targetId} already in zone ${zoneName}`,\n );\n return;\n }\n\n zone.targetIds.push(targetId);\n logger.log(`[ZoneManager] Added target ${targetId} to zone ${zoneName}`);\n }\n\n /**\n * Remove target from a zone\n */\n removeTarget(zoneName: string, targetId: string): void {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n\n const index = zone.targetIds.indexOf(targetId);\n if (index === -1) {\n logger.warn(`[ZoneManager] Target ${targetId} not in zone ${zoneName}`);\n return;\n }\n\n zone.targetIds.splice(index, 1);\n logger.log(\n `[ZoneManager] Removed target ${targetId} from zone ${zoneName}`,\n );\n }\n\n /**\n * Get all target IDs in a zone\n */\n getTargets(zoneName: string): string[] {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n return [...zone.targetIds]; // Return clone\n }\n\n /**\n * Update zone metadata\n */\n updateMetadata(zoneName: string, metadata: Record<string, unknown>): void {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n\n zone.metadata = { ...zone.metadata, ...metadata };\n logger.log(`[ZoneManager] Updated metadata for zone ${zoneName}`);\n }\n\n /**\n * Find all zones containing a specific target\n */\n findZonesWithTarget(targetId: string): Zone[] {\n const result: Zone[] = [];\n for (const zone of this.zones.values()) {\n if (zone.targetIds.includes(targetId)) {\n result.push(zone);\n }\n }\n return result;\n }\n\n /**\n * Clear all zones\n */\n clear(): void {\n this.zones.clear();\n logger.log(\"[ZoneManager] Cleared all zones\");\n }\n\n /**\n * Get zone count\n */\n count(): number {\n return this.zones.size;\n }\n\n /**\n * Merge multiple zones into a new zone\n */\n merge(newZoneName: string, zoneNames: string[]): Zone {\n const targetIds = new Set<string>();\n\n for (const zoneName of zoneNames) {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n for (const targetId of zone.targetIds) {\n targetIds.add(targetId);\n }\n }\n\n return this.create(newZoneName, Array.from(targetIds), {\n mergedFrom: zoneNames,\n });\n }\n\n /**\n * Clone a zone with a new name\n */\n clone(sourceName: string, newName: string): Zone {\n const source = this.zones.get(sourceName);\n if (!source) {\n throw new Error(`Zone ${sourceName} not found`);\n }\n\n return this.create(newName, [...source.targetIds], {\n ...source.metadata,\n clonedFrom: sourceName,\n });\n }\n\n /**\n * Get targets that are in multiple zones (intersection)\n */\n getIntersection(zoneNames: string[]): string[] {\n if (zoneNames.length === 0) return [];\n\n const firstZone = this.zones.get(zoneNames[0]);\n if (!firstZone) {\n throw new Error(`Zone ${zoneNames[0]} not found`);\n }\n\n let intersection = new Set(firstZone.targetIds);\n\n for (let i = 1; i < zoneNames.length; i++) {\n const zone = this.zones.get(zoneNames[i]);\n if (!zone) {\n throw new Error(`Zone ${zoneNames[i]} not found`);\n }\n intersection = new Set(\n [...intersection].filter((id) => zone.targetIds.includes(id)),\n );\n }\n\n return Array.from(intersection);\n }\n\n /**\n * Get all unique targets across multiple zones (union)\n */\n getUnion(zoneNames: string[]): string[] {\n const union = new Set<string>();\n\n for (const zoneName of zoneNames) {\n const zone = this.zones.get(zoneName);\n if (!zone) {\n throw new Error(`Zone ${zoneName} not found`);\n }\n for (const targetId of zone.targetIds) {\n union.add(targetId);\n }\n }\n\n return Array.from(union);\n }\n}\n","import { exec, execFile } from \"node:child_process\";\nimport {\n createReadStream,\n existsSync,\n mkdirSync,\n readdirSync,\n statSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport { promisify } from \"node:util\";\nimport { logger } from \"@elizaos/core\";\nimport {\n augmentEnvWithFfmpegTools,\n resolveFfprobeBinaryPath,\n} from \"../utils/ffmpegEnv\";\nimport { formatMusicDebugCommand, musicDebug } from \"../utils/musicDebug\";\nimport { getYtdlpPath, YTDLP_INSTALL_INSTRUCTIONS } from \"../utils/ytdlpCheck\";\nimport { getYtdlpJsRuntimeShellFragment } from \"../utils/ytdlpCli\";\nimport {\n getYoutubeExtractorShellFragment,\n shouldRetryYtdlpWithPermissiveFormat,\n} from \"../utils/ytdlpYoutube\";\n\n/**\n * Get YouTube cookies file path from environment or return null\n */\nfunction getYouTubeCookiesPath(): string | null {\n // Check environment variable first\n const cookiesPath = process.env.YOUTUBE_COOKIES || process.env.YTDLP_COOKIES;\n if (cookiesPath && existsSync(cookiesPath)) {\n logger.debug(`Using YouTube cookies file: ${cookiesPath}`);\n return cookiesPath;\n }\n return null;\n}\n\n/**\n * Get proxy URL from environment or return null\n * Supports HTTP, HTTPS, and SOCKS proxies\n */\nfunction getProxyUrl(): string | null {\n // Check multiple environment variable names for proxy\n const proxyUrl =\n process.env.YOUTUBE_PROXY ||\n process.env.YTDLP_PROXY ||\n process.env.HTTP_PROXY ||\n process.env.HTTPS_PROXY ||\n process.env.http_proxy ||\n process.env.https_proxy;\n\n if (proxyUrl) {\n // Validate proxy URL format\n try {\n const url = new URL(proxyUrl);\n if ([\"http:\", \"https:\", \"socks4:\", \"socks5:\"].includes(url.protocol)) {\n logger.debug(`Using proxy: ${proxyUrl}`);\n return proxyUrl;\n } else {\n logger.warn(\n `Invalid proxy protocol: ${url.protocol}. Supported: http, https, socks4, socks5`,\n );\n }\n } catch (_error) {\n logger.warn(`Invalid proxy URL format: ${proxyUrl}`);\n }\n }\n return null;\n}\n\n/** Matches streaming fallback in ytdlpFallback when strict `-f` has no match. */\nconst YTDLP_CACHE_PERMISSIVE_FORMAT =\n \"bestaudio/best[height<=720]/best[height<=480]/best\";\n\nfunction buildYtdlpCacheDownloadCommand(opts: {\n ytdlpPath: string;\n youtubeUrl: string;\n cacheFilePath: string;\n format: string;\n formatSelector: string;\n proxyUrl: string | null;\n cookiesPath: string | null;\n}): string {\n let command = opts.ytdlpPath;\n command += getYtdlpJsRuntimeShellFragment();\n command += getYoutubeExtractorShellFragment(opts.youtubeUrl);\n command += ` -f \"${opts.formatSelector}\" -x --audio-format ${opts.format}`;\n if (opts.format !== \"flac\") {\n command += ` --audio-quality 0`;\n }\n command += ` --no-playlist`;\n if (opts.proxyUrl) {\n command += ` --proxy \"${opts.proxyUrl}\"`;\n }\n if (opts.cookiesPath) {\n command += ` --cookies \"${opts.cookiesPath}\"`;\n }\n command += ` -o \"${opts.cacheFilePath}\" \"${opts.youtubeUrl}\"`;\n return command;\n}\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\nexport interface AudioFileInfo {\n filePath: string;\n size: number;\n duration?: number; // Duration in seconds\n bitrate?: number; // Bitrate in kbps\n format?: string; // Audio format (e.g., 'opus', 'webm')\n sampleRate?: number; // Sample rate in Hz\n channels?: number; // Number of audio channels\n}\n\nexport interface AudioCacheKey {\n artist?: string;\n album?: string;\n song: string;\n quality: \"low\" | \"medium\" | \"high\" | \"highest\";\n url: string; // YouTube URL as fallback identifier\n}\n\nexport interface AudioCacheEntry {\n filePath: string;\n cachedAt: number;\n size: number;\n format: string;\n}\n\n/**\n * Audio cache service that downloads, converts, and caches audio files\n *\n * ## Overview\n * This service manages a file-based cache of audio files downloaded from YouTube.\n * Audio is downloaded using yt-dlp and converted to OGG Opus format, which is:\n * - Native to Discord voice (no transcoding needed)\n * - Efficiently compressed (smaller cache size)\n * - High quality (VBR encoding)\n * - Web-compatible (supported by modern browsers)\n *\n * ## Caching Strategy\n * Files are cached based on artist/album/song/quality and stored in the configured\n * cache directory. Each cached file includes metadata (duration, bitrate, etc.)\n * obtained via ffprobe.\n *\n * ## Stream Handling (CRITICAL)\n * ⚠️ This service creates clean, unmodified file streams. DO NOT add event listeners\n * or call resume() on these streams - adding listeners (especially 'readable' and 'data')\n * puts streams into paused mode and prevents Discord.js from controlling stream flow.\n *\n * Let the consumer (Discord.js) handle all stream control, probing, and consumption.\n *\n * @example\n * ```typescript\n * const cache = new AudioCacheService('/path/to/cache');\n * const key = { song: 'Track Name', quality: 'high', url: 'https://...' };\n * const stream = await cache.getAudioStream(key, 'https://youtube.com/...');\n * // Pass stream directly to Discord.js - don't touch it!\n * voiceManager.playAudio(stream, { guildId, channel });\n * ```\n */\nexport class AudioCacheService {\n private cacheDir: string;\n private readonly CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days\n private cacheIndex: Map<string, AudioCacheEntry> = new Map();\n\n constructor(cacheDir?: string) {\n // Default to ./cache/audio in the project root, or use provided path\n this.cacheDir = cacheDir || join(process.cwd(), \"cache\", \"audio\");\n\n // Ensure cache directory exists\n if (!existsSync(this.cacheDir)) {\n mkdirSync(this.cacheDir, { recursive: true });\n logger.debug(`Created audio cache directory: ${this.cacheDir}`);\n }\n\n // Load existing cache index\n this.loadCacheIndex();\n\n // Clean up expired entries on startup\n this.cleanExpiredCache();\n }\n\n /**\n * Generate cache key from track metadata\n */\n private generateCacheKey(key: AudioCacheKey): string {\n // Sanitize strings for filesystem\n const sanitize = (str: string | undefined): string => {\n if (!str) return \"unknown\";\n return str\n .replace(/[^a-zA-Z0-9]/g, \"_\")\n .replace(/_+/g, \"_\")\n .toLowerCase()\n .substring(0, 100); // Limit length\n };\n\n const parts = [\n sanitize(key.artist),\n sanitize(key.album),\n sanitize(key.song),\n key.quality,\n ];\n\n // Normalize URL before hashing to ensure consistent cache keys\n // WHY: Same video can have different URL formats (youtube.com, youtu.be, with/without params)\n const normalizedUrl = this.normalizeUrlForCache(key.url);\n const urlHash = this.hashString(normalizedUrl).substring(0, 8);\n parts.push(urlHash);\n\n return parts.join(\"_\");\n }\n\n /**\n * Simple hash function for URLs\n */\n private hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16);\n }\n\n /**\n * Extract YouTube video ID from various URL formats\n * WHY: YouTube URLs can come in many forms but point to the same video:\n * - https://www.youtube.com/watch?v=ABC123\n * - https://youtube.com/watch?v=ABC123\n * - https://youtu.be/ABC123\n * - https://www.youtube.com/watch?v=ABC123&list=PLxyz&index=1\n *\n * We need to normalize these to get consistent cache keys\n */\n private extractYouTubeVideoId(url: string): string | null {\n try {\n // Handle youtu.be short URLs\n const shortMatch = url.match(/youtu\\.be\\/([a-zA-Z0-9_-]{11})/);\n if (shortMatch) {\n return shortMatch[1];\n }\n\n // Handle youtube.com/watch?v= URLs\n const watchMatch = url.match(\n /youtube\\.com\\/watch\\?.*v=([a-zA-Z0-9_-]{11})/,\n );\n if (watchMatch) {\n return watchMatch[1];\n }\n\n // Handle youtube.com/embed/ URLs\n const embedMatch = url.match(/youtube\\.com\\/embed\\/([a-zA-Z0-9_-]{11})/);\n if (embedMatch) {\n return embedMatch[1];\n }\n\n // Handle youtube.com/v/ URLs\n const vMatch = url.match(/youtube\\.com\\/v\\/([a-zA-Z0-9_-]{11})/);\n if (vMatch) {\n return vMatch[1];\n }\n\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Normalize URL for consistent caching\n * WHY: Same video can have different URL formats - we want them to hit the same cache entry\n */\n private normalizeUrlForCache(url: string): string {\n const videoId = this.extractYouTubeVideoId(url);\n if (videoId) {\n // Use canonical YouTube URL format for consistent hashing\n return `youtube:${videoId}`;\n }\n // For non-YouTube URLs, use the full URL\n return url;\n }\n\n /**\n * Get cache file path for a key\n */\n private getCacheFilePath(key: AudioCacheKey, format?: string): string {\n const cacheKey = this.generateCacheKey(key);\n // Determine file extension based on format\n const audioFormat = (\n format ||\n process.env.AUDIO_CACHE_FORMAT ||\n \"opus\"\n ).toLowerCase();\n const formatExt =\n audioFormat === \"flac\"\n ? \"flac\"\n : audioFormat === \"wav\"\n ? \"wav\"\n : audioFormat === \"mp3\"\n ? \"mp3\"\n : audioFormat === \"m4a\"\n ? \"m4a\"\n : \"opus\";\n return join(this.cacheDir, `${cacheKey}.${formatExt}`);\n }\n\n /**\n * Check if audio is cached (checks all supported formats)\n */\n isCached(key: AudioCacheKey): boolean {\n // Check for cached file in any supported format using new normalized URL\n const supportedFormats = [\"opus\", \"flac\", \"wav\", \"mp3\", \"m4a\"];\n for (const format of supportedFormats) {\n const filePath = this.getCacheFilePath(key, format);\n const entry = this.cacheIndex.get(filePath);\n\n if (entry && existsSync(filePath)) {\n // Check if expired\n const age = Date.now() - entry.cachedAt;\n if (age <= this.CACHE_TTL) {\n logger.info(`[CACHE HIT] ${key.song} (${format})`);\n return true;\n }\n }\n }\n\n // Fallback: Search cache directory for files with matching URL hash\n // WHY: Older cached files may use non-normalized URL hash\n try {\n if (existsSync(this.cacheDir)) {\n const normalizedUrl = this.normalizeUrlForCache(key.url);\n const normalizedUrlHash = this.hashString(normalizedUrl).substring(\n 0,\n 8,\n );\n const rawUrlHash = this.hashString(key.url).substring(0, 8);\n\n const files = readdirSync(this.cacheDir);\n const supportedExtensions = [\".opus\", \".flac\", \".wav\", \".mp3\", \".m4a\"];\n\n // Check for files matching either hash\n for (const file of files) {\n if (!supportedExtensions.some((ext) => file.endsWith(ext))) continue;\n\n if (file.includes(normalizedUrlHash) || file.includes(rawUrlHash)) {\n const filePath = join(this.cacheDir, file);\n const entry = this.cacheIndex.get(filePath);\n if (entry && existsSync(filePath)) {\n const age = Date.now() - entry.cachedAt;\n if (age <= this.CACHE_TTL) {\n logger.info(\n `[CACHE HIT] ${key.song} (found by URL hash search)`,\n );\n return true;\n }\n }\n }\n }\n }\n } catch (error) {\n logger.debug(`Error searching cache by URL hash: ${error}`);\n }\n\n logger.info(`[CACHE MISS] ${key.song}`);\n return false;\n }\n\n /**\n * Find cached audio by URL (searches all cached files)\n * This is useful when you only have the URL and don't know the exact cache key\n */\n async findCachedAudioByUrl(\n url: string,\n quality: \"low\" | \"medium\" | \"high\" | \"highest\" = \"high\",\n ): Promise<Readable | null> {\n // Normalize URL before searching to match cached files\n const normalizedUrl = this.normalizeUrlForCache(url);\n const normalizedUrlHash = this.hashString(normalizedUrl).substring(0, 8);\n\n // Also calculate the old-format hash (raw URL) for migration/fallback\n // WHY: Existing cached files may use the old non-normalized URL hash\n const rawUrlHash = this.hashString(url).substring(0, 8);\n\n // Search cache directory for files containing either URL hash\n try {\n if (!existsSync(this.cacheDir)) {\n return null;\n }\n\n const files = readdirSync(this.cacheDir);\n // Support multiple audio formats when searching by URL\n const supportedExtensions = [\".opus\", \".flac\", \".wav\", \".mp3\", \".m4a\"];\n\n // Try normalized hash first (new format), then fall back to raw hash (old format)\n let matchingFiles = files.filter(\n (file) =>\n supportedExtensions.some((ext) => file.endsWith(ext)) &&\n file.includes(normalizedUrlHash),\n );\n\n // Fallback: try old-format hash if normalized didn't find anything\n if (matchingFiles.length === 0 && rawUrlHash !== normalizedUrlHash) {\n logger.debug(\n `[CACHE] No match with normalized hash ${normalizedUrlHash}, trying raw hash ${rawUrlHash}`,\n );\n matchingFiles = files.filter(\n (file) =>\n supportedExtensions.some((ext) => file.endsWith(ext)) &&\n file.includes(rawUrlHash),\n );\n if (matchingFiles.length > 0) {\n logger.debug(`[CACHE] Found match with old-format URL hash`);\n }\n }\n\n if (matchingFiles.length === 0) {\n logger.info(\n `[CACHE MISS] No cached file found for URL hashes: ${normalizedUrlHash} / ${rawUrlHash}`,\n );\n return null;\n }\n\n // Try to find a file matching the quality, or use the first match\n let matchedFile: string | null = null;\n for (const file of matchingFiles) {\n if (file.includes(`_${quality}_`) || file.includes(`_${quality}.`)) {\n matchedFile = file;\n break;\n }\n }\n\n if (!matchedFile && matchingFiles.length > 0) {\n matchedFile = matchingFiles[0];\n logger.debug(\n `Using first matching cached file: ${matchedFile} (quality may not match)`,\n );\n }\n\n if (matchedFile) {\n const filePath = join(this.cacheDir, matchedFile);\n if (existsSync(filePath)) {\n const stats = statSync(filePath);\n if (stats.size > 0) {\n logger.info(`[CACHE HIT] Found by URL: ${matchedFile}`);\n\n // Verify file is complete using ffprobe before creating stream\n try {\n const probeInfo = await this.probeAudioFile(filePath);\n if (probeInfo.duration && probeInfo.duration >= 1) {\n const minutes = Math.floor(probeInfo.duration / 60);\n const seconds = Math.floor(probeInfo.duration % 60);\n logger.info(\n `[CACHE] File duration: ${minutes}:${seconds.toString().padStart(2, \"0\")} (${probeInfo.duration.toFixed(2)}s)`,\n );\n } else {\n logger.warn(\n `[CACHE] File appears incomplete (duration: ${probeInfo.duration}s): ${matchedFile}`,\n );\n }\n } catch (probeError) {\n logger.debug(\n `[CACHE] Could not verify file with ffprobe: ${probeError}`,\n );\n }\n\n // Create stream directly from file (same logic as getCachedAudio)\n const stream = createReadStream(filePath, {\n highWaterMark: 64 * 1024, // 64KB buffer for better streaming\n autoClose: false, // Don't auto-close - let Discord.js control when to close\n });\n\n // Only add error listener - no 'readable', 'data', 'open', or 'pause' listeners\n // Adding these puts the stream in paused mode and breaks Discord.js stream control\n stream.on(\"error\", (error) => {\n // Only log non-cleanup errors (Premature close might be expected during cleanup)\n const errorMsg = error.message || String(error);\n if (!errorMsg.includes(\"Premature close\") || stream.readable) {\n logger.error(\n `Cached audio stream error for ${url}: ${errorMsg}`,\n );\n } else {\n logger.debug(\n `Cached audio stream closed during cleanup for ${url}: ${errorMsg}`,\n );\n }\n });\n\n // Log when stream ends/closes for debugging (but don't interfere with stream control)\n stream.on(\"end\", () => {\n logger.debug(\n `Cached audio stream ended normally for URL: ${url}`,\n );\n });\n stream.on(\"close\", () => {\n logger.debug(`Cached audio stream closed for URL: ${url}`);\n });\n\n logger.info(\n `[CACHE] Stream created for URL: ${url}, readable: ${stream.readable}, destroyed: ${stream.destroyed}`,\n );\n return stream;\n }\n }\n }\n } catch (error) {\n logger.debug(`Error searching cache by URL: ${error}`);\n }\n\n logger.info(`[CACHE MISS] No cached file found for URL: ${url}`);\n return null;\n }\n\n /**\n * Get cached audio file as a readable stream\n *\n * Returns a clean file stream with NO event listeners attached (except error handling).\n * The stream is in paused mode by default - the consumer must control flow.\n *\n * ⚠️ WARNING: Do not add event listeners or call resume() on the returned stream.\n * Adding listeners puts the stream in paused mode and prevents proper playback.\n * Let Discord.js handle all stream control.\n *\n * @param key - Cache key identifying the audio file\n * @returns Clean readable stream, or null if not cached/invalid\n */\n async getCachedAudio(key: AudioCacheKey): Promise<Readable | null> {\n // Check for cached file in any supported format\n const supportedFormats = [\"opus\", \"flac\", \"wav\", \"mp3\", \"m4a\"];\n let filePath: string | null = null;\n\n // Try to find cached file in any format\n for (const format of supportedFormats) {\n const testPath = this.getCacheFilePath(key, format);\n if (existsSync(testPath)) {\n const entry = this.cacheIndex.get(testPath);\n if (entry) {\n const age = Date.now() - entry.cachedAt;\n if (age <= this.CACHE_TTL) {\n filePath = testPath;\n logger.debug(`Found cached audio in ${format} format: ${filePath}`);\n break;\n }\n }\n }\n }\n\n if (!filePath) {\n logger.info(`[CACHE MISS] ${key.song} - no cached file found`);\n return null;\n }\n try {\n // Verify file exists and is valid before creating stream\n if (!existsSync(filePath)) {\n logger.info(`[CACHE MISS] ${key.song} - file not found: ${filePath}`);\n return null;\n }\n\n const stats = statSync(filePath);\n if (stats.size === 0) {\n logger.warn(`[CACHE] Cached file is empty: ${filePath}`);\n return null;\n }\n\n logger.info(\n `[CACHE HIT] ${key.song} (${(stats.size / 1024 / 1024).toFixed(2)}MB)`,\n );\n\n // Verify file is complete using ffprobe BEFORE creating stream\n try {\n const probeInfo = await this.probeAudioFile(filePath);\n if (probeInfo.duration && probeInfo.duration >= 1) {\n const minutes = Math.floor(probeInfo.duration / 60);\n const seconds = Math.floor(probeInfo.duration % 60);\n logger.info(\n `[CACHE] File duration: ${minutes}:${seconds.toString().padStart(2, \"0\")} (${probeInfo.duration.toFixed(2)}s)`,\n );\n } else {\n logger.warn(\n `[CACHE] File appears incomplete (duration: ${probeInfo.duration}s): ${filePath}`,\n );\n // Still return the stream, but log the warning\n }\n } catch (probeError) {\n // ffprobe might not be available, that's okay\n logger.debug(\n `[CACHE] Could not verify cached file with ffprobe: ${probeError}`,\n );\n }\n\n // Create a clean file stream with optimal buffer size\n // CRITICAL: Do not add event listeners beyond 'error' - they interfere with Discord.js\n const stream = createReadStream(filePath, {\n highWaterMark: 64 * 1024, // 64KB buffer for better streaming\n autoClose: false, // Don't auto-close - let Discord.js control when to close\n });\n\n // Only add error listener - no 'readable', 'data', 'open', or 'pause' listeners\n // Adding these puts the stream in paused mode and breaks Discord.js stream control\n stream.on(\"error\", (error) => {\n logger.error(\n `Cached audio stream error for ${key.song}: ${error.message}`,\n );\n });\n\n // Log when stream ends normally (for debugging)\n stream.on(\"end\", () => {\n logger.debug(`Cached audio stream ended normally for: ${key.song}`);\n });\n\n // Log when stream closes (for debugging)\n stream.on(\"close\", () => {\n logger.debug(`Cached audio stream closed for: ${key.song}`);\n });\n\n logger.info(\n `[CACHE] Stream created for ${key.song}, readable: ${stream.readable}, destroyed: ${stream.destroyed}`,\n );\n\n return stream;\n } catch (error) {\n logger.error(`Error reading cached audio: ${error}`);\n return null;\n }\n }\n\n /**\n * Download and cache audio from YouTube URL\n *\n * Uses yt-dlp to download audio and convert to the configured format.\n * yt-dlp automatically invokes ffmpeg for format conversion.\n *\n * ## Process\n * 1. Check if already cached (skip if so)\n * 2. Download audio using yt-dlp with quality settings\n * 3. Convert to configured format (default: Opus, configurable via AUDIO_CACHE_FORMAT)\n * 4. Probe metadata with ffprobe (duration, bitrate, etc.)\n * 5. Update cache index\n *\n * ## Supported Formats\n * - opus: Discord-optimized, lossy but high quality (default)\n * - flac: Lossless format, prevents additional quality loss from lossy-to-lossy conversions (larger files)\n * Even though YouTube source is lossy, FLAC preserves the best quality available and prevents\n * further degradation from multiple lossy conversions (e.g., Opus→Opus or Opus→MP3)\n * - wav: Uncompressed lossless (very large files)\n * - mp3: Lossy, widely compatible\n * - m4a: Lossy, Apple format\n *\n * Set AUDIO_CACHE_FORMAT environment variable to choose format (e.g., export AUDIO_CACHE_FORMAT=flac)\n *\n * Note: Using FLAC prevents quality loss from lossy-to-lossy transcoding, preserving the best\n * quality available from the YouTube source even if the source itself is lossy.\n *\n * ## Quality Mapping\n * - low: worst available audio\n * - medium: up to 128kbps\n * - high: up to 192kbps\n * - highest: best available audio\n *\n * Note: FLAC is lossless, so quality settings don't apply (preserves source quality)\n *\n * @param key - Cache key for the audio\n * @param youtubeUrl - YouTube URL to download from\n * @returns Path to cached file\n * @throws Error if yt-dlp/ffmpeg not installed, download fails, or file invalid\n */\n async downloadAndCache(\n key: AudioCacheKey,\n youtubeUrl: string,\n ): Promise<string> {\n // Check if already cached in any format\n if (this.isCached(key)) {\n // Find the actual cached file path\n const supportedFormats = [\"opus\", \"flac\", \"wav\", \"mp3\", \"m4a\"];\n for (const format of supportedFormats) {\n const testPath = this.getCacheFilePath(key, format);\n if (existsSync(testPath)) {\n const entry = this.cacheIndex.get(testPath);\n if (entry) {\n const age = Date.now() - entry.cachedAt;\n if (age <= this.CACHE_TTL) {\n logger.debug(\n `Audio already cached: ${key.song} (${format} format)`,\n );\n return testPath;\n }\n }\n }\n }\n }\n\n logger.info(`Downloading and caching audio: ${key.song} (${key.quality})`);\n musicDebug(\"downloadAndCache start\", {\n song: key.song,\n quality: key.quality,\n url: youtubeUrl,\n });\n\n try {\n // Use yt-dlp to download audio directly in the format we want\n // yt-dlp can output to stdout as opus, which we'll pipe to file\n const qualityMap = {\n low: \"worstaudio\",\n medium: \"bestaudio[abr<=128]\",\n high: \"bestaudio[abr<=192]\",\n highest: \"bestaudio\",\n };\n\n const qualityArg = qualityMap[key.quality];\n\n // Verify yt-dlp is available before attempting to use it\n let ytdlpPath: string;\n try {\n ytdlpPath = await getYtdlpPath();\n logger.debug(`Using yt-dlp at: ${ytdlpPath}`);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.error(`yt-dlp not available: ${errorMessage}`);\n throw new Error(\n `yt-dlp is required for audio caching but was not found.\\n${YTDLP_INSTALL_INSTRUCTIONS}`,\n );\n }\n\n // Check for YouTube cookies for authentication\n const cookiesPath = getYouTubeCookiesPath();\n\n // Check for proxy configuration\n const proxyUrl = getProxyUrl();\n\n // Determine audio format (FLAC for lossless, Opus for Discord-optimized)\n // Check environment variable, default to Opus for Discord compatibility\n const audioFormat = (\n process.env.AUDIO_CACHE_FORMAT || \"opus\"\n ).toLowerCase();\n const supportedFormats = [\"opus\", \"flac\", \"wav\", \"mp3\", \"m4a\"];\n const format = supportedFormats.includes(audioFormat)\n ? audioFormat\n : \"opus\";\n\n // Update file path to use correct format extension\n const cacheFilePath = this.getCacheFilePath(key, format);\n\n logger.debug(\n `Using audio format: ${format} (${format === \"flac\" ? \"lossless - prevents additional quality loss\" : \"lossy\"}) for cache`,\n );\n\n const formatSelectors = [qualityArg, YTDLP_CACHE_PERMISSIVE_FORMAT];\n\n for (const [attempt, formatSelector] of formatSelectors.entries()) {\n const command = buildYtdlpCacheDownloadCommand({\n ytdlpPath,\n youtubeUrl,\n cacheFilePath,\n format,\n formatSelector,\n proxyUrl,\n cookiesPath,\n });\n\n if (proxyUrl) {\n logger.debug(`Using proxy: ${proxyUrl}`);\n }\n if (cookiesPath) {\n logger.debug(`Using cookies file for authentication: ${cookiesPath}`);\n }\n\n logger.debug(`Executing: ${command}`);\n try {\n const { stderr } = await execAsync(command, {\n maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large outputs\n timeout: 300000, // 5 minute timeout\n env: augmentEnvWithFfmpegTools(),\n });\n\n if (stderr) {\n const stderrLines = stderr.split(\"\\n\");\n const errorLines = stderrLines.filter(\n (line) =>\n line.trim() &&\n !line.includes(\"WARNING\") &&\n !line.includes(\"Downloading\") &&\n !line.includes(\"Extracting\") &&\n !line.includes(\"[download]\") &&\n !line.includes(\"[info]\") &&\n !line.includes(\"[youtube]\") &&\n !line.includes(\"Destination:\") &&\n !line.match(/^\\s*\\d+\\.\\d+%/), // Progress percentages\n );\n\n if (errorLines.length > 0) {\n logger.warn(`yt-dlp warnings/errors: ${errorLines.join(\"; \")}`);\n musicDebug(\"cache yt-dlp stderr (filtered lines)\", {\n command,\n stderr: errorLines.join(\"\\n\"),\n });\n }\n }\n break;\n } catch (execError: unknown) {\n const ee = execError as {\n message?: string;\n code?: string | number;\n stderr?: Buffer | string;\n };\n const stderrStr =\n typeof ee.stderr === \"string\"\n ? ee.stderr\n : (ee.stderr?.toString?.() ?? \"\");\n const errorMsg = ee.message || String(execError);\n const errorCode = ee.code;\n\n if (\n attempt === 0 &&\n shouldRetryYtdlpWithPermissiveFormat(stderrStr, errorMsg)\n ) {\n logger.info(\n `[audioCache] Preferred yt-dlp format unavailable, retrying cache download with permissive format...`,\n );\n continue;\n }\n\n logger.error(\n `yt-dlp execution failed: ${errorMsg} (code: ${errorCode})`,\n );\n\n musicDebug(\"cache yt-dlp exec failed\", {\n command,\n formatSelector,\n attempt: attempt + 1,\n error: errorMsg,\n code: errorCode,\n stderr: stderrStr,\n });\n\n // Do not use errorMsg.includes('yt-dlp') — the command line contains the binary path.\n if (errorCode === \"ENOENT\") {\n throw new Error(\n `yt-dlp is not installed or not in PATH.\\n${YTDLP_INSTALL_INSTRUCTIONS}`,\n );\n }\n\n const combined = `${stderrStr}\\n${errorMsg}`.toLowerCase();\n if (\n combined.includes(\"sign in\") ||\n combined.includes(\"login\") ||\n combined.includes(\"not a bot\")\n ) {\n throw new Error(\n `YouTube authentication required. Set YOUTUBE_COOKIES or YTDLP_COOKIES environment variable to a cookies file.\\n` +\n `Export cookies from your browser using extensions like \"Get cookies.txt LOCALLY\" or \"cookies.txt\".`,\n );\n }\n\n if (\n combined.includes(\"ffmpeg\") &&\n (combined.includes(\"not found\") ||\n combined.includes(\"no such file\"))\n ) {\n throw new Error(\n \"ffmpeg is required for audio conversion but was not found. \" +\n \"The music player bundles ffmpeg-static from npm; ensure dependencies are installed.\",\n );\n }\n\n throw new Error(\n `Failed to download/cache audio: ${errorMsg}${stderrStr ? `\\n${stderrStr.slice(0, 2000)}` : \"\"}`,\n );\n }\n }\n\n // Verify file was created and is valid\n if (!existsSync(cacheFilePath)) {\n throw new Error(`Downloaded file not found at ${cacheFilePath}`);\n }\n\n const stats = statSync(cacheFilePath);\n\n // Validate file size\n if (stats.size === 0) {\n throw new Error(`Downloaded file is empty: ${cacheFilePath}`);\n }\n\n if (stats.size < 1024) {\n logger.warn(\n `Downloaded file is very small (${stats.size} bytes) - might be incomplete: ${cacheFilePath}`,\n );\n }\n\n // Get detailed file info using ffprobe\n let fileInfo: AudioFileInfo = {\n filePath: cacheFilePath,\n size: stats.size,\n };\n\n try {\n const probeInfo = await this.probeAudioFile(cacheFilePath);\n fileInfo = { ...fileInfo, ...probeInfo };\n\n if (fileInfo.duration) {\n const minutes = Math.floor(fileInfo.duration / 60);\n const seconds = Math.floor(fileInfo.duration % 60);\n logger.info(\n `✅ Successfully cached audio: ${key.song}\\n` +\n ` File: ${cacheFilePath}\\n` +\n ` Format: ${format} (${format === \"flac\" ? \"lossless\" : \"lossy\"})\\n` +\n ` Size: ${(stats.size / 1024 / 1024).toFixed(2)}MB (${stats.size.toLocaleString()} bytes)\\n` +\n ` Duration: ${minutes}:${seconds.toString().padStart(2, \"0\")} (${fileInfo.duration.toFixed(2)}s)\\n` +\n (fileInfo.bitrate\n ? ` Bitrate: ${fileInfo.bitrate}kbps\\n`\n : \"\") +\n (fileInfo.sampleRate\n ? ` Sample Rate: ${fileInfo.sampleRate}Hz\\n`\n : \"\") +\n (fileInfo.channels ? ` Channels: ${fileInfo.channels}\\n` : \"\"),\n );\n } else {\n logger.info(\n `✅ Successfully cached audio: ${key.song}\\n` +\n ` File: ${cacheFilePath}\\n` +\n ` Format: ${format} (${format === \"flac\" ? \"lossless\" : \"lossy\"})\\n` +\n ` Size: ${(stats.size / 1024 / 1024).toFixed(2)}MB (${stats.size.toLocaleString()} bytes)\\n` +\n ` ⚠️ Could not determine duration (metadata missing — file is still on disk; playback/cache return is unaffected).\\n` +\n ` Hint: set ELIZA_MUSIC_DEBUG=1 to log ffprobe details.`,\n );\n musicDebug(\"cache: no duration after probe\", {\n path: cacheFilePath,\n size: stats.size,\n });\n }\n } catch (probeError) {\n logger.warn(\n `Could not probe audio file ${cacheFilePath}: ${probeError}`,\n );\n logger.info(\n `✅ Successfully cached audio: ${key.song}\\n` +\n ` File: ${cacheFilePath}\\n` +\n ` Format: ${format} (${format === \"flac\" ? \"lossless\" : \"lossy\"})\\n` +\n ` Size: ${(stats.size / 1024 / 1024).toFixed(2)}MB (${stats.size.toLocaleString()} bytes)\\n` +\n ` ⚠️ Could not probe file metadata (ffprobe error — cache file still written; playback pipeline continues).`,\n );\n }\n\n // Update cache index\n this.cacheIndex.set(cacheFilePath, {\n filePath: cacheFilePath,\n cachedAt: Date.now(),\n size: stats.size,\n format: format,\n });\n\n return cacheFilePath;\n } catch (error) {\n logger.error(`Error downloading/caching audio: ${error}`);\n\n // Clean up partial file if it exists (check all possible formats)\n const supportedFormats = [\"opus\", \"flac\", \"wav\", \"mp3\", \"m4a\"];\n const audioFormat = (\n process.env.AUDIO_CACHE_FORMAT || \"opus\"\n ).toLowerCase();\n const format = supportedFormats.includes(audioFormat)\n ? audioFormat\n : \"opus\";\n const cacheFilePath = this.getCacheFilePath(key, format);\n\n if (existsSync(cacheFilePath)) {\n try {\n unlinkSync(cacheFilePath);\n } catch (cleanupError) {\n logger.warn(`Error cleaning up partial file: ${cleanupError}`);\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Get or download audio (returns file path)\n */\n async getOrDownload(key: AudioCacheKey, youtubeUrl: string): Promise<string> {\n if (this.isCached(key)) {\n return this.getCacheFilePath(key);\n }\n\n return await this.downloadAndCache(key, youtubeUrl);\n }\n\n /**\n * Resolve the on-disk file path for a cached track by URL (any quality/format).\n * Returns null when the file has not been cached yet or is expired.\n */\n findCachedFilePathByUrl(url: string): string | null {\n const normalizedUrl = this.normalizeUrlForCache(url);\n const normalizedUrlHash = this.hashString(normalizedUrl).substring(0, 8);\n const rawUrlHash = this.hashString(url).substring(0, 8);\n\n try {\n if (!existsSync(this.cacheDir)) return null;\n\n const files = readdirSync(this.cacheDir);\n const exts = [\".opus\", \".flac\", \".wav\", \".mp3\", \".m4a\"];\n\n let matches = files.filter(\n (f) =>\n exts.some((ext) => f.endsWith(ext)) && f.includes(normalizedUrlHash),\n );\n if (matches.length === 0 && rawUrlHash !== normalizedUrlHash) {\n matches = files.filter(\n (f) => exts.some((ext) => f.endsWith(ext)) && f.includes(rawUrlHash),\n );\n }\n if (matches.length === 0) return null;\n\n const pick = matches[0];\n if (pick === undefined) return null;\n const p = join(this.cacheDir, pick);\n if (!existsSync(p)) return null;\n const s = statSync(p);\n if (s.size === 0) return null;\n return p;\n } catch {\n return null;\n }\n }\n\n /**\n * Get cached audio as stream, or download and return stream\n */\n async getAudioStream(\n key: AudioCacheKey,\n youtubeUrl: string,\n ): Promise<Readable> {\n // Try cache first\n const cached = await this.getCachedAudio(key);\n if (cached) {\n logger.debug(`Using cached audio: ${key.song}`);\n\n // Get file info for logging\n const filePath = this.getCacheFilePath(key);\n try {\n const stats = statSync(filePath);\n const probeInfo = await this.probeAudioFile(filePath);\n if (probeInfo.duration) {\n const minutes = Math.floor(probeInfo.duration / 60);\n const seconds = Math.floor(probeInfo.duration % 60);\n logger.info(\n `📦 Using cached audio: ${key.song}\\n` +\n ` File: ${filePath}\\n` +\n ` Size: ${(stats.size / 1024 / 1024).toFixed(2)}MB (${stats.size.toLocaleString()} bytes)\\n` +\n ` Duration: ${minutes}:${seconds.toString().padStart(2, \"0\")} (${probeInfo.duration.toFixed(2)}s)`,\n );\n } else {\n logger.info(\n `📦 Using cached audio: ${key.song} (${(stats.size / 1024 / 1024).toFixed(2)}MB)`,\n );\n }\n } catch (infoError) {\n logger.debug(`Could not get cached file info: ${infoError}`);\n }\n\n // Add logging for cached streams\n cached.on(\"end\", () => {\n logger.debug(`Cached audio stream ended normally for: ${key.song}`);\n });\n cached.on(\"error\", (error) => {\n logger.error(\n `Cached audio stream error for ${key.song}: ${error.message || error}`,\n );\n });\n cached.on(\"close\", () => {\n logger.debug(`Cached audio stream closed for: ${key.song}`);\n });\n\n // Prevent premature close - don't allow the stream to be destroyed\n // Store original destroy but override to prevent premature closes\n cached.destroy = (error?: Error) => {\n logger.warn(\n `Attempted to destroy cached audio stream for ${key.song}${error ? `: ${error.message}` : \"\"}`,\n );\n // Don't actually destroy - let it end naturally\n return cached;\n };\n\n return cached;\n }\n\n // Download and cache, then return stream\n logger.debug(`Downloading audio: ${key.song}`);\n const filePath = await this.downloadAndCache(key, youtubeUrl);\n\n // Verify file exists and has reasonable size before streaming\n if (!existsSync(filePath)) {\n throw new Error(`Downloaded file not found at ${filePath}`);\n }\n\n const stats = statSync(filePath);\n if (stats.size === 0) {\n throw new Error(`Downloaded file is empty: ${filePath}`);\n }\n\n if (stats.size < 1024) {\n logger.warn(\n `Downloaded file is very small (${stats.size} bytes) - might be incomplete: ${filePath}`,\n );\n }\n\n // Get file info for logging\n let fileInfo: AudioFileInfo = {\n filePath,\n size: stats.size,\n };\n\n try {\n const probeInfo = await this.probeAudioFile(filePath);\n fileInfo = { ...fileInfo, ...probeInfo };\n\n if (fileInfo.duration) {\n const minutes = Math.floor(fileInfo.duration / 60);\n const seconds = Math.floor(fileInfo.duration % 60);\n logger.debug(\n `Creating read stream for cached file:\\n` +\n ` File: ${filePath}\\n` +\n ` Size: ${(stats.size / 1024 / 1024).toFixed(2)}MB (${stats.size.toLocaleString()} bytes)\\n` +\n ` Duration: ${minutes}:${seconds.toString().padStart(2, \"0\")} (${fileInfo.duration.toFixed(2)}s)\\n` +\n ` Format: ${fileInfo.format || \"opus\"}`,\n );\n } else {\n logger.debug(\n `Creating read stream for cached file: ${filePath} (${(stats.size / 1024 / 1024).toFixed(2)}MB, ${stats.size.toLocaleString()} bytes)`,\n );\n }\n } catch (_probeError) {\n logger.debug(\n `Creating read stream for cached file: ${filePath} (${(stats.size / 1024 / 1024).toFixed(2)}MB, ${stats.size.toLocaleString()} bytes)`,\n );\n }\n\n const stream = createReadStream(filePath, {\n highWaterMark: 64 * 1024, // 64KB buffer for better streaming\n autoClose: false, // Don't auto-close the file handle\n });\n\n // CRITICAL: Keep stream CLEAN for Discord.js demuxProbe\n // Event listeners put streams in paused mode, preventing format detection\n // Only add 'error' listener for cleanup\n stream.on(\"error\", (error) => {\n logger.error(\n `Downloaded audio stream error for ${key.song}: ${error.message}`,\n );\n });\n\n logger.debug(\n `Downloaded audio stream created for ${key.song}, readable: ${stream.readable}, destroyed: ${stream.destroyed}`,\n );\n\n return stream;\n }\n\n /**\n * Probe audio file using ffprobe to get metadata\n */\n private async probeAudioFile(\n filePath: string,\n ): Promise<Partial<AudioFileInfo>> {\n const ffprobeBin = resolveFfprobeBinaryPath();\n let fileSize = 0;\n try {\n fileSize = statSync(filePath).size;\n } catch {\n /* best-effort for debug / bitrate fallback */\n }\n\n musicDebug(\"probeAudioFile start\", {\n filePath,\n ffprobeBin,\n fileSize,\n });\n\n try {\n const { stdout } = await execFileAsync(\n ffprobeBin,\n [\n \"-v\",\n \"quiet\",\n \"-print_format\",\n \"json\",\n \"-show_format\",\n \"-show_streams\",\n filePath,\n ],\n {\n maxBuffer: 10 * 1024 * 1024,\n timeout: 10000,\n env: augmentEnvWithFfmpegTools(),\n },\n );\n\n const probeData = JSON.parse(stdout) as {\n format?: {\n duration?: string;\n bit_rate?: string;\n format_name?: string;\n };\n streams?: Array<{\n codec_type?: string;\n duration?: string;\n sample_rate?: string;\n channels?: number;\n }>;\n };\n const info: Partial<AudioFileInfo> = {};\n\n const audioStream = probeData.streams?.find(\n (s) => s.codec_type === \"audio\",\n );\n\n if (probeData.format?.duration) {\n info.duration = parseFloat(probeData.format.duration);\n } else if (audioStream?.duration) {\n info.duration = parseFloat(audioStream.duration);\n }\n\n if (probeData.format?.format_name) {\n const formatName = probeData.format.format_name.toLowerCase();\n if (formatName.includes(\"opus\")) {\n info.format = \"opus\";\n } else if (formatName.includes(\"webm\")) {\n info.format = \"webm\";\n } else if (formatName.includes(\"ogg\")) {\n info.format = \"ogg\";\n } else {\n info.format = formatName.split(\",\")[0];\n }\n }\n\n if (probeData.format?.bit_rate) {\n info.bitrate = Math.round(\n parseInt(probeData.format.bit_rate, 10) / 1000,\n );\n }\n\n if (audioStream) {\n if (audioStream.sample_rate) {\n info.sampleRate = parseInt(audioStream.sample_rate, 10);\n }\n if (audioStream.channels) {\n info.channels = audioStream.channels;\n }\n }\n\n const durOk =\n typeof info.duration === \"number\" &&\n Number.isFinite(info.duration) &&\n info.duration > 0;\n\n if (!durOk && probeData.format?.bit_rate && fileSize > 0) {\n const br = parseInt(probeData.format.bit_rate, 10);\n if (br > 0) {\n const estimated = (fileSize * 8) / br;\n if (estimated > 0.5 && estimated < 86400) {\n info.duration = estimated;\n musicDebug(\n \"probe duration estimated from format bit_rate + file size\",\n {\n estimatedSec: estimated,\n bitRate: br,\n },\n );\n }\n }\n }\n\n musicDebug(\"probeAudioFile done\", {\n duration: info.duration,\n format: info.format,\n bitrateKbps: info.bitrate,\n });\n\n return info;\n } catch (error: unknown) {\n const err = error as {\n message?: string;\n code?: string;\n stderr?: Buffer | string;\n stdout?: Buffer | string;\n };\n const errorMsg = err.message || String(error);\n const stderrStr =\n err.stderr === undefined\n ? \"\"\n : typeof err.stderr === \"string\"\n ? err.stderr\n : err.stderr.toString(\"utf8\");\n const stdoutStr =\n err.stdout === undefined\n ? \"\"\n : typeof err.stdout === \"string\"\n ? err.stdout\n : err.stdout.toString(\"utf8\");\n if (errorMsg.includes(\"ffprobe\") || err.code === \"ENOENT\") {\n logger.debug(`ffprobe not available: ${errorMsg}`);\n } else {\n logger.debug(`ffprobe error for ${filePath}: ${errorMsg}`);\n }\n musicDebug(\"probeAudioFile error\", {\n filePath,\n command: formatMusicDebugCommand(ffprobeBin, [\n \"-v\",\n \"quiet\",\n \"-print_format\",\n \"json\",\n \"-show_format\",\n \"-show_streams\",\n filePath,\n ]),\n error: errorMsg,\n code: err.code,\n stderr: stderrStr,\n stdout: stdoutStr,\n });\n return {};\n }\n }\n\n /**\n * Load cache index from filesystem\n */\n private loadCacheIndex(): void {\n try {\n // Scan cache directory and build index\n // Support multiple audio formats: opus, flac, wav, mp3, m4a\n const supportedExtensions = [\".opus\", \".flac\", \".wav\", \".mp3\", \".m4a\"];\n const files = readdirSync(this.cacheDir);\n for (const file of files) {\n const hasSupportedExt = supportedExtensions.some((ext) =>\n file.endsWith(ext),\n );\n if (hasSupportedExt) {\n const filePath = join(this.cacheDir, file);\n try {\n const stats = statSync(filePath);\n const format = file.split(\".\").pop() || \"opus\";\n this.cacheIndex.set(filePath, {\n filePath,\n cachedAt: stats.mtimeMs,\n size: stats.size,\n format: format,\n });\n } catch (error) {\n logger.warn(`Error reading cache file ${file}: ${error}`);\n }\n }\n }\n logger.debug(`Loaded ${this.cacheIndex.size} cached audio files`);\n } catch (error) {\n logger.warn(`Error loading cache index: ${error}`);\n }\n }\n\n /**\n * Clean up expired cache entries\n */\n private cleanExpiredCache(): void {\n let cleaned = 0;\n let totalSize = 0;\n\n for (const [filePath, entry] of this.cacheIndex.entries()) {\n const age = Date.now() - entry.cachedAt;\n if (age > this.CACHE_TTL) {\n try {\n if (existsSync(filePath)) {\n totalSize += entry.size;\n unlinkSync(filePath);\n cleaned++;\n }\n this.cacheIndex.delete(filePath);\n } catch (error) {\n logger.warn(\n `Error deleting expired cache file ${filePath}: ${error}`,\n );\n }\n }\n }\n\n if (cleaned > 0) {\n logger.info(\n `Cleaned ${cleaned} expired cache files (${(totalSize / 1024 / 1024).toFixed(2)}MB)`,\n );\n }\n }\n\n /**\n * Get cache statistics\n */\n getCacheStats(): {\n totalFiles: number;\n totalSize: number;\n oldestEntry: number | null;\n newestEntry: number | null;\n } {\n let totalSize = 0;\n let oldestEntry: number | null = null;\n let newestEntry: number | null = null;\n\n for (const entry of this.cacheIndex.values()) {\n totalSize += entry.size;\n if (!oldestEntry || entry.cachedAt < oldestEntry) {\n oldestEntry = entry.cachedAt;\n }\n if (!newestEntry || entry.cachedAt > newestEntry) {\n newestEntry = entry.cachedAt;\n }\n }\n\n return {\n totalFiles: this.cacheIndex.size,\n totalSize,\n oldestEntry,\n newestEntry,\n };\n }\n\n /**\n * Clear all cache\n */\n clearCache(): void {\n let cleared = 0;\n for (const filePath of this.cacheIndex.keys()) {\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n cleared++;\n }\n } catch (error) {\n logger.warn(`Error deleting cache file ${filePath}: ${error}`);\n }\n }\n this.cacheIndex.clear();\n logger.info(`Cleared ${cleared} cache files`);\n }\n\n /**\n * Get audio metadata for a cached file (returns undefined if not cached)\n */\n async getAudioMetadata(key: AudioCacheKey): Promise<\n | {\n bitrate?: number;\n format?: string;\n sampleRate?: number;\n channels?: number;\n size?: number;\n duration?: number;\n }\n | undefined\n > {\n const filePath = this.getCacheFilePath(key);\n if (!existsSync(filePath)) {\n return undefined;\n }\n\n try {\n const stats = statSync(filePath);\n const probeInfo = await this.probeAudioFile(filePath);\n\n return {\n bitrate: probeInfo.bitrate,\n format: probeInfo.format,\n sampleRate: probeInfo.sampleRate,\n channels: probeInfo.channels,\n size: stats.size,\n duration: probeInfo.duration,\n };\n } catch (error) {\n logger.debug(`Could not get audio metadata: ${error}`);\n return undefined;\n }\n }\n}\n","import type { HandlerCallback } from \"@elizaos/core\";\n\n/**\n * Lightweight progress-update helper compatible with the ProgressiveMessage\n * API from @elizaos/plugin-discord.\n *\n * On Discord the upstream version edits a single message in-place. On the\n * Eliza web chat, the API server's SSE handler uses snapshot/replace\n * semantics for action callbacks — each callback call replaces the previous\n * callback text rather than appending. This gives the same edit-in-place UX:\n * the user sees \"🔍 Searching…\" → \"✨ Setting up…\" → \"Now playing: **X**\"\n * all in the same chat bubble.\n *\n * The contract matches plugin-discord: call update() for transient status,\n * complete()/fail() for the final result. All methods invoke the callback.\n */\nexport class ProgressiveMessage {\n private callback: HandlerCallback;\n private source: string;\n\n constructor(callback: HandlerCallback, source = \"discord\") {\n this.callback = callback;\n this.source = source;\n }\n\n update(text: string, _opts?: { important?: boolean }): void {\n void this.callback({\n text,\n source: this.source,\n merge: \"replace\",\n });\n }\n\n async fail(text: string): Promise<void> {\n await this.callback({\n text,\n source: this.source,\n merge: \"replace\",\n });\n }\n\n async finish(text: string): Promise<void> {\n await this.callback({\n text,\n source: this.source,\n merge: \"replace\",\n });\n }\n\n async complete(text: string): Promise<void> {\n await this.callback({\n text,\n source: this.source,\n merge: \"replace\",\n });\n }\n}\n","export const MUSIC_PLAYER_ACTION_DOCS = [\n \"playback_sources: YouTube, SoundCloud, Spotify links via search, and yt-dlp supported media URLs\",\n \"transport_actions: PAUSE_MUSIC, RESUME_MUSIC, STOP_MUSIC, SKIP_TRACK\",\n \"queue_actions: PLAY_AUDIO starts playback, QUEUE_MUSIC appends, SHOW_QUEUE lists upcoming tracks\",\n \"streaming: /music-player/stream and /music-player/now-playing expose current guild audio state\",\n].join(\"; \");\n","import {\n type Action,\n type ActionExample,\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n logger,\n type Memory,\n type State,\n} from \"@elizaos/core\";\nimport { MusicService } from \"../service\";\nimport {\n type FetchProgress,\n SmartMusicFetchService,\n} from \"../services/smartMusicFetch\";\nimport { classifyPlaybackTransportIntent } from \"../utils/playbackTransportIntent\";\nimport { ProgressiveMessage } from \"../utils/progressiveMessage\";\nimport { resolveMusicGuildIdForPlayback } from \"../utils/resolveMusicGuildId\";\nimport {\n confirmationRequired,\n isConfirmed,\n mergedOptions,\n} from \"./confirmation\";\n\nfunction formatFetchProgressDetails(details: unknown): string | undefined {\n if (details === undefined) return undefined;\n if (typeof details === \"string\") return details;\n try {\n return JSON.stringify(details);\n } catch {\n return String(details);\n }\n}\n\nconst MUSIC_SERVICE_NAME = \"music\";\nconst PLAYBACK_CONTEXTS = [\"media\", \"automation\"] as const;\nconst PLAYBACK_KEYWORDS = [\n \"pause\",\n \"resume\",\n \"unpause\",\n \"skip\",\n \"next\",\n \"stop\",\n \"queue\",\n \"playback\",\n \"music\",\n \"audio\",\n \"pausa\",\n \"reanudar\",\n \"saltar\",\n \"detener\",\n \"cola\",\n \"reprendre\",\n \"arrêter\",\n \"suivant\",\n \"warteschlange\",\n \"pausieren\",\n \"fortsetzen\",\n \"überspringen\",\n \"停止\",\n \"一時停止\",\n \"再開\",\n \"スキップ\",\n \"暂停\",\n \"继续\",\n \"跳过\",\n \"停止\",\n \"일시정지\",\n \"재개\",\n \"건너뛰기\",\n \"중지\",\n] as const;\n\nexport type PlaybackControlOp = \"pause\" | \"resume\" | \"skip\" | \"stop\" | \"queue\";\n\ntype PlaybackOp = PlaybackControlOp;\ntype ActionResultData = NonNullable<ActionResult[\"data\"]>;\n\nfunction failureResult(\n text: string,\n error: string,\n data?: ActionResultData,\n): ActionResult {\n return { success: false, text, error, data };\n}\n\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nfunction hasPlaybackIntent(message: Memory, state: State | undefined): boolean {\n const text = [\n typeof message.content?.text === \"string\" ? message.content.text : \"\",\n typeof state?.values?.recentMessages === \"string\"\n ? state.values.recentMessages\n : \"\",\n ]\n .join(\"\\n\")\n .toLowerCase();\n return PLAYBACK_KEYWORDS.some((keyword) =>\n text.includes(keyword.toLowerCase()),\n );\n}\n\nfunction normalizeOp(value: unknown): PlaybackOp | null {\n if (typeof value !== \"string\") return null;\n const v = value.trim().toLowerCase();\n if (\n v === \"pause\" ||\n v === \"resume\" ||\n v === \"skip\" ||\n v === \"stop\" ||\n v === \"queue\"\n ) {\n return v;\n }\n return null;\n}\n\nfunction inferOpFromText(text: string): PlaybackOp | null {\n const transport = classifyPlaybackTransportIntent(text);\n if (transport) return transport;\n const lower = (text || \"\").toLowerCase();\n if (/\\b(queue|add\\s+to\\s+queue)\\b/.test(lower)) return \"queue\";\n return null;\n}\n\nfunction findActiveGuildId(musicService: MusicService): string | null {\n const queues = musicService.getQueues();\n for (const [guildId] of queues) {\n if (musicService.getCurrentTrack(guildId)) return guildId;\n }\n return null;\n}\n\n/** Supports optional `options` so callers can pass explicit `op` without message text. */\nexport async function validatePlaybackControl(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options?: Record<string, unknown>,\n): Promise<boolean> {\n const merged = mergedOptions(options);\n const explicit = normalizeOp(merged.op);\n const text = message.content?.text ?? \"\";\n const inferredFromText = inferOpFromText(text);\n const inferred = explicit ?? inferredFromText;\n if (\n !inferred &&\n !(\n selectedContextMatches(state, PLAYBACK_CONTEXTS) ||\n hasPlaybackIntent(message, state)\n )\n ) {\n return false;\n }\n if (!inferred) return true;\n const musicService = runtime.getService(MUSIC_SERVICE_NAME) as MusicService;\n if (!musicService) return inferred === \"queue\";\n if (inferred === \"queue\") return true;\n if (inferred === \"pause\") {\n const guildId = findActiveGuildId(musicService);\n if (!guildId) return false;\n return (\n musicService.getIsPlaying(guildId) && !musicService.getIsPaused(guildId)\n );\n }\n if (inferred === \"resume\") {\n const guildId = findActiveGuildId(musicService);\n if (!guildId) return false;\n return musicService.getIsPaused(guildId);\n }\n return findActiveGuildId(musicService) !== null;\n}\n\nexport { inferOpFromText, normalizeOp };\n\nasync function handlePause(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(MUSIC_SERVICE_NAME) as MusicService;\n if (!musicService) {\n await callback({\n text: \"Music service is not available.\",\n source: message.content.source,\n });\n return failureResult(\n \"Music service unavailable\",\n \"Music service unavailable\",\n );\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const guildId = resolveMusicGuildIdForPlayback(message, room, musicService);\n if (!guildId) {\n const text = \"Nothing is playing right now.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"Nothing playing\", \"Nothing playing\");\n }\n\n const track = musicService.getCurrentTrack(guildId);\n await musicService.pause(guildId);\n\n const text = track\n ? `Paused **${track.title}**. Say \"resume\" to continue.`\n : \"Playback paused.\";\n await callback({ text, source: message.content.source });\n return { success: true, text };\n}\n\nasync function handleResume(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(MUSIC_SERVICE_NAME) as MusicService;\n if (!musicService) {\n await callback({\n text: \"Music service is not available.\",\n source: message.content.source,\n });\n return failureResult(\n \"Music service unavailable\",\n \"Music service unavailable\",\n );\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const guildId = resolveMusicGuildIdForPlayback(message, room, musicService);\n if (!guildId) {\n const text = \"Nothing is paused right now.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"Nothing paused\", \"Nothing paused\");\n }\n\n const track = musicService.getCurrentTrack(guildId);\n await musicService.resume(guildId);\n const text = track ? `Resumed **${track.title}**.` : \"Playback resumed.\";\n await callback({ text, source: message.content.source });\n return { success: true, text };\n}\n\nasync function handleSkip(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(MUSIC_SERVICE_NAME) as MusicService;\n if (!musicService) {\n await callback({\n text: \"Music service is not available.\",\n source: message.content.source,\n });\n return failureResult(\n \"Music service unavailable\",\n \"Music service unavailable\",\n );\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const guildId = resolveMusicGuildIdForPlayback(message, room, musicService);\n if (!guildId) {\n const text = \"Nothing is playing right now.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"Nothing playing\", \"Nothing playing\");\n }\n\n const currentTrack = musicService.getCurrentTrack(guildId);\n if (!currentTrack) {\n const text = \"No track is currently playing.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"No current track\", \"No current track\");\n }\n\n const preview = `Confirmation required before skipping **${currentTrack.title}**.`;\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"skip\",\n guildId,\n currentTrack: currentTrack.title,\n });\n }\n\n const skipped = await musicService.skip(guildId, message.entityId);\n if (!skipped) {\n const text = \"Failed to skip track.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"Skip failed\", \"Skip failed\");\n }\n\n const nextTrack = musicService.getCurrentTrack(guildId);\n const text = nextTrack\n ? `Skipped **${currentTrack.title}**. Now playing: **${nextTrack.title}**`\n : `Skipped **${currentTrack.title}**. Queue is now empty.`;\n await callback({ text, source: message.content.source });\n return { success: true, text };\n}\n\nasync function handleStop(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const musicService = runtime.getService(MUSIC_SERVICE_NAME) as MusicService;\n if (!musicService) {\n await callback({\n text: \"Music service is not available.\",\n source: message.content.source,\n });\n return failureResult(\n \"Music service unavailable\",\n \"Music service unavailable\",\n );\n }\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const guildId = resolveMusicGuildIdForPlayback(message, room, musicService);\n if (!guildId) {\n const text = \"Nothing is playing right now.\";\n await callback({ text, source: message.content.source });\n return failureResult(\"Nothing playing\", \"Nothing playing\");\n }\n\n const track = musicService.getCurrentTrack(guildId);\n const queueLength = musicService.getQueueList(guildId).length;\n const preview = track\n ? `Confirmation required before stopping **${track.title}** and clearing ${queueLength} queued track${queueLength !== 1 ? \"s\" : \"\"}.`\n : \"Confirmation required before stopping playback and clearing the queue.\";\n if (!isConfirmed(options)) {\n await callback({ text: preview, source: message.content.source });\n return confirmationRequired(preview, {\n op: \"stop\",\n guildId,\n currentTrack: track?.title ?? null,\n queueLength,\n });\n }\n\n await musicService.stopPlayback(guildId);\n musicService.clear(guildId);\n\n const text = track\n ? `Stopped playing **${track.title}** and cleared the queue.`\n : \"Playback stopped.\";\n await callback({ text, source: message.content.source });\n return { success: true, text };\n}\n\nasync function handleQueue(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown>,\n callback: HandlerCallback,\n): Promise<ActionResult> {\n const messageText = message.content.text || \"\";\n const directQuery =\n typeof options.query === \"string\" && options.query.trim().length > 0\n ? options.query.trim()\n : undefined;\n const query = (directQuery || messageText).trim();\n if (!query || query.length < 3) {\n const text =\n \"Please tell me what song you'd like to queue (at least 3 characters).\";\n await callback({ text, source: message.content.source || \"discord\" });\n return failureResult(text, \"Missing queue query\");\n }\n\n const preview = `Confirmation required before adding \"${query}\" to the music queue.`;\n if (!isConfirmed(options)) {\n await callback({\n text: preview,\n source: message.content.source || \"discord\",\n });\n return confirmationRequired(preview, { op: \"queue\", query });\n }\n\n const progress = new ProgressiveMessage(\n callback,\n message.content.source || \"discord\",\n );\n progress.update(\"🔍 Looking up track...\");\n\n const smartFetch = new SmartMusicFetchService(runtime);\n const preferredQuality =\n (runtime.getSetting(\"MUSIC_QUALITY_PREFERENCE\") as string) || \"mp3_320\";\n\n let lastProgress = \"\";\n const onProgress = async (progressInfo: FetchProgress) => {\n const detailText = formatFetchProgressDetails(progressInfo.details);\n const statusText = `${progressInfo.message}${detailText ? `: ${detailText}` : \"\"}`;\n if (statusText !== lastProgress) {\n lastProgress = statusText;\n logger.info(`[PLAYBACK_OP queue] ${statusText}`);\n progress.update(\n `🔍 ${progressInfo.message}${detailText ? `: ${detailText}` : \"\"}`,\n { important: true },\n );\n }\n };\n\n const result = await smartFetch.fetchMusic({\n query,\n requestedBy: message.entityId,\n onProgress,\n preferredQuality: preferredQuality as \"flac\" | \"mp3_320\" | \"any\",\n });\n\n if (!result.success || !result.url) {\n await progress.fail(\n `❌ Couldn't find or download \"${query}\". ${result.error || \"Please try a different search term.\"}`,\n );\n return failureResult(\n `Couldn't find or download \"${query}\". ${result.error || \"Please try a different search term.\"}`,\n result.error || \"Music not found\",\n { op: \"queue\", query },\n );\n }\n\n progress.update(\"✨ Adding to queue...\");\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n let currentServerId = room?.serverId;\n if (!currentServerId) {\n currentServerId =\n message.content.source === \"discord\"\n ? room?.serverId || message.roomId\n : `web-${message.roomId}`;\n } else if (message.content.source !== \"discord\") {\n currentServerId = `web-${currentServerId}`;\n }\n\n const requestUserId = message.entityId;\n let musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) musicService = new MusicService(runtime);\n\n const queueLength = musicService.getQueueList(currentServerId).length;\n const position = queueLength + 1;\n\n let sourceEmoji = \"\";\n if (result.source === \"library\") sourceEmoji = \"📚\";\n else if (result.source === \"ytdlp\") sourceEmoji = \"🎬\";\n else if (result.source === \"torrent\") sourceEmoji = \"🌊\";\n\n let responseText = `${sourceEmoji} Added to queue (position ${position}): **${query}**`;\n if (result.source === \"torrent\") {\n responseText += \"\\n_Downloaded via torrent and added to your library_\";\n }\n\n const track = await musicService.addTrack(currentServerId, {\n url: result.url,\n title: query,\n requestedBy: requestUserId,\n });\n\n runtime\n .createMemory(\n {\n entityId: runtime.agentId,\n agentId: message.agentId,\n roomId: message.roomId,\n content: {\n source: \"action\",\n thought: `Queued music: ${query} (source: ${result.source})`,\n actions: [\"PLAYBACK\"],\n },\n metadata: {\n type: \"custom\" as const,\n kind: \"PLAYBACK\",\n op: \"queue\",\n audioUrl: result.url,\n title: query,\n trackId: track.id,\n source: result.source,\n },\n },\n \"messages\",\n )\n .catch((error) => logger.warn(`Failed to create memory: ${error}`));\n\n await progress.complete(responseText);\n return {\n success: true,\n text: responseText,\n data: {\n op: \"queue\",\n query,\n position,\n trackId: track.id,\n source: result.source,\n audioUrl: result.url,\n },\n };\n}\n\nexport const playbackOp: Action = {\n name: \"PLAYBACK\",\n contexts: [\"media\", \"automation\"],\n contextGate: { anyOf: [\"media\", \"automation\"] },\n roleGate: { minRole: \"USER\" },\n similes: [\n \"PAUSE_MUSIC\",\n \"RESUME_MUSIC\",\n \"STOP_MUSIC\",\n \"SKIP_TRACK\",\n \"QUEUE_MUSIC\",\n \"PAUSE\",\n \"RESUME\",\n \"UNPAUSE\",\n \"SKIP\",\n \"NEXT_TRACK\",\n \"ADD_TO_QUEUE\",\n ],\n description:\n \"Music playback control. Use op=pause, resume, skip, stop, or queue. \" +\n \"Use this for transport control instead of PLAY_AUDIO. \" +\n \"skip, stop, and queue require confirmed:true.\",\n descriptionCompressed:\n \"Music playback ops: pause, resume, skip, stop, queue.\",\n parameters: [\n {\n name: \"subaction\",\n description: \"Playback operation: pause, resume, skip, stop, or queue.\",\n required: true,\n schema: {\n type: \"string\",\n enum: [\"pause\", \"resume\", \"skip\", \"stop\", \"queue\"],\n },\n },\n {\n name: \"query\",\n description: \"Track query for op=queue.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"confirmed\",\n description: \"Must be true for skip, stop, or queue.\",\n required: false,\n schema: { type: \"boolean\", default: false },\n },\n ],\n validate: async (\n runtime,\n message,\n state,\n options?: Record<string, unknown>,\n ) => validatePlaybackControl(runtime, message, state, options),\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n ): Promise<ActionResult | undefined> => {\n if (!callback) return failureResult(\"Missing callback\", \"Missing callback\");\n const merged = mergedOptions(options);\n const op =\n normalizeOp(merged.op) ?? inferOpFromText(message.content?.text ?? \"\");\n if (!op) {\n const text =\n \"Could not determine playback op. Use op=pause, resume, skip, stop, or queue.\";\n await callback({ text, source: message.content.source });\n return failureResult(text, text);\n }\n\n if (op === \"pause\") return handlePause(runtime, message, state, callback);\n if (op === \"resume\") return handleResume(runtime, message, state, callback);\n if (op === \"skip\")\n return handleSkip(runtime, message, state, merged, callback);\n if (op === \"stop\")\n return handleStop(runtime, message, state, merged, callback);\n return handleQueue(runtime, message, state, merged, callback);\n },\n examples: [\n [\n { name: \"{{name1}}\", content: { text: \"pause the music\" } },\n {\n name: \"{{name2}}\",\n content: {\n text: 'Paused the music. Say \"resume\" to continue.',\n actions: [\"PLAYBACK\"],\n },\n },\n ],\n [\n { name: \"{{name1}}\", content: { text: \"resume\" } },\n {\n name: \"{{name2}}\",\n content: { text: \"Resumed playback.\", actions: [\"PLAYBACK\"] },\n },\n ],\n [\n { name: \"{{name1}}\", content: { text: \"skip\" } },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Confirmation required before skipping.\",\n actions: [\"PLAYBACK\"],\n },\n },\n ],\n [\n { name: \"{{name1}}\", content: { text: \"stop the music\" } },\n {\n name: \"{{name2}}\",\n content: {\n text: \"Confirmation required before stopping playback.\",\n actions: [\"PLAYBACK\"],\n },\n },\n ],\n [\n { name: \"{{name1}}\", content: { text: \"queue Hotel California\" } },\n {\n name: \"{{name2}}\",\n content: {\n text: 'Confirmation required before adding \"Hotel California\" to the queue.',\n actions: [\"PLAYBACK\"],\n },\n },\n ],\n ] as ActionExample[][],\n};\n\nexport default playbackOp;\n","import { type IAgentRuntime, logger, Service, type UUID } from \"@elizaos/core\";\n\n// Local stubs for the optional torrent peer plugins. They aren't always\n// installed in the host workspace; we resolve them at runtime via\n// runtime.getService(...) and only need a minimal contract here for tsc.\ninterface TorrentSearchResult {\n title: string;\n size?: string | number;\n seeders?: number;\n leechers?: number;\n magnet?: string;\n url?: string;\n category?: string;\n // Allow downstream code to read additional provider-specific fields without\n // tripping the type checker.\n [key: string]: unknown;\n}\n\ninterface TorrentSearchService {\n search(\n query: string,\n options?: Record<string, unknown>,\n ): Promise<TorrentSearchResult[]>;\n}\n\nfunction isTorrentSearchService(\n service: unknown,\n): service is TorrentSearchService {\n return (\n typeof service === \"object\" &&\n service !== null &&\n typeof (service as { search?: unknown }).search === \"function\"\n );\n}\n\ninterface TorrentInfo {\n id: string;\n done?: boolean;\n files?: string[];\n [key: string]: unknown;\n}\n\ninterface TorrentService {\n download?(\n magnet: string,\n options?: Record<string, unknown>,\n ): Promise<{\n files?: string[];\n [key: string]: unknown;\n }>;\n addTorrent(options: {\n magnetURI?: string;\n addedBy?: string;\n [key: string]: unknown;\n }): Promise<TorrentInfo>;\n getTorrent(id: string): TorrentInfo | null;\n}\n\nfunction isTorrentService(service: unknown): service is TorrentService {\n return (\n typeof service === \"object\" &&\n service !== null &&\n typeof (service as { addTorrent?: unknown }).addTorrent === \"function\" &&\n typeof (service as { getTorrent?: unknown }).getTorrent === \"function\"\n );\n}\n\ninterface MusicLibraryTrack {\n filePath?: string;\n title?: string;\n url?: string;\n}\n\ninterface SmartFetchMusicLibraryService {\n searchTracks?(\n query: string,\n options?: { limit?: number },\n ): Promise<MusicLibraryTrack[]>;\n searchYouTube?(\n query: string,\n options?: { limit?: number; includeShorts?: boolean },\n ): Promise<Array<{ title: string; url: string }>>;\n}\n\nconst SMART_FETCH_SERVICE_NAME = \"smart-music-fetch\";\n\nexport interface FetchProgress {\n stage:\n | \"checking_library\"\n | \"trying_ytdlp\"\n | \"searching_torrents\"\n | \"downloading_torrents\"\n | \"indexing\"\n | \"ready\"\n | \"failed\";\n message: string;\n details?: unknown;\n}\n\nexport interface FetchResult {\n success: boolean;\n source: \"library\" | \"ytdlp\" | \"torrent\";\n url?: string;\n files?: string[];\n error?: string;\n}\n\nexport interface SmartFetchOptions {\n query: string;\n requestedBy?: UUID;\n onProgress?: (progress: FetchProgress) => void;\n preferredQuality?: \"flac\" | \"mp3_320\" | \"any\"; // Preference, not requirement - will accept lesser quality\n parallelDownloads?: number;\n}\n\n/**\n * Smart music fetch service that tries multiple sources automatically\n * 1. Check music library\n * 2. Try yt-dlp (YouTube, SoundCloud, etc.)\n * 3. Search and download torrents (2-3 in parallel)\n * 4. Notify when ready\n */\nexport class SmartMusicFetchService extends Service {\n static serviceType: string = SMART_FETCH_SERVICE_NAME;\n capabilityDescription =\n \"Intelligently fetches music from multiple sources with automatic fallback\";\n\n static async start(runtime: IAgentRuntime): Promise<SmartMusicFetchService> {\n logger.debug(\n `Starting SmartMusicFetchService for agent ${runtime.character.name}`,\n );\n return new SmartMusicFetchService(runtime);\n }\n\n async stop(): Promise<void> {\n // Nothing to clean up\n }\n\n /**\n * Smart fetch music from any available source\n */\n async fetchMusic(options: SmartFetchOptions): Promise<FetchResult> {\n const {\n query,\n requestedBy,\n onProgress,\n preferredQuality = \"mp3_320\",\n parallelDownloads = 2,\n } = options;\n\n try {\n // Stage 1: Check music library\n onProgress?.({\n stage: \"checking_library\",\n message: \"Checking music library...\",\n });\n\n const libraryResult = await this.checkMusicLibrary(query);\n if (libraryResult.found) {\n onProgress?.({\n stage: \"ready\",\n message: \"Found in library!\",\n details: libraryResult,\n });\n return {\n success: true,\n source: \"library\",\n url: libraryResult.url,\n };\n }\n\n // Stage 2: Try yt-dlp (YouTube, SoundCloud, etc.)\n onProgress?.({\n stage: \"trying_ytdlp\",\n message: \"Searching YouTube and other platforms...\",\n });\n\n const ytdlpResult = await this.tryYtdlp(query);\n if (ytdlpResult.success) {\n onProgress?.({\n stage: \"ready\",\n message: \"Found on YouTube!\",\n details: ytdlpResult,\n });\n return {\n success: true,\n source: \"ytdlp\",\n url: ytdlpResult.url,\n };\n }\n\n // Stage 3: Search torrents\n onProgress?.({\n stage: \"searching_torrents\",\n message: \"Searching torrent indexers...\",\n });\n\n const torrentResults = await this.searchMusicTorrents(\n query,\n preferredQuality,\n );\n if (torrentResults.length === 0) {\n onProgress?.({ stage: \"failed\", message: \"No sources found\" });\n return {\n success: false,\n source: \"torrent\",\n error: \"No music found from any source\",\n };\n }\n\n // Stage 4: Download best torrents in parallel\n onProgress?.({\n stage: \"downloading_torrents\",\n message: `Downloading ${Math.min(parallelDownloads, torrentResults.length)} torrents in parallel...`,\n details: { count: torrentResults.length },\n });\n\n const downloadResult = await this.downloadTorrentsParallel(\n torrentResults.slice(0, parallelDownloads),\n requestedBy,\n );\n\n if (downloadResult.success) {\n onProgress?.({ stage: \"indexing\", message: \"Indexing music files...\" });\n\n // Wait a bit for the DOWNLOAD_COMPLETE event to be processed by music library\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n onProgress?.({\n stage: \"ready\",\n message: \"Music ready to play!\",\n details: downloadResult,\n });\n return {\n success: true,\n source: \"torrent\",\n files: downloadResult.files,\n };\n }\n\n onProgress?.({\n stage: \"failed\",\n message: \"All download attempts failed\",\n });\n return {\n success: false,\n source: \"torrent\",\n error: downloadResult.error || \"All sources failed\",\n };\n } catch (error) {\n logger.error(`Smart fetch error: ${error}`);\n onProgress?.({\n stage: \"failed\",\n message: `Error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n });\n return {\n success: false,\n source: \"library\",\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Check if music exists in library\n */\n private async checkMusicLibrary(\n query: string,\n ): Promise<{ found: boolean; url?: string; tracks?: MusicLibraryTrack[] }> {\n try {\n const musicLibrary = this.runtime?.getService(\n \"musicLibrary\",\n ) as SmartFetchMusicLibraryService | null;\n if (!musicLibrary?.searchTracks) {\n return { found: false };\n }\n\n const results = await musicLibrary.searchTracks(query, { limit: 1 });\n if (results && results.length > 0) {\n return {\n found: true,\n url: results[0].url || results[0].filePath,\n tracks: results,\n };\n }\n\n return { found: false };\n } catch (error) {\n logger.debug(`Music library check failed: ${error}`);\n return { found: false };\n }\n }\n\n /**\n * Try to find and get URL from YouTube/SoundCloud via search\n */\n private async tryYtdlp(\n query: string,\n ): Promise<{ success: boolean; url?: string; title?: string }> {\n try {\n // Try YouTube search service\n const musicLibrary = this.runtime?.getService(\n \"musicLibrary\",\n ) as SmartFetchMusicLibraryService | null;\n if (!musicLibrary?.searchYouTube) {\n return { success: false };\n }\n\n const results = await musicLibrary.searchYouTube(query, { limit: 1 });\n if (results && results.length > 0) {\n return {\n success: true,\n url: results[0].url,\n title: results[0].title,\n };\n }\n\n return { success: false };\n } catch (error) {\n logger.debug(`YouTube search failed: ${error}`);\n return { success: false };\n }\n }\n\n /**\n * Search for music torrents with quality scoring\n */\n private async searchMusicTorrents(\n query: string,\n preferredQuality: string,\n ): Promise<TorrentSearchResult[]> {\n try {\n const torrentSearch = this.runtime?.getService(\"torrent-search\");\n if (!isTorrentSearchService(torrentSearch)) {\n logger.warn(\"Torrent search service not available\");\n return [];\n }\n\n // Search without quality filter to get all options\n const allResults = await torrentSearch.search(query, { limit: 30 });\n\n // Filter for music\n const musicResults = allResults.filter((r) =>\n this.isMusicTorrent(r.title),\n );\n\n // Score and sort by quality preference + seeders\n const scoredResults = musicResults.map((r) => ({\n ...r,\n score: this.calculateQualityScore(r.title, preferredQuality, r.seeders),\n }));\n\n // Sort by score (higher is better)\n scoredResults.sort((a, b) => b.score - a.score);\n\n return scoredResults;\n } catch (error) {\n logger.error(`Torrent search failed: ${error}`);\n return [];\n }\n }\n\n /**\n * Calculate quality score for a torrent\n * Considers: quality match, seeders, and file format\n */\n private calculateQualityScore(\n title: string,\n preferredQuality: string,\n seeders: number | undefined,\n ): number {\n const lower = title.toLowerCase();\n let score = 0;\n\n // Base score from seeders (more seeders = better availability)\n score += Math.min(seeders ?? 0, 100); // Cap at 100 to not overshadow quality\n\n // Quality scoring based on preference\n if (preferredQuality === \"flac\") {\n // Prefer FLAC, but accept high-quality MP3\n if (lower.includes(\"flac\")) score += 200;\n else if (lower.includes(\"320\") || lower.includes(\"320kbps\")) score += 150;\n else if (lower.includes(\"256\") || lower.includes(\"v0\")) score += 100;\n else if (lower.includes(\"192\")) score += 50;\n else if (lower.includes(\"mp3\")) score += 25; // Any MP3 is acceptable\n } else if (preferredQuality === \"mp3_320\") {\n // Prefer 320kbps MP3, but accept FLAC or lower bitrates\n if (lower.includes(\"320\") || lower.includes(\"320kbps\")) score += 200;\n else if (lower.includes(\"flac\"))\n score += 180; // FLAC is great too\n else if (lower.includes(\"256\") || lower.includes(\"v0\")) score += 150;\n else if (lower.includes(\"192\")) score += 100;\n else if (lower.includes(\"128\")) score += 50;\n else if (lower.includes(\"mp3\")) score += 25; // Any MP3 is acceptable\n } else {\n // 'any' - just prefer higher quality generally\n if (lower.includes(\"flac\")) score += 150;\n else if (lower.includes(\"320\")) score += 140;\n else if (lower.includes(\"256\") || lower.includes(\"v0\")) score += 120;\n else if (lower.includes(\"192\")) score += 100;\n else if (lower.includes(\"mp3\")) score += 50;\n }\n\n // Bonus for complete albums vs singles\n if (lower.includes(\"album\") || lower.includes(\"discography\")) {\n score += 20;\n }\n\n // Penalty for suspicious/low-quality indicators\n if (lower.includes(\"sample\") || lower.includes(\"preview\")) {\n score -= 100;\n }\n\n return score;\n }\n\n /**\n * Check if torrent is likely music\n */\n private isMusicTorrent(title: string): boolean {\n const lower = title.toLowerCase();\n const musicExt = [\".mp3\", \".flac\", \".wav\", \".m4a\", \".ogg\"];\n const musicKeywords = [\n \"album\",\n \"discography\",\n \"flac\",\n \"mp3\",\n \"320kbps\",\n \"lossless\",\n ];\n const videoKeywords = [\"bluray\", \"brrip\", \"x264\", \"x265\", \"1080p\", \"720p\"];\n\n const hasMusic =\n musicExt.some((ext) => lower.includes(ext)) ||\n musicKeywords.some((kw) => lower.includes(kw));\n const hasVideo = videoKeywords.some((kw) => lower.includes(kw));\n\n return hasMusic && !hasVideo;\n }\n\n /**\n * Download multiple torrents in parallel, return first to complete\n */\n private async downloadTorrentsParallel(\n torrents: TorrentSearchResult[],\n requestedBy?: UUID,\n ): Promise<{ success: boolean; files?: string[]; error?: string }> {\n try {\n const torrentService = this.runtime?.getService(\"torrent\");\n if (!isTorrentService(torrentService)) {\n return { success: false, error: \"Torrent service not available\" };\n }\n\n logger.info(`Starting ${torrents.length} parallel torrent downloads`);\n\n // Start all downloads\n const downloadPromises = torrents.map(async (torrent, index) => {\n try {\n logger.debug(`Starting download ${index + 1}: ${torrent.title}`);\n const info = await torrentService.addTorrent({\n magnetURI: torrent.magnet,\n addedBy: requestedBy,\n });\n\n // Wait for completion (poll status)\n return await this.waitForTorrentCompletion(\n torrentService,\n info.id,\n 300000,\n ); // 5 min timeout\n } catch (error) {\n logger.warn(`Torrent ${index + 1} failed: ${error}`);\n return null;\n }\n });\n\n // Wait for first successful download\n const results = await Promise.allSettled(downloadPromises);\n\n for (const result of results) {\n if (result.status === \"fulfilled\" && result.value) {\n logger.info(\"First torrent completed successfully\");\n return {\n success: true,\n files: result.value.files,\n };\n }\n }\n\n return { success: false, error: \"All torrent downloads failed\" };\n } catch (error) {\n logger.error(`Parallel download error: ${error}`);\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n }\n\n /**\n * Wait for torrent to complete\n */\n private async waitForTorrentCompletion(\n torrentService: TorrentService,\n infoHash: string,\n timeout: number,\n ): Promise<{ files: string[] } | null> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n const info = torrentService.getTorrent(infoHash);\n if (!info) return null;\n\n if (info.done) {\n // Extract file paths (this is simplified - actual implementation depends on TorrentService API)\n return { files: [] }; // Would return actual file paths\n }\n\n // Wait a bit before checking again\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n return null; // Timeout\n }\n}\n","import type { Memory } from \"@elizaos/core\";\nimport type { MusicService } from \"../service\";\n\nexport type RoomLike = { serverId?: string } | null | undefined;\n\nfunction findActiveGuildId(musicService: MusicService): string | null {\n const queues = musicService.getQueues();\n for (const [guildId] of queues) {\n if (musicService.getCurrentTrack(guildId)) return guildId;\n }\n return null;\n}\n\n/**\n * Resolve the music queue guild id the same way as playAudio / PLAYBACK_OP queue.\n * WHY: Control actions used `room.serverId` alone; on web that is often a raw\n * id while playback uses `web-${roomId}` or `web-${serverId}`, so pause/skip\n * hit a non-existent queue and no-op'd while the agent still replied \"Paused\".\n */\nexport function resolveMusicGuildIdForPlayback(\n message: Memory,\n room: RoomLike,\n musicService: MusicService,\n): string | null {\n const isDiscord = message.content.source === \"discord\";\n\n if (isDiscord) {\n const sid = room?.serverId?.trim();\n if (!sid) {\n return findActiveGuildId(musicService);\n }\n if (musicService.getCurrentTrack(sid)) return sid;\n return findActiveGuildId(musicService);\n }\n\n const raw = room?.serverId?.trim();\n const guildId = raw ? `web-${raw}` : `web-${message.roomId}`;\n if (musicService.getCurrentTrack(guildId)) return guildId;\n return findActiveGuildId(musicService);\n}\n","import type {\n Action,\n ActionExample,\n ActionResult,\n HandlerCallback,\n IAgentRuntime,\n JsonValue,\n Memory,\n State,\n} from \"@elizaos/core\";\nimport { isPlaybackTransportControlOnlyMessage } from \"../utils/playbackTransportIntent\";\nimport { mergedOptions } from \"./confirmation\";\nimport { manageRouting } from \"./manageRouting\";\nimport { manageZones } from \"./manageZones\";\nimport {\n inferMusicLibraryOp,\n MUSIC_LIBRARY_OP_ALIASES,\n musicLibraryAction,\n} from \"./musicLibrary\";\nimport { playAudio } from \"./playAudio\";\nimport {\n inferOpFromText,\n normalizeOp,\n playbackOp,\n validatePlaybackControl,\n} from \"./playbackOp\";\n\nfunction jsonHandlerOptions(\n record: Record<string, unknown>,\n): Record<string, JsonValue | undefined> {\n return record as Record<string, JsonValue | undefined>;\n}\n\n/** Library-backed ops (same contract as legacy MUSIC_LIBRARY). */\ntype MusicLibraryOp = \"playlist\" | \"play_query\" | \"search_youtube\" | \"download\";\n\ntype UnifiedKind = \"library\" | \"playback\" | \"play_audio\" | \"routing\" | \"zones\";\n\nconst PLAYER_CONTROL_OPS = new Set([\n \"pause\",\n \"resume\",\n \"skip\",\n \"stop\",\n \"queue\",\n]);\n\nconst ROUTING_TOKENS = new Set([\"routing\", \"manage_routing\", \"route_audio\"]);\n\nconst ZONE_TOKENS = new Set([\"zones\", \"manage_zones\"]);\n\nconst PLAY_AUDIO_TOKENS = new Set([\"play_audio\", \"stream\", \"play_music_audio\"]);\n\nfunction normalizeToken(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const normalized = value\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\");\n return normalized.length > 0 ? normalized : null;\n}\n\nfunction normalizeMusicLibraryOpToken(value: string): MusicLibraryOp | null {\n const n = normalizeToken(value);\n if (!n) return null;\n const direct: MusicLibraryOp[] = [\n \"playlist\",\n \"play_query\",\n \"search_youtube\",\n \"download\",\n ];\n if ((direct as readonly string[]).includes(n)) {\n return n as MusicLibraryOp;\n }\n return MUSIC_LIBRARY_OP_ALIASES[n] ?? null;\n}\n\nfunction tokenToUnifiedKind(token: string): UnifiedKind | null {\n const n = normalizeToken(token);\n if (!n) return null;\n if (PLAYER_CONTROL_OPS.has(n)) return \"playback\";\n if (ROUTING_TOKENS.has(n)) return \"routing\";\n if (ZONE_TOKENS.has(n)) return \"zones\";\n if (PLAY_AUDIO_TOKENS.has(n)) return \"play_audio\";\n if (normalizeMusicLibraryOpToken(n)) return \"library\";\n return null;\n}\n\nfunction readExplicitUnifiedKind(\n merged: Record<string, unknown>,\n): UnifiedKind | null {\n const keys = [\"op\", \"action\", \"music_op\", \"command\"] as const;\n for (const k of keys) {\n const raw = merged[k];\n if (typeof raw !== \"string\") continue;\n const kind = tokenToUnifiedKind(raw);\n if (kind) return kind;\n }\n return null;\n}\n\nfunction ensurePlaybackMerged(\n merged: Record<string, unknown>,\n message: Memory,\n): Record<string, unknown> {\n const out = { ...merged };\n const op =\n normalizeOp(out.op) ??\n normalizeOp(out.playback_op) ??\n normalizeOp(out.action);\n const resolved = op ?? inferOpFromText(message.content?.text ?? \"\");\n if (resolved) {\n out.op = resolved;\n }\n return out;\n}\n\nasync function inferUnifiedKind(\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n merged: Record<string, unknown>,\n): Promise<UnifiedKind | null> {\n const explicit = readExplicitUnifiedKind(merged);\n if (explicit) return explicit;\n\n const text = message.content?.text ?? \"\";\n\n if (isPlaybackTransportControlOnlyMessage(text)) {\n return \"playback\";\n }\n\n if (inferOpFromText(text)) {\n return \"playback\";\n }\n\n const playState = (state ?? {}) as State;\n if (await playAudio.validate(runtime, message, playState)) {\n return \"play_audio\";\n }\n\n if (await validatePlaybackControl(runtime, message, state, merged)) {\n return \"playback\";\n }\n\n if (\n runtime.getService(\"musicLibrary\") &&\n (await inferMusicLibraryOp(runtime, message, state, merged))\n ) {\n return \"library\";\n }\n\n if (\n await manageRouting.validate(\n runtime,\n message,\n state,\n jsonHandlerOptions(merged),\n )\n ) {\n return \"routing\";\n }\n\n if (\n await manageZones.validate(\n runtime,\n message,\n state,\n jsonHandlerOptions(merged),\n )\n ) {\n return \"zones\";\n }\n\n return null;\n}\n\nconst MUSIC_CONTEXTS = [\n \"media\",\n \"automation\",\n \"knowledge\",\n \"web\",\n \"files\",\n \"settings\",\n] as const;\n\nfunction selectedContextMatches(\n state: State | undefined,\n contexts: readonly string[],\n): boolean {\n const selected = new Set<string>();\n const collect = (value: unknown) => {\n if (!Array.isArray(value)) return;\n for (const item of value) {\n if (typeof item === \"string\") selected.add(item);\n }\n };\n collect(\n (state?.values as Record<string, unknown> | undefined)?.selectedContexts,\n );\n collect(\n (state?.data as Record<string, unknown> | undefined)?.selectedContexts,\n );\n const contextObject = (state?.data as Record<string, unknown> | undefined)\n ?.contextObject as\n | {\n trajectoryPrefix?: { selectedContexts?: unknown };\n metadata?: { selectedContexts?: unknown };\n }\n | undefined;\n collect(contextObject?.trajectoryPrefix?.selectedContexts);\n collect(contextObject?.metadata?.selectedContexts);\n return contexts.some((context) => selected.has(context));\n}\n\nconst musicExamples: ActionExample[][] = [\n ...(musicLibraryAction.examples ?? []),\n ...(playbackOp.examples ?? []),\n ...(playAudio.examples ?? []),\n ...(manageRouting.examples ?? []),\n ...((manageZones as Partial<Action>).examples ?? []),\n];\n\nexport const musicAction: Action = {\n name: \"MUSIC\",\n contexts: [...MUSIC_CONTEXTS],\n contextGate: { anyOf: [...MUSIC_CONTEXTS] },\n roleGate: { minRole: \"USER\" },\n similes: [\n ...(musicLibraryAction.similes ?? []),\n ...(playbackOp.similes ?? []),\n ...(playAudio.similes ?? []),\n ...(manageRouting.similes ?? []),\n ...(manageZones.similes ?? []),\n ],\n description:\n \"Unified music action. Use flat action for everything: library (playlist, play_query, search_youtube, download), playback transport (pause, resume, skip, stop, queue), play_audio, routing, zones. \" +\n \"Transport skip/stop/queue and library mutations require confirmed:true where the underlying operation requires it.\",\n descriptionCompressed:\n \"Flat op: playlist/play_query/search_youtube/download/pause/resume/skip/stop/queue/play_audio/routing/zones.\",\n parameters: [\n {\n name: \"action\",\n description:\n \"Flat operation: playlist | play_query | search_youtube | download | pause | resume | skip | stop | queue | play_audio | routing | zones (hyphens and legacy aliases accepted).\",\n required: false,\n schema: {\n type: \"string\",\n enum: [\n \"playlist\",\n \"play_query\",\n \"search_youtube\",\n \"download\",\n \"pause\",\n \"resume\",\n \"skip\",\n \"stop\",\n \"queue\",\n \"play_audio\",\n \"routing\",\n \"zones\",\n ],\n },\n },\n {\n name: \"subaction\",\n description:\n \"Playlist subaction when op=playlist (save, load, delete, add, …).\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"query\",\n description: \"Search/play/queue query depending on op.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"url\",\n description: \"Direct media URL when using play_audio.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"playlistName\",\n description: \"Playlist name for playlist ops.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"song\",\n description: \"Song query for playlist add.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"limit\",\n description: \"Search result limit (YouTube / library helpers).\",\n required: false,\n schema: { type: \"number\", minimum: 1, maximum: 10 },\n },\n {\n name: \"confirmed\",\n description:\n \"Must be true when the underlying operation requires confirmation.\",\n required: false,\n schema: { type: \"boolean\", default: false },\n },\n {\n name: \"operation\",\n description:\n \"Structured routing operation when using routing (set_mode, start_route, …).\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"mode\",\n description: \"Routing mode for routing operations.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"sourceId\",\n description: \"Stream/source id for routing.\",\n required: false,\n schema: { type: \"string\" },\n },\n {\n name: \"targetIds\",\n description: \"Routing target ids.\",\n required: false,\n schema: { type: \"array\", items: { type: \"string\" } },\n },\n ],\n validate: async (\n runtime: IAgentRuntime,\n message: Memory,\n state?: State,\n options?: Record<string, unknown>,\n ): Promise<boolean> => {\n const merged = mergedOptions(options);\n const kind = await inferUnifiedKind(runtime, message, state, merged);\n if (kind) return true;\n return selectedContextMatches(state, MUSIC_CONTEXTS);\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State | undefined,\n options: Record<string, unknown> | undefined,\n callback?: HandlerCallback,\n ): Promise<ActionResult | undefined> => {\n const merged = mergedOptions(options);\n const kind = await inferUnifiedKind(runtime, message, state, merged);\n\n if (!kind) {\n const text =\n \"Could not classify a music operation. Set action to one of: playlist, play_query, search_youtube, download, pause, resume, skip, stop, queue, play_audio, routing, or zones.\";\n if (callback) {\n await callback({ text, source: message.content.source });\n }\n return { success: false, text, error: text };\n }\n\n switch (kind) {\n case \"playback\": {\n const dispatchMerged = ensurePlaybackMerged(merged, message);\n return playbackOp.handler(\n runtime,\n message,\n state,\n jsonHandlerOptions(dispatchMerged),\n callback,\n );\n }\n case \"play_audio\": {\n if (!callback) {\n return { success: false, error: \"Missing callback\", text: \"\" };\n }\n return playAudio.handler(\n runtime,\n message,\n state as State,\n jsonHandlerOptions(merged),\n callback,\n );\n }\n case \"library\":\n return musicLibraryAction.handler(\n runtime,\n message,\n state,\n jsonHandlerOptions(merged),\n callback,\n );\n case \"routing\":\n return manageRouting.handler(\n runtime,\n message,\n state,\n jsonHandlerOptions(merged),\n callback,\n );\n case \"zones\":\n return manageZones.handler(\n runtime,\n message,\n state,\n jsonHandlerOptions(merged),\n callback,\n );\n default:\n return { success: false, error: \"Unreachable\", text: \"\" };\n }\n },\n examples: musicExamples,\n};\n\nexport default musicAction;\n","import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\nimport type { AlbumInfo, ArtistInfo, TrackInfo } from \"../types\";\n\ntype MusicInfoItem =\n | { type: \"track\"; info: TrackInfo }\n | { type: \"artist\"; info: ArtistInfo }\n | { type: \"album\"; info: AlbumInfo };\n\nconst MAX_DETECTED_ENTITIES = 3;\nconst MAX_MUSIC_INFO_TEXT = 6000;\n\n/**\n * Provider that injects music information context into the agent's state\n * This is particularly useful for DJ introductions and music-related conversations\n * Uses entity detection to find music references in casual conversation\n */\nexport const musicInfoProvider: Provider = {\n name: \"MUSIC_INFO\",\n description: \"Provides information about tracks, artists, and albums\",\n descriptionCompressed: \"Track, artist, album info.\",\n position: 10, // Position after basic providers but before complex ones\n contexts: [\"media\", \"knowledge\"],\n contextGate: { anyOf: [\"media\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n\n get: async (runtime: IAgentRuntime, message: Memory, _state: State) => {\n try {\n logger.debug(\"[MUSIC_INFO Provider] Starting provider execution\");\n\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n logger.debug(\n \"[MUSIC_INFO Provider] Music library service not available\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n // Extract potential music references from the message\n const messageText = message.content?.text || \"\";\n if (!messageText || messageText.trim().length === 0) {\n logger.debug(\"[MUSIC_INFO Provider] Empty message text\");\n return { text: \"\", data: {}, values: {} };\n }\n\n logger.debug(\n `[MUSIC_INFO Provider] Processing message: \"${messageText.substring(0, 100)}${messageText.length > 100 ? \"...\" : \"\"}\"`,\n );\n\n const musicInfo: MusicInfoItem[] = [];\n\n logger.debug(\"[MUSIC_INFO Provider] Attempting entity detection\");\n const detectedEntities = await musicLibrary.detectEntities(messageText);\n logger.debug(\n `[MUSIC_INFO Provider] Detected ${detectedEntities.length} entities: ${detectedEntities.map((e) => `${e.type}:${e.name}`).join(\", \")}`,\n );\n\n for (const entity of detectedEntities.slice(0, MAX_DETECTED_ENTITIES)) {\n logger.debug(\n `[MUSIC_INFO Provider] Fetching info for ${entity.type}: ${entity.name}`,\n );\n if (entity.type === \"song\") {\n const trackInfo = await musicLibrary.getTrackInfo(entity.name);\n if (trackInfo?.track) {\n musicInfo.push({ type: \"track\", info: trackInfo.track });\n logger.debug(\n `[MUSIC_INFO Provider] Successfully fetched track info for: ${entity.name}`,\n );\n }\n } else if (entity.type === \"artist\") {\n const artistInfo = await musicLibrary.getArtistInfo(entity.name);\n if (artistInfo) {\n musicInfo.push({ type: \"artist\", info: artistInfo });\n logger.debug(\n `[MUSIC_INFO Provider] Successfully fetched artist info for: ${entity.name}`,\n );\n }\n } else if (entity.type === \"album\") {\n const albumInfo = await musicLibrary.getAlbumInfo(entity.name);\n if (albumInfo) {\n musicInfo.push({ type: \"album\", info: albumInfo });\n logger.debug(\n `[MUSIC_INFO Provider] Successfully fetched album info for: ${entity.name}`,\n );\n }\n }\n }\n\n if (musicInfo.length === 0) {\n logger.debug(\n \"[MUSIC_INFO Provider] No music info found, returning empty result\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n logger.debug(\n `[MUSIC_INFO Provider] Found ${musicInfo.length} music info item(s)`,\n );\n\n const text = JSON.stringify(\n {\n music_info: musicInfo.map((item) => ({\n type: item.type,\n ...item.info,\n })),\n },\n null,\n 2,\n ).slice(0, MAX_MUSIC_INFO_TEXT);\n\n logger.debug(\n `[MUSIC_INFO Provider] Returning ${text.length} characters of music info text`,\n );\n\n return {\n text,\n data: {\n musicInfo,\n },\n values: {\n musicInfoText: text,\n },\n };\n } catch {\n return { text: \"\", data: {}, values: {} };\n }\n },\n};\n","import {\n type IAgentRuntime,\n logger,\n type Memory,\n type Provider,\n type ProviderResult,\n type State,\n} from \"@elizaos/core\";\nimport {\n getLibraryStats,\n getMostPlayedSongs,\n getRecentSongs,\n} from \"../components/musicLibrary\";\n\n/**\n * Check if the message is asking about available tracks/library\n */\nfunction isAskingAboutAvailable(messageText: string): boolean {\n const lower = messageText.toLowerCase();\n const patterns = [\n /what.*(do you|can you).*have/i,\n /what.*available/i,\n /what.*in.*library/i,\n /what.*tracks/i,\n /what.*songs/i,\n /list.*(tracks|songs|music)/i,\n /show.*(tracks|songs|music|library)/i,\n /what.*you.*got/i,\n /what.*can.*play/i,\n /available.*tracks/i,\n /available.*songs/i,\n /your.*library/i,\n /music.*library/i,\n ];\n\n return patterns.some((pattern) => pattern.test(lower));\n}\n\nexport const musicLibraryProvider: Provider = {\n name: \"MUSIC_LIBRARY\",\n contexts: [\"media\", \"knowledge\"],\n contextGate: { anyOf: [\"media\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n get: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n ): Promise<ProviderResult> => {\n try {\n const messageText = (message.content?.text || \"\").trim();\n const isAskingForList = isAskingAboutAvailable(messageText);\n\n if (isAskingForList) {\n const stats = await getLibraryStats(runtime);\n const mostPlayed = await getMostPlayedSongs(runtime, 20);\n const recentSongs = await getRecentSongs(runtime, 10);\n\n return {\n text: JSON.stringify(\n {\n music_library: {\n total_tracks: stats.totalSongs,\n total_plays: stats.totalPlays,\n most_played: stats.mostPlayed ?? null,\n top_tracks: mostPlayed.map((song, index) => ({\n rank: index + 1,\n title: song.title,\n artist: song.artist || song.channel || \"Unknown Artist\",\n play_count: song.playCount,\n duration: song.duration,\n })),\n recent_tracks: recentSongs.map((song, index) => ({\n rank: index + 1,\n title: song.title,\n artist: song.artist || song.channel || \"Unknown Artist\",\n play_count: song.playCount,\n last_played: formatTimeAgo(Date.now() - song.lastPlayed),\n })),\n note:\n stats.totalSongs === 0\n ? \"Library is empty. Tracks are added as they are played.\"\n : 'References like \"it\", \"that\", or \"this song\" usually mean the most recent track.',\n },\n },\n null,\n 2,\n ),\n };\n }\n\n const recentSongs = await getRecentSongs(runtime, 5);\n if (recentSongs.length === 0) {\n return { text: \"\" };\n }\n\n return {\n text: JSON.stringify(\n {\n recent_music: recentSongs.map((song, index) => ({\n rank: index + 1,\n title: song.title,\n artist: song.artist || song.channel || \"Unknown Artist\",\n play_count: song.playCount,\n last_played: formatTimeAgo(Date.now() - song.lastPlayed),\n })),\n reference_note:\n 'References like \"it\", \"that\", or \"this song\" usually mean the most recent track.',\n },\n null,\n 2,\n ),\n };\n } catch (error) {\n logger.error(\n \"Error in music library provider:\",\n error instanceof Error ? error.message : String(error),\n );\n return { text: \"\" };\n }\n },\n};\n\n/**\n * Format milliseconds into a human-readable time ago string\n */\nfunction formatTimeAgo(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (days > 0) {\n return `${days} day${days > 1 ? \"s\" : \"\"} ago`;\n }\n if (hours > 0) {\n return `${hours} hour${hours > 1 ? \"s\" : \"\"} ago`;\n }\n if (minutes > 0) {\n return `${minutes} minute${minutes > 1 ? \"s\" : \"\"} ago`;\n }\n return \"just now\";\n}\n\nexport default musicLibraryProvider;\n","import {\n type IAgentRuntime,\n logger,\n type Memory,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 as uuidv4, v5 as uuidv5 } from \"uuid\";\n\n/**\n * Represents a song in the global music library\n */\nexport interface LibrarySong {\n id: string;\n url: string;\n title: string;\n artist?: string;\n channel?: string;\n duration?: number;\n playCount: number;\n lastPlayed: number; // timestamp\n firstAdded: number; // timestamp\n requestedBy: Set<string>; // set of user IDs who requested this song\n}\n\nconst MUSIC_LIBRARY_NAMESPACE = \"7f3e5e3e-8f3e-4e3e-8f3e-7f3e5e3e8f3e\";\nconst MUSIC_LIBRARY_TABLE = \"music_library\";\n\ntype StoredSong = Omit<LibrarySong, \"requestedBy\"> & {\n requestedBy?: string[];\n};\n\nfunction getLibraryScopeRoomId(runtime: IAgentRuntime): UUID {\n return runtime.agentId;\n}\n\nfunction isStoredSong(value: unknown): value is StoredSong {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Partial<StoredSong>;\n return (\n typeof candidate.id === \"string\" &&\n typeof candidate.url === \"string\" &&\n typeof candidate.title === \"string\" &&\n typeof candidate.playCount === \"number\" &&\n typeof candidate.lastPlayed === \"number\" &&\n typeof candidate.firstAdded === \"number\" &&\n (candidate.artist === undefined || typeof candidate.artist === \"string\") &&\n (candidate.channel === undefined ||\n typeof candidate.channel === \"string\") &&\n (candidate.duration === undefined ||\n typeof candidate.duration === \"number\") &&\n (candidate.requestedBy === undefined ||\n (Array.isArray(candidate.requestedBy) &&\n candidate.requestedBy.every((entry) => typeof entry === \"string\")))\n );\n}\n\nfunction getStoredSongFromMemory(memory: Memory): StoredSong | null {\n return isStoredSong(memory.content.song) ? memory.content.song : null;\n}\n\nfunction getSongIdFromMemory(memory: Memory): string | null {\n return typeof memory.content.songId === \"string\"\n ? memory.content.songId\n : null;\n}\n\n/**\n * Generate a deterministic UUID for a song based on its URL\n */\nfunction generateSongId(url: string): string {\n // Normalize the URL to handle different formats\n const normalizedUrl = url.toLowerCase().replace(/^https?:\\/\\/(www\\.)?/, \"\");\n return uuidv5(normalizedUrl, MUSIC_LIBRARY_NAMESPACE);\n}\n\n/**\n * Internal: Convert stored song data to LibrarySong with Set\n */\nfunction hydrateSong(stored: StoredSong): LibrarySong {\n return {\n ...stored,\n requestedBy: new Set(stored.requestedBy || []),\n };\n}\n\n/**\n * Internal: Convert LibrarySong to storable format\n */\nfunction dehydrateSong(song: LibrarySong): StoredSong {\n return {\n ...song,\n requestedBy: Array.from(song.requestedBy),\n };\n}\n\n/**\n * Get a song from the library by URL\n */\nexport async function getSong(\n runtime: IAgentRuntime,\n url: string,\n): Promise<LibrarySong | null> {\n const songId = generateSongId(url);\n\n // Need to get ALL memories to find the specific song\n // count: 1 would only return one random memory, not necessarily the song we want\n const memories = await runtime.getMemories({\n tableName: MUSIC_LIBRARY_TABLE,\n roomId: getLibraryScopeRoomId(runtime), // Use agentId as global scope\n count: 1000, // Get enough to find the song\n });\n\n const songMemory = memories.find(\n (memory) => getSongIdFromMemory(memory) === songId,\n );\n const storedSong = songMemory ? getStoredSongFromMemory(songMemory) : null;\n if (storedSong) {\n return hydrateSong(storedSong);\n }\n\n return null;\n}\n\n/**\n * Add or update a song in the library\n */\nexport async function addSongToLibrary(\n runtime: IAgentRuntime,\n songData: {\n url: string;\n title: string;\n artist?: string;\n channel?: string;\n duration?: number;\n requestedBy?: string;\n },\n): Promise<LibrarySong> {\n const songId = generateSongId(songData.url);\n const now = Date.now();\n\n // Try to get existing song\n let song = await getSong(runtime, songData.url);\n\n if (song) {\n // Update existing song\n song.playCount++;\n song.lastPlayed = now;\n if (songData.requestedBy) {\n song.requestedBy.add(songData.requestedBy);\n }\n // Update metadata if provided\n if (songData.title) song.title = songData.title;\n if (songData.artist) song.artist = songData.artist;\n if (songData.channel) song.channel = songData.channel;\n if (songData.duration) song.duration = songData.duration;\n } else {\n // Create new song\n song = {\n id: songId,\n url: songData.url,\n title: songData.title,\n artist: songData.artist,\n channel: songData.channel,\n duration: songData.duration,\n playCount: 1,\n lastPlayed: now,\n firstAdded: now,\n requestedBy: songData.requestedBy\n ? new Set([songData.requestedBy])\n : new Set(),\n };\n }\n\n // Save to music_library table\n const memory: Memory = {\n id: uuidv4() as UUID,\n entityId: runtime.agentId, // Global scope\n agentId: runtime.agentId,\n roomId: runtime.agentId, // Use agentId as global room\n content: {\n songId,\n song: dehydrateSong(song),\n text: `${song.title} - ${song.artist || song.channel || \"Unknown\"}`,\n source: \"music_library\",\n },\n createdAt: now,\n };\n\n await runtime.createMemory(memory, MUSIC_LIBRARY_TABLE);\n\n logger.info(\n `[MusicLibrary] Added song to library: \"${song.title}\" (${song.playCount} plays, ID: ${songId.slice(0, 8)}...)`,\n );\n\n return song;\n}\n\n/**\n * Get recent songs from the library (sorted by last played)\n */\nexport async function getRecentSongs(\n runtime: IAgentRuntime,\n limit: number = 10,\n): Promise<LibrarySong[]> {\n try {\n const memories = await runtime.getMemories({\n tableName: MUSIC_LIBRARY_TABLE,\n roomId: getLibraryScopeRoomId(runtime),\n count: 100, // Get more than we need to sort\n });\n\n logger.debug(\n `[MusicLibrary] getRecentSongs: Found ${memories.length} raw memories in ${MUSIC_LIBRARY_TABLE}`,\n );\n\n const songs = memories\n .map((memory) => getStoredSongFromMemory(memory))\n .filter((song): song is StoredSong => song !== null)\n .map((song) => hydrateSong(song))\n .sort((a, b) => b.lastPlayed - a.lastPlayed)\n .slice(0, limit);\n\n logger.debug(\n `[MusicLibrary] getRecentSongs: Returning ${songs.length} songs`,\n );\n\n return songs;\n } catch (error) {\n logger.error(\n `[MusicLibrary] Error getting recent songs: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n throw error;\n }\n}\n\n/**\n * Search the library for songs matching a query\n */\nexport async function searchLibrary(\n runtime: IAgentRuntime,\n query: string,\n limit: number = 10,\n): Promise<LibrarySong[]> {\n try {\n const memories = await runtime.getMemories({\n tableName: MUSIC_LIBRARY_TABLE,\n roomId: getLibraryScopeRoomId(runtime),\n count: 500, // Get a good sample to search through\n });\n\n const lowerQuery = query.toLowerCase();\n const matches: LibrarySong[] = [];\n\n for (const memory of memories) {\n const storedSong = getStoredSongFromMemory(memory);\n if (!storedSong) continue;\n\n const song = hydrateSong(storedSong);\n const titleMatch = song.title.toLowerCase().includes(lowerQuery);\n const artistMatch = song.artist?.toLowerCase().includes(lowerQuery);\n const channelMatch = song.channel?.toLowerCase().includes(lowerQuery);\n\n if (titleMatch || artistMatch || channelMatch) {\n matches.push(song);\n }\n }\n\n // Sort by play count (descending) and last played (descending)\n matches.sort((a, b) => {\n if (a.playCount !== b.playCount) {\n return b.playCount - a.playCount;\n }\n return b.lastPlayed - a.lastPlayed;\n });\n\n return matches.slice(0, limit);\n } catch (error) {\n logger.error(\n `Error searching library: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n throw error;\n }\n}\n\n/**\n * Get the most recently played song\n */\nexport async function getLastPlayedSong(\n runtime: IAgentRuntime,\n): Promise<LibrarySong | null> {\n const recent = await getRecentSongs(runtime, 1);\n return recent[0] || null;\n}\n\n/**\n * Get all songs sorted by play count\n */\nexport async function getMostPlayedSongs(\n runtime: IAgentRuntime,\n limit: number = 10,\n): Promise<LibrarySong[]> {\n try {\n const memories = await runtime.getMemories({\n tableName: MUSIC_LIBRARY_TABLE,\n roomId: getLibraryScopeRoomId(runtime),\n count: 500,\n });\n\n const songs = memories\n .map((memory) => getStoredSongFromMemory(memory))\n .filter((song): song is StoredSong => song !== null)\n .map((song) => hydrateSong(song))\n .sort((a, b) => {\n if (a.playCount !== b.playCount) {\n return b.playCount - a.playCount;\n }\n return b.lastPlayed - a.lastPlayed;\n })\n .slice(0, limit);\n\n return songs;\n } catch (error) {\n logger.error(\n `Error getting most played songs: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n throw error;\n }\n}\n\n/**\n * Get library statistics\n */\nexport async function getLibraryStats(runtime: IAgentRuntime): Promise<{\n totalSongs: number;\n totalPlays: number;\n mostPlayed?: LibrarySong;\n}> {\n try {\n const memories = await runtime.getMemories({\n tableName: MUSIC_LIBRARY_TABLE,\n roomId: getLibraryScopeRoomId(runtime),\n count: 1000,\n });\n\n const songs = memories\n .map((memory) => getStoredSongFromMemory(memory))\n .filter((song): song is StoredSong => song !== null)\n .map((song) => hydrateSong(song));\n\n const totalSongs = songs.length;\n const totalPlays = songs.reduce((sum, song) => sum + song.playCount, 0);\n\n let mostPlayed: LibrarySong | undefined;\n if (songs.length > 0) {\n mostPlayed = songs.reduce((max, song) =>\n song.playCount > max.playCount ? song : max,\n );\n }\n\n return {\n totalSongs,\n totalPlays,\n mostPlayed,\n };\n } catch (error) {\n logger.error(\n `Error getting library stats: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n throw error;\n }\n}\n","import {\n type IAgentRuntime,\n logger,\n type Memory,\n type Provider,\n type ProviderResult,\n type State,\n type UUID,\n} from \"@elizaos/core\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\n\nconst MUSIC_LIBRARY_SERVICE_NAME = \"musicLibrary\";\nconst DEFAULT_LIMIT = 20;\n\nexport const musicPlaylistsProvider: Provider = {\n name: \"musicPlaylists\",\n description: \"Saved playlists for the requesting user as JSON context.\",\n contexts: [\"media\", \"knowledge\"],\n contextGate: { anyOf: [\"media\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n get: async (\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n ): Promise<ProviderResult> => {\n try {\n const userId = message.entityId as UUID | undefined;\n if (!userId) return { text: \"\" };\n\n const musicLibrary = runtime.getService(\n MUSIC_LIBRARY_SERVICE_NAME,\n ) as MusicLibraryService | null;\n if (!musicLibrary) return { text: \"\" };\n\n const playlists = await musicLibrary.loadPlaylists(userId);\n if (playlists.length === 0) {\n return {\n text: JSON.stringify(\n {\n music_playlists: {\n count: 0,\n items: [],\n note: \"No saved playlists.\",\n },\n },\n null,\n 2,\n ),\n };\n }\n\n const sorted = [...playlists].sort((a, b) => b.updatedAt - a.updatedAt);\n const items = sorted.slice(0, DEFAULT_LIMIT).map((p) => ({\n id: p.id,\n name: p.name,\n track_count: p.tracks.length,\n updated_at: p.updatedAt,\n }));\n\n return {\n text: JSON.stringify(\n {\n music_playlists: {\n count: sorted.length,\n items,\n truncated: sorted.length > DEFAULT_LIMIT,\n },\n },\n null,\n 2,\n ),\n };\n } catch (error) {\n logger.error(\n \"Error in musicPlaylists provider:\",\n error instanceof Error ? error.message : String(error),\n );\n return { text: \"\" };\n }\n },\n};\n\nexport default musicPlaylistsProvider;\n","import {\n type IAgentRuntime,\n logger,\n type Memory,\n type Provider,\n type ProviderResult,\n type State,\n} from \"@elizaos/core\";\nimport type { MusicService } from \"../service\";\n\nconst MUSIC_SERVICE_NAME = \"music\";\nconst DEFAULT_LIMIT = 25;\n\nconst formatDuration = (seconds?: number): string => {\n if (!seconds) return \"Unknown\";\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n};\n\nexport const musicQueueProvider: Provider = {\n name: \"musicQueue\",\n description: \"Current music queue and now-playing track.\",\n contexts: [\"media\", \"knowledge\"],\n contextGate: { anyOf: [\"media\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n get: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n ): Promise<ProviderResult> => {\n try {\n const musicService = runtime.getService(\n MUSIC_SERVICE_NAME,\n ) as MusicService | null;\n if (!musicService) return { text: \"\" };\n\n const room = state?.data?.room || (await runtime.getRoom(message.roomId));\n const currentServerId = room?.serverId;\n if (!currentServerId) return { text: \"\" };\n\n const currentTrack = musicService.getCurrentTrack(currentServerId);\n const queue = musicService.getQueueList(currentServerId);\n\n if (!currentTrack && queue.length === 0) {\n return {\n text: JSON.stringify(\n {\n music_queue: { now_playing: null, count: 0, items: [] },\n },\n null,\n 2,\n ),\n };\n }\n\n const items = queue.slice(0, DEFAULT_LIMIT).map((track, index) => ({\n position: index + 1,\n title: track.title,\n duration: formatDuration(track.duration),\n }));\n\n return {\n text: JSON.stringify(\n {\n music_queue: {\n now_playing: currentTrack\n ? {\n title: currentTrack.title,\n duration: formatDuration(currentTrack.duration),\n }\n : null,\n count: queue.length,\n items,\n truncated: queue.length > DEFAULT_LIMIT,\n },\n },\n null,\n 2,\n ),\n };\n } catch (error) {\n logger.error(\n \"Error in musicQueue provider:\",\n error instanceof Error ? error.message : String(error),\n );\n return { text: \"\" };\n }\n },\n};\n\nexport default musicQueueProvider;\n","import type { IAgentRuntime, Memory, Provider, State } from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport type { DetectedMusicEntity } from \"../services/musicEntityDetectionService\";\nimport type { MusicLibraryService } from \"../services/musicLibraryService\";\nimport type { ExtractedMusicInfo } from \"../services/wikipediaExtractionService\";\n\n/**\n * Provider that uses LLMs to dynamically extract music information from Wikipedia\n * Takes the full Wikipedia extract and uses LLM to intelligently extract relevant context\n */\nexport const wikipediaProvider: Provider = {\n name: \"WIKIPEDIA_MUSIC\",\n description:\n \"Provides music information extracted from Wikipedia using LLM-based parsing\",\n descriptionCompressed: \"Music info from Wikipedia via LLM parsing.\",\n position: 11, // After basic music info provider\n contexts: [\"media\", \"knowledge\"],\n contextGate: { anyOf: [\"media\", \"knowledge\"] },\n cacheStable: false,\n cacheScope: \"turn\",\n\n get: async (runtime: IAgentRuntime, message: Memory, state: State) => {\n logger.debug(\"[WIKIPEDIA_MUSIC Provider] Starting provider execution\");\n\n const messageText = message.content?.text || \"\";\n if (!messageText || messageText.trim().length === 0) {\n logger.debug(\"[WIKIPEDIA_MUSIC Provider] Empty message text\");\n return { text: \"\", data: {}, values: {} };\n }\n\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Processing message: \"${messageText.substring(0, 100)}${messageText.length > 100 ? \"...\" : \"\"}\"`,\n );\n\n // Use entity detection service to find music entities\n // This is more generic - it will detect music entities even without explicit keywords\n const musicLibrary = runtime.getService(\n \"musicLibrary\",\n ) as MusicLibraryService | null;\n if (!musicLibrary) {\n logger.debug(\n \"[WIKIPEDIA_MUSIC Provider] Music library service not available\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n // Try to detect music entities - this uses LLM so it's smart about context\n let detectedEntities: DetectedMusicEntity[] = [];\n try {\n logger.debug(\"[WIKIPEDIA_MUSIC Provider] Attempting entity detection\");\n detectedEntities = await musicLibrary.detectEntities(messageText);\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Detected ${detectedEntities.length} entities: ${detectedEntities.map((e) => `${e.type}:${e.name}`).join(\", \")}`,\n );\n } catch (error) {\n logger.warn(\n `[WIKIPEDIA_MUSIC Provider] Entity detection failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n // If entity detection fails, return empty\n return { text: \"\", data: {}, values: {} };\n }\n\n if (detectedEntities.length === 0) {\n logger.debug(\n \"[WIKIPEDIA_MUSIC Provider] No entities detected, returning empty result\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n // Filter out URLs - Wikipedia extraction doesn't work with URLs\n const urlPattern = /^https?:\\/\\//i;\n const validEntities = detectedEntities.filter((entity) => {\n const isUrl = urlPattern.test(entity.name);\n if (isUrl) {\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Skipping URL entity: ${entity.name}`,\n );\n }\n return !isUrl;\n });\n\n if (validEntities.length === 0) {\n logger.debug(\n \"[WIKIPEDIA_MUSIC Provider] No valid entities after filtering URLs, returning empty result\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Processing ${validEntities.length} valid entities (filtered ${detectedEntities.length - validEntities.length} URLs)`,\n );\n\n // Determine context from state or message\n const purpose = determineContext(state, message);\n logger.debug(`[WIKIPEDIA_MUSIC Provider] Determined context: ${purpose}`);\n\n const extractedInfo: Array<{\n entity: DetectedMusicEntity;\n info: ExtractedMusicInfo;\n }> = [];\n\n for (const entity of validEntities.slice(0, 2)) {\n // Limit to 2 entities to avoid too many API calls\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Extracting Wikipedia info for ${entity.type}: ${entity.name}`,\n );\n try {\n const context = {\n purpose,\n currentArtist: entity.type === \"artist\" ? entity.name : undefined,\n currentTrack: entity.type === \"song\" ? entity.name : undefined,\n currentAlbum: entity.type === \"album\" ? entity.name : undefined,\n };\n\n const info = await musicLibrary.extractFromWikipedia(\n entity.name,\n entity.type,\n context,\n );\n\n if (info) {\n extractedInfo.push({ entity, info });\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Successfully extracted Wikipedia info for ${entity.name}`,\n );\n } else {\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] No Wikipedia info extracted for ${entity.name}`,\n );\n }\n } catch (error) {\n logger.warn(\n `[WIKIPEDIA_MUSIC Provider] Error extracting Wikipedia info for ${entity.type} \"${entity.name}\": ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (extractedInfo.length === 0) {\n logger.debug(\n \"[WIKIPEDIA_MUSIC Provider] No Wikipedia info extracted, returning empty result\",\n );\n return { text: \"\", data: {}, values: {} };\n }\n\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Extracted info for ${extractedInfo.length} entity/entities`,\n );\n\n const text = JSON.stringify(\n {\n wikipedia_music: extractedInfo.map((item) => ({\n entity_type: item.entity.type,\n entity_name: item.entity.name,\n confidence: item.entity.confidence,\n ...item.info,\n })),\n },\n null,\n 2,\n );\n\n logger.debug(\n `[WIKIPEDIA_MUSIC Provider] Returning ${text.length} characters of Wikipedia context text`,\n );\n\n return {\n text,\n data: {\n wikipediaInfo: extractedInfo,\n },\n values: {\n wikipediaText: text,\n },\n };\n },\n};\n\n/**\n * Determine context purpose from state or message\n */\nfunction determineContext(\n _state: State,\n message: Memory,\n): \"dj_intro\" | \"music_selection\" | \"general_info\" | \"related_artists\" {\n const messageText = (message.content?.text || \"\").toLowerCase();\n\n if (\n messageText.includes(\"introduce\") ||\n messageText.includes(\"intro\") ||\n messageText.includes(\"dj\")\n ) {\n return \"dj_intro\";\n }\n if (\n messageText.includes(\"select\") ||\n messageText.includes(\"suggest\") ||\n messageText.includes(\"recommend\") ||\n messageText.includes(\"play\")\n ) {\n return \"music_selection\";\n }\n if (\n messageText.includes(\"related\") ||\n messageText.includes(\"similar\") ||\n messageText.includes(\"influence\")\n ) {\n return \"related_artists\";\n }\n\n return \"general_info\";\n}\n","import { Transform, type TransformCallback } from \"node:stream\";\nimport type {\n IAgentRuntime,\n Route,\n RouteRequest,\n RouteResponse,\n} from \"@elizaos/core\";\nimport type { MusicService } from \"./service\";\nimport { musicDebug } from \"./utils/musicDebug\";\n\ntype StreamingRouteRequest = RouteRequest & {\n get?: (name: string) => string | undefined;\n on?: (event: \"close\", listener: () => void) => unknown;\n protocol?: string;\n};\n\ntype StreamingRouteResponse = RouteResponse & NodeJS.WritableStream;\n\nfunction firstRouteValue(\n value: string | string[] | undefined,\n): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction routeGuildId(req: RouteRequest): string | undefined {\n return firstRouteValue(req.query?.guildId) || req.params?.guildId;\n}\n\nfunction setRouteHeader(res: RouteResponse, name: string, value: string): void {\n res.setHeader?.(name, value);\n}\n\n/**\n * Encode metadata for Shoutcast/Icecast format\n * Metadata is injected every 8192 bytes (or as specified)\n */\nfunction encodeShoutcastMetadata(title: string): Buffer {\n // Format: StreamTitle='title';\n const metadata = `StreamTitle='${title.replace(/'/g, \"''\")}';`;\n const metadataLength = Math.ceil(metadata.length / 16) * 16; // Must be multiple of 16\n const buffer = Buffer.alloc(metadataLength + 1);\n buffer[0] = metadataLength / 16; // Length in 16-byte blocks\n buffer.write(metadata, 1, \"utf8\");\n return buffer;\n}\n\n/**\n * Create a transform stream that injects Shoutcast metadata\n */\nfunction createShoutcastStream(\n trackTitle: string,\n metadataInterval: number = 8192,\n): Transform {\n let bytesSinceMetadata = 0;\n\n return new Transform({\n transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: TransformCallback,\n ) {\n const chunks: Buffer[] = [];\n let offset = 0;\n\n while (offset < chunk.length) {\n const remaining = chunk.length - offset;\n const needed = metadataInterval - bytesSinceMetadata;\n const toTake = Math.min(remaining, needed);\n\n chunks.push(chunk.slice(offset, offset + toTake));\n bytesSinceMetadata += toTake;\n offset += toTake;\n\n // Inject metadata when we reach the interval\n if (bytesSinceMetadata >= metadataInterval) {\n chunks.push(encodeShoutcastMetadata(trackTitle));\n bytesSinceMetadata = 0;\n }\n }\n\n callback(null, Buffer.concat(chunks));\n },\n });\n}\n\n/**\n * Route handler to stream the currently playing audio\n * GET /music-player/stream?guildId=<guildId>&format=<format>\n * Supports both 'webm' (default) and 'shoutcast' formats\n */\nasync function streamAudioHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const guildId = routeGuildId(req);\n const format = firstRouteValue(req.query?.format) || \"ogg\";\n const supportsShoutcast =\n format.toLowerCase() === \"shoutcast\" || format.toLowerCase() === \"icecast\";\n const streamingReq = req as StreamingRouteRequest;\n const streamingRes = res as StreamingRouteResponse;\n\n if (!guildId) {\n res.status(400).json({ error: \"guildId is required\" });\n return;\n }\n\n runtime.logger.info(\n `[WebStream] /stream request received for guildId=${guildId}`,\n );\n\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n runtime.logger.warn(\n \"[WebStream] Music service unavailable — returning 503\",\n );\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n\n const currentTrack = musicService.getCurrentTrack(guildId);\n if (!currentTrack) {\n runtime.logger.info(\n `[WebStream] No current track for guildId=${guildId} — returning 404`,\n );\n musicDebug(\"stream 404 no current track\", { guildId });\n res.status(404).json({ error: \"No track is currently playing\" });\n return;\n }\n\n musicDebug(\"stream open\", {\n guildId,\n format,\n track: currentTrack.title,\n });\n\n try {\n // NEW ARCHITECTURE: Subscribe to broadcast\n //\n // WHY SUBSCRIPTION MODEL:\n // Old approach tried to pipe the same stream to multiple HTTP responses.\n // This caused \"Premature close\" errors - Node streams don't naturally support\n // multiple readers. When one web client disconnected, it affected others.\n //\n // NEW APPROACH:\n // Each web client gets their own independent stream from the broadcast.\n // - Client connects → subscribe() gives them a fresh PassThrough stream\n // - Client disconnects → unsubscribe() cleans up just their stream\n // - Other clients are completely unaffected\n //\n // WHY UNIQUE CLIENT ID:\n // The broadcast tracks subscribers by ID. Each web connection needs a unique\n // identifier so it can be independently managed (subscribe/unsubscribe).\n //\n // DISCORD IMPACT:\n // None. Discord is subscribed as `discord-${guildId}`. Web clients are\n // `web-${timestamp}-${random}`. They're independent subscribers to the same\n // broadcast, like different radios tuned to the same station.\n const broadcast = musicService.getBroadcast(guildId);\n const clientId = `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n const subscription = broadcast.subscribe(clientId);\n const listenerStream = subscription.stream;\n\n musicDebug(\"stream subscribed to broadcast\", {\n guildId,\n clientId,\n subscribers: broadcast.getSubscriberCount(),\n });\n runtime.logger.debug(\n `[WebStream] Client ${clientId} subscribed to broadcast for guild ${guildId}`,\n );\n\n if (supportsShoutcast) {\n // Shoutcast/Icecast format with metadata injection\n setRouteHeader(res, \"Content-Type\", \"audio/mpeg\");\n setRouteHeader(res, \"icy-name\", runtime.character.name || \"Music Player\");\n setRouteHeader(res, \"icy-genre\", \"Various\");\n setRouteHeader(\n res,\n \"icy-url\",\n `${streamingReq.protocol ?? \"http\"}://${streamingReq.get?.(\"host\") ?? \"localhost\"}`,\n );\n setRouteHeader(res, \"icy-pub\", \"1\");\n setRouteHeader(res, \"icy-br\", \"128\"); // Bitrate (approximate)\n setRouteHeader(res, \"icy-metaint\", \"8192\"); // Metadata interval in bytes\n setRouteHeader(res, \"Cache-Control\", \"no-cache\");\n setRouteHeader(res, \"Connection\", \"keep-alive\");\n setRouteHeader(res, \"X-Content-Type-Options\", \"nosniff\");\n\n // Create stream with metadata injection\n const shoutcastStream = createShoutcastStream(currentTrack.title, 8192);\n\n // Pipe broadcast stream through metadata injector to response\n listenerStream.pipe(shoutcastStream).pipe(streamingRes);\n\n // Clean up on client disconnect\n streamingReq.on?.(\"close\", () => {\n subscription.unsubscribe();\n shoutcastStream.destroy();\n runtime.logger.debug(\n `[WebStream] Client ${clientId} disconnected (Shoutcast)`,\n );\n });\n } else {\n // Ogg Opus: matches StreamCore output after ELIZA_MUSIC_BROADCAST_NORMALIZE\n // (yt-dlp cache/temp files are Ogg Opus; `audio/webm` mislabeling = silent playback).\n setRouteHeader(res, \"Content-Type\", \"audio/ogg; codecs=opus\");\n setRouteHeader(res, \"Cache-Control\", \"no-cache\");\n setRouteHeader(res, \"Connection\", \"keep-alive\");\n setRouteHeader(res, \"X-Content-Type-Options\", \"nosniff\");\n\n // Pipe broadcast stream to response\n listenerStream.pipe(streamingRes);\n\n // Clean up on client disconnect\n streamingReq.on?.(\"close\", () => {\n subscription.unsubscribe();\n runtime.logger.debug(\n `[WebStream] Client ${clientId} disconnected (Ogg Opus)`,\n );\n });\n }\n\n // Handle stream errors\n listenerStream.on(\"error\", (error) => {\n if (!res.headersSent) {\n res.status(500).json({ error: \"Stream error occurred\" });\n }\n runtime.logger.error(`Web listener stream error: ${error}`);\n });\n } catch (error) {\n runtime.logger.error(`Error creating audio stream: ${error}`);\n if (!res.headersSent) {\n res.status(500).json({ error: \"Failed to create audio stream\" });\n }\n }\n}\n\n/**\n * Route handler to get current track information\n * GET /music-player/now-playing?guildId=<guildId>\n */\nasync function nowPlayingHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const guildId = routeGuildId(req);\n\n if (!guildId) {\n res.status(400).json({ error: \"guildId is required\" });\n return;\n }\n\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n\n const currentTrack = musicService.getCurrentTrack(guildId);\n if (!currentTrack) {\n res.status(404).json({ error: \"No track is currently playing\" });\n return;\n }\n\n res.status(200).json({\n track: {\n id: currentTrack.id,\n title: currentTrack.title,\n url: currentTrack.url,\n duration: currentTrack.duration,\n requestedBy: currentTrack.requestedBy,\n addedAt: currentTrack.addedAt,\n },\n streamUrl: `/music-player/stream?guildId=${guildId}`,\n });\n}\n\n/**\n * Route handler to get queue information\n * GET /music-player/queue?guildId=<guildId>\n */\nasync function queueHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const guildId = routeGuildId(req);\n\n if (!guildId) {\n res.status(400).json({ error: \"guildId is required\" });\n return;\n }\n\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n\n const currentTrack = musicService.getCurrentTrack(guildId);\n const queue = musicService.getQueueList(guildId);\n\n res.status(200).json({\n currentTrack: currentTrack\n ? {\n id: currentTrack.id,\n title: currentTrack.title,\n url: currentTrack.url,\n duration: currentTrack.duration,\n requestedBy: currentTrack.requestedBy,\n addedAt: currentTrack.addedAt,\n }\n : null,\n queue: queue.map((track) => ({\n id: track.id,\n title: track.title,\n url: track.url,\n duration: track.duration,\n requestedBy: track.requestedBy,\n addedAt: track.addedAt,\n })),\n queueLength: queue.length,\n });\n}\n\n/**\n * Find the first guild with an actively playing track.\n * Returns { guildId, track } or null if nothing is playing anywhere.\n */\nfunction findActiveGuild(musicService: MusicService): {\n guildId: string;\n track: ReturnType<MusicService[\"getCurrentTrack\"]>;\n} | null {\n const queues = musicService.getQueues();\n for (const [guildId] of queues) {\n const track = musicService.getCurrentTrack(guildId);\n if (track) return { guildId, track };\n }\n return null;\n}\n\n/**\n * Route handler for global playback status (no guildId required).\n * GET /music-player/status\n * Returns the first active guild + its current track, or 404 if nothing is playing.\n */\nasync function statusHandler(\n _req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n\n const active = findActiveGuild(musicService);\n if (!active) {\n res.status(200).json({ error: \"No track is currently playing\" });\n return;\n }\n\n res.status(200).json({\n guildId: active.guildId,\n track: {\n id: active.track?.id,\n title: active.track?.title,\n url: active.track?.url,\n duration: active.track?.duration,\n requestedBy: active.track?.requestedBy,\n addedAt: active.track?.addedAt,\n },\n isPaused: musicService.getIsPaused(active.guildId),\n streamUrl: `/music-player/stream?guildId=${encodeURIComponent(active.guildId)}`,\n });\n}\n\n// ── Authenticated control (DJ booth) — NOT public radio ─────────────────\n\n/**\n * Parse optional JSON body for POST /control/* (no express.json on plugin routes).\n */\nasync function readControlJsonBody(\n req: RouteRequest,\n): Promise<Record<string, unknown>> {\n if (req.body) {\n return req.body;\n }\n const stream = req as RouteRequest &\n AsyncIterable<Buffer | string | Uint8Array>;\n if (typeof stream[Symbol.asyncIterator] !== \"function\") {\n return {};\n }\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\").trim();\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw) as unknown;\n return typeof parsed === \"object\" &&\n parsed !== null &&\n !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Resolve guildId from JSON body, query string, or first active guild.\n */\nfunction resolveControlGuildId(\n req: RouteRequest,\n body: Record<string, unknown>,\n musicService: MusicService,\n): string | null {\n const fromBody =\n typeof body.guildId === \"string\" && body.guildId.trim()\n ? body.guildId.trim()\n : undefined;\n const q = req.query?.guildId;\n const fromQuery =\n typeof q === \"string\" && q.trim()\n ? q.trim()\n : Array.isArray(q) && typeof q[0] === \"string\"\n ? q[0].trim()\n : undefined;\n if (fromBody) return fromBody;\n if (fromQuery) return fromQuery;\n const active = findActiveGuild(musicService);\n return active?.guildId ?? null;\n}\n\nasync function controlPauseHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n const body = await readControlJsonBody(req);\n const guildId = resolveControlGuildId(req, body, musicService);\n if (!guildId) {\n res.status(404).json({ error: \"No active playback to pause\" });\n return;\n }\n await musicService.pause(guildId);\n runtime.logger.info(`[MusicControl] Paused playback for guild ${guildId}`);\n res.status(200).json({ ok: true, guildId, state: \"paused\" });\n}\n\nasync function controlResumeHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n const body = await readControlJsonBody(req);\n const guildId = resolveControlGuildId(req, body, musicService);\n if (!guildId) {\n res.status(404).json({ error: \"No active playback to resume\" });\n return;\n }\n await musicService.resume(guildId);\n runtime.logger.info(`[MusicControl] Resumed playback for guild ${guildId}`);\n res.status(200).json({ ok: true, guildId, state: \"playing\" });\n}\n\nasync function controlStopHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n const body = await readControlJsonBody(req);\n const guildId = resolveControlGuildId(req, body, musicService);\n if (!guildId) {\n res.status(404).json({ error: \"No active playback to stop\" });\n return;\n }\n await musicService.stopPlayback(guildId);\n musicService.clear(guildId);\n runtime.logger.info(`[MusicControl] Stopped playback for guild ${guildId}`);\n res.status(200).json({ ok: true, guildId, state: \"stopped\" });\n}\n\nasync function controlSkipHandler(\n req: RouteRequest,\n res: RouteResponse,\n runtime: IAgentRuntime,\n): Promise<void> {\n const musicService = runtime.getService(\"music\") as MusicService;\n if (!musicService) {\n res.status(503).json({ error: \"Music service is not available\" });\n return;\n }\n const body = await readControlJsonBody(req);\n const guildId = resolveControlGuildId(req, body, musicService);\n if (!guildId) {\n res.status(404).json({ error: \"No active playback to skip\" });\n return;\n }\n const skipped = await musicService.skip(guildId);\n if (!skipped) {\n res.status(404).json({ error: \"No track to skip\" });\n return;\n }\n const nextTrack = musicService.getCurrentTrack(guildId);\n runtime.logger.info(\n `[MusicControl] Skipped track for guild ${guildId}${nextTrack ? ` → ${nextTrack.title}` : \"\"}`,\n );\n res.status(200).json({\n ok: true,\n guildId,\n nextTrack: nextTrack\n ? { id: nextTrack.id, title: nextTrack.title, url: nextTrack.url }\n : null,\n });\n}\n\n/**\n * Routes for the music player plugin.\n *\n * Two tiers:\n * - **Public radio** (`public: true`): GET stream, status, queue — any listener,\n * no auth. `<audio>` cannot send Bearer tokens; keep streams here.\n * - **DJ booth** (`public: false`): POST /control/* — same API token as the\n * dashboard (`Authorization: Bearer`, `X-Eliza-Token`, or `X-Api-Key`).\n * Operators and automation use this; it is not mixed with public GET paths.\n *\n * **Agentic path:** the chat action PLAYBACK_OP (op=pause|resume|skip|stop|queue)\n * still routes control through the agent for natural language.\n */\nexport const musicPlayerRoutes: Route[] = [\n {\n type: \"GET\",\n path: \"/stream\",\n public: true,\n name: \"Stream Audio\",\n handler: streamAudioHandler,\n },\n {\n type: \"GET\",\n path: \"/stream/:guildId\",\n public: true,\n name: \"Stream Audio (with guildId param)\",\n handler: streamAudioHandler,\n },\n {\n type: \"GET\",\n path: \"/now-playing\",\n public: true,\n name: \"Now Playing\",\n handler: nowPlayingHandler,\n },\n {\n type: \"GET\",\n path: \"/now-playing/:guildId\",\n public: true,\n name: \"Now Playing (with guildId param)\",\n handler: nowPlayingHandler,\n },\n {\n type: \"GET\",\n path: \"/queue\",\n public: true,\n name: \"Queue\",\n handler: queueHandler,\n },\n {\n type: \"GET\",\n path: \"/queue/:guildId\",\n public: true,\n name: \"Queue (with guildId param)\",\n handler: queueHandler,\n },\n {\n type: \"GET\",\n path: \"/status\",\n public: true,\n name: \"Playback Status\",\n handler: statusHandler,\n },\n {\n type: \"POST\",\n path: \"/control/pause\",\n public: false,\n name: \"Pause playback (authenticated)\",\n handler: controlPauseHandler,\n },\n {\n type: \"POST\",\n path: \"/control/resume\",\n public: false,\n name: \"Resume playback (authenticated)\",\n handler: controlResumeHandler,\n },\n {\n type: \"POST\",\n path: \"/control/stop\",\n public: false,\n name: \"Stop playback (authenticated)\",\n handler: controlStopHandler,\n },\n {\n type: \"POST\",\n path: \"/control/skip\",\n public: false,\n name: \"Skip track (authenticated)\",\n handler: controlSkipHandler,\n },\n];\n","import type { IAgentRuntime, SearchCategoryRegistration } from \"@elizaos/core\";\n\nexport const YOUTUBE_SEARCH_CATEGORY: SearchCategoryRegistration = {\n category: \"youtube\",\n label: \"YouTube videos\",\n description: \"Search YouTube for music videos, songs, and general videos.\",\n contexts: [\"media\", \"knowledge\"],\n filters: [\n { name: \"query\", label: \"Query\", type: \"string\", required: true },\n {\n name: \"limit\",\n label: \"Limit\",\n description: \"Maximum videos to return.\",\n type: \"number\",\n default: 5,\n },\n {\n name: \"includeShorts\",\n label: \"Include Shorts\",\n description: \"Whether YouTube Shorts should be included.\",\n type: \"boolean\",\n default: false,\n },\n ],\n resultSchemaSummary:\n \"YouTubeSearchResult[] with url, title, duration, channel, and views.\",\n capabilities: [\"videos\", \"music\", \"links\", \"metadata\"],\n source: \"plugin:music-library\",\n serviceType: \"musicLibrary\",\n};\n\nexport const WIKIPEDIA_MUSIC_SEARCH_CATEGORY: SearchCategoryRegistration = {\n category: \"wikipedia_music\",\n label: \"Wikipedia music metadata\",\n description:\n \"Look up artist, album, and track background from Wikipedia music pages.\",\n contexts: [\"knowledge\", \"media\"],\n filters: [\n { name: \"query\", label: \"Query\", type: \"string\", required: true },\n {\n name: \"entityType\",\n label: \"Entity type\",\n description: \"Music entity type to look up.\",\n type: \"enum\",\n options: [\n { label: \"Artist\", value: \"artist\" },\n { label: \"Album\", value: \"album\" },\n { label: \"Song\", value: \"song\" },\n ],\n },\n {\n name: \"artist\",\n label: \"Artist\",\n description: \"Optional artist disambiguation for album or song lookups.\",\n type: \"string\",\n },\n ],\n resultSchemaSummary:\n \"ArtistInfo, AlbumInfo, or TrackInfo with bio/description, genres, links, images, and related artists when available.\",\n capabilities: [\n \"music-metadata\",\n \"artist-background\",\n \"album-info\",\n \"track-info\",\n ],\n source: \"plugin:music-library\",\n serviceType: \"musicLibrary\",\n};\n\nfunction hasSearchCategory(runtime: IAgentRuntime, category: string): boolean {\n try {\n runtime.getSearchCategory(category, { includeDisabled: true });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function registerMusicLibrarySearchCategories(\n runtime: IAgentRuntime,\n): void {\n if (!hasSearchCategory(runtime, YOUTUBE_SEARCH_CATEGORY.category)) {\n runtime.registerSearchCategory(YOUTUBE_SEARCH_CATEGORY);\n }\n if (!hasSearchCategory(runtime, WIKIPEDIA_MUSIC_SEARCH_CATEGORY.category)) {\n runtime.registerSearchCategory(WIKIPEDIA_MUSIC_SEARCH_CATEGORY);\n }\n}\n","import { type IAgentRuntime, logger, Service, type UUID } from \"@elizaos/core\";\nimport {\n type DJAnalytics,\n getAnalytics,\n trackTrackPlayed,\n} from \"../components/analytics\";\nimport {\n type DJTip,\n type DJTipStats,\n getDJTipStats,\n trackDJTip,\n} from \"../components/djTips\";\nimport {\n addSongToLibrary,\n getLastPlayedSong,\n getLibraryStats,\n getMostPlayedSongs,\n getRecentSongs,\n getSong,\n type LibrarySong,\n searchLibrary,\n} from \"../components/musicLibrary\";\nimport {\n deletePlaylist,\n loadPlaylists,\n type Playlist,\n savePlaylist,\n} from \"../components/playlists\";\nimport {\n getRoomPreferences,\n getUserPreferences,\n trackFavorite,\n trackSkip,\n trackTrackRequest,\n type UserMusicPreferences,\n} from \"../components/preferences\";\nimport { repetitionControl } from \"../components/repetitionControl\";\nimport {\n getSongMemory,\n recordSongDedication,\n recordSongPlay,\n recordSongRequest,\n type SongMemory,\n} from \"../components/songMemory\";\nimport {\n type DetectedMusicEntity,\n MusicEntityDetectionHelper,\n} from \"./musicEntityDetectionService\";\nimport { MusicInfoHelper } from \"./musicInfoService\";\nimport type { MusicInfoServiceStatus } from \"./serviceStatus\";\nimport { SpotifyClient } from \"./spotifyClient\";\nimport { WikipediaClient } from \"./wikipediaClient\";\nimport {\n type ExtractedMusicInfo,\n type WikipediaExtractionContext,\n WikipediaExtractionHelper,\n} from \"./wikipediaExtractionService\";\nimport { YouTubeSearchHelper, type YouTubeSearchResult } from \"./youtubeSearch\";\n\nconst MUSIC_LIBRARY_SERVICE_NAME = \"musicLibrary\";\n\nexport interface AggregatedRoomPreferences {\n favoriteTracks: Array<{\n url: string;\n title: string;\n requestedBy: UUID[];\n playCount: number;\n }>;\n dislikedTracks: string[];\n}\n\nexport class MusicLibraryService extends Service {\n static serviceType: string = MUSIC_LIBRARY_SERVICE_NAME;\n capabilityDescription = \"Music recommendations based on what you like\";\n\n public readonly spotifyClient: SpotifyClient;\n public readonly repetitionControl = repetitionControl;\n private readonly youtubeSearch: YouTubeSearchHelper;\n private readonly wikipedia: WikipediaClient;\n private readonly musicInfo: MusicInfoHelper;\n private readonly entityDetection: MusicEntityDetectionHelper;\n private readonly wikipediaExtraction: WikipediaExtractionHelper;\n\n constructor(runtime?: IAgentRuntime) {\n super(runtime);\n this.youtubeSearch = new YouTubeSearchHelper();\n this.wikipedia = new WikipediaClient();\n this.musicInfo = new MusicInfoHelper(runtime, this.wikipedia);\n this.entityDetection = new MusicEntityDetectionHelper(runtime);\n this.wikipediaExtraction = new WikipediaExtractionHelper(\n runtime,\n this.wikipedia,\n );\n const clientId = runtime?.getSetting(\"SPOTIFY_CLIENT_ID\") as\n | string\n | undefined;\n const clientSecret = runtime?.getSetting(\"SPOTIFY_CLIENT_SECRET\") as\n | string\n | undefined;\n this.spotifyClient = new SpotifyClient(clientId, clientSecret);\n }\n\n static async start(runtime: IAgentRuntime): Promise<MusicLibraryService> {\n logger.debug(\n `Starting MusicLibraryService for agent ${runtime.character.name}`,\n );\n return new MusicLibraryService(runtime);\n }\n\n async stop(): Promise<void> {\n this.musicInfo.clearCache();\n this.entityDetection.clearCache();\n this.wikipediaExtraction.clearCache();\n this.youtubeSearch.clearCache();\n }\n\n private ensureRuntime(): IAgentRuntime {\n if (!this.runtime) {\n throw new Error(\"MusicLibraryService runtime is not available\");\n }\n return this.runtime;\n }\n\n // === Library storage ===\n\n async addSong(\n songData: Parameters<typeof addSongToLibrary>[1],\n ): Promise<LibrarySong> {\n logger.info(\n `[MusicLibraryService] addSong called: \"${songData.title}\" (${songData.url})`,\n );\n try {\n const result = await addSongToLibrary(this.ensureRuntime(), songData);\n logger.info(\n `[MusicLibraryService] ✅ Song saved: \"${result.title}\" (${result.playCount} plays)`,\n );\n return result;\n } catch (error) {\n logger.error(`[MusicLibraryService] ❌ Failed to save song: ${error}`);\n throw error;\n }\n }\n\n async getSong(url: string): Promise<LibrarySong | null> {\n return getSong(this.ensureRuntime(), url);\n }\n\n async getRecentSongs(limit?: number): Promise<LibrarySong[]> {\n return getRecentSongs(this.ensureRuntime(), limit);\n }\n\n async getLastPlayedSong(): Promise<LibrarySong | null> {\n return getLastPlayedSong(this.ensureRuntime());\n }\n\n async getMostPlayedSongs(limit?: number): Promise<LibrarySong[]> {\n return getMostPlayedSongs(this.ensureRuntime(), limit);\n }\n\n async searchLibrary(query: string, limit?: number): Promise<LibrarySong[]> {\n return searchLibrary(this.ensureRuntime(), query, limit);\n }\n\n async getLibraryStats(): Promise<{\n totalSongs: number;\n totalPlays: number;\n mostPlayed?: LibrarySong;\n }> {\n return getLibraryStats(this.ensureRuntime());\n }\n\n // === Playlists ===\n\n async savePlaylist(\n entityId: UUID,\n playlist: Parameters<typeof savePlaylist>[2],\n ): Promise<Playlist> {\n return savePlaylist(this.ensureRuntime(), entityId, playlist);\n }\n\n async loadPlaylists(entityId: UUID): Promise<Playlist[]> {\n return loadPlaylists(this.ensureRuntime(), entityId);\n }\n\n async deletePlaylist(entityId: UUID, playlistId: string): Promise<boolean> {\n return deletePlaylist(this.ensureRuntime(), entityId, playlistId);\n }\n\n // === Preferences ===\n\n async getUserPreferences(\n entityId: UUID,\n ): Promise<UserMusicPreferences | null> {\n return getUserPreferences(this.ensureRuntime(), entityId);\n }\n\n async getRoomPreferences(\n roomId: UUID,\n ): Promise<Map<UUID, UserMusicPreferences>> {\n return getRoomPreferences(this.ensureRuntime(), roomId);\n }\n\n async getAggregatedRoomPreferences(\n roomId: UUID,\n ): Promise<AggregatedRoomPreferences> {\n try {\n const preferences = await this.getRoomPreferences(roomId);\n const favoriteTrackMap = new Map<\n string,\n {\n url: string;\n title: string;\n requestedBy: Set<UUID>;\n playCount: number;\n }\n >();\n const dislikedTracks = new Set<string>();\n\n for (const [entityId, prefs] of preferences.entries()) {\n if (prefs.favoriteTracks) {\n for (const track of prefs.favoriteTracks) {\n if (!track.url) continue;\n const existing = favoriteTrackMap.get(track.url);\n if (existing) {\n existing.playCount += track.playCount || 1;\n existing.requestedBy.add(entityId);\n } else {\n favoriteTrackMap.set(track.url, {\n url: track.url,\n title: track.title || \"Unknown Title\",\n playCount: track.playCount || 1,\n requestedBy: new Set<UUID>([entityId]),\n });\n }\n }\n }\n\n if (prefs.dislikedTracks) {\n for (const url of prefs.dislikedTracks) {\n dislikedTracks.add(url);\n }\n }\n }\n\n const favoriteTracks = Array.from(favoriteTrackMap.values())\n .map((entry) => ({\n url: entry.url,\n title: entry.title,\n playCount: entry.playCount,\n requestedBy: Array.from(entry.requestedBy),\n }))\n .sort((a, b) => b.playCount - a.playCount);\n\n return {\n favoriteTracks,\n dislikedTracks: Array.from(dislikedTracks),\n };\n } catch (error) {\n logger.debug(\n `[MusicLibraryService] Failed to aggregate room preferences: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return {\n favoriteTracks: [],\n dislikedTracks: [],\n };\n }\n }\n\n async trackTrackRequest(\n entityId: UUID,\n track: { url: string; title: string },\n roomId?: UUID,\n worldId?: UUID,\n ): Promise<void> {\n await trackTrackRequest(\n this.ensureRuntime(),\n entityId,\n track,\n roomId,\n worldId,\n );\n }\n\n async trackSkip(\n entityId: UUID,\n trackUrl: string,\n roomId?: UUID,\n worldId?: UUID,\n ): Promise<void> {\n await trackSkip(this.ensureRuntime(), entityId, trackUrl, roomId, worldId);\n }\n\n async trackFavorite(\n entityId: UUID,\n track: { url: string; title: string },\n roomId?: UUID,\n worldId?: UUID,\n ): Promise<void> {\n await trackFavorite(this.ensureRuntime(), entityId, track, roomId, worldId);\n }\n\n // === Analytics ===\n\n async trackTrackPlayed(\n roomId: UUID,\n track: { url: string; title: string },\n duration: number,\n requestedBy?: { entityId: UUID; name: string },\n ): Promise<void> {\n await trackTrackPlayed(\n this.ensureRuntime(),\n roomId,\n track,\n duration,\n requestedBy,\n );\n }\n\n async getAnalytics(roomId: UUID): Promise<DJAnalytics | null> {\n return getAnalytics(this.ensureRuntime(), roomId);\n }\n\n // === Song memory ===\n\n async getSongMemory(url: string): Promise<SongMemory | null> {\n return getSongMemory(this.ensureRuntime(), url);\n }\n\n async recordSongPlay(\n song: Parameters<typeof recordSongPlay>[1],\n context: Parameters<typeof recordSongPlay>[2],\n ): Promise<void> {\n await recordSongPlay(this.ensureRuntime(), song, context);\n }\n\n async recordSongRequest(\n song: Parameters<typeof recordSongRequest>[1],\n requester: Parameters<typeof recordSongRequest>[2],\n ): Promise<void> {\n await recordSongRequest(this.ensureRuntime(), song, requester);\n }\n\n async recordSongDedication(\n url: string,\n dedication: Parameters<typeof recordSongDedication>[2],\n ): Promise<void> {\n await recordSongDedication(this.ensureRuntime(), url, dedication);\n }\n\n // === DJ tips ===\n\n async trackDJTip(roomId: UUID, tip: Omit<DJTip, \"roomId\">): Promise<void> {\n await trackDJTip(this.ensureRuntime(), roomId, tip);\n }\n\n async getDJTipStats(): Promise<DJTipStats> {\n return getDJTipStats(this.ensureRuntime());\n }\n\n // === YouTube search ===\n\n async search(\n query: string,\n options: { limit?: number; includeShorts?: boolean } = {},\n ): Promise<YouTubeSearchResult[]> {\n return this.searchYouTube(query, options);\n }\n\n async searchYouTube(\n query: string,\n options: { limit?: number; includeShorts?: boolean } = {},\n ): Promise<YouTubeSearchResult[]> {\n return this.youtubeSearch.search(query, options);\n }\n\n async searchOneYouTube(query: string): Promise<YouTubeSearchResult | null> {\n return this.youtubeSearch.searchOne(query);\n }\n\n async validateYouTubeUrl(url: string): Promise<boolean> {\n return this.youtubeSearch.validateUrl(url);\n }\n\n async getYouTubeVideoInfo(url: string): Promise<YouTubeSearchResult | null> {\n return this.youtubeSearch.getVideoInfo(url);\n }\n\n // === Music metadata ===\n\n getServiceStatus(): MusicInfoServiceStatus {\n return this.musicInfo.getServiceStatus();\n }\n\n async getTrackInfo(urlOrTitle: string) {\n return this.musicInfo.getTrackInfo(urlOrTitle);\n }\n\n async getArtistInfo(artistName: string) {\n return this.musicInfo.getArtistInfo(artistName);\n }\n\n async getAlbumInfo(albumTitle: string, artistName?: string) {\n return this.musicInfo.getAlbumInfo(albumTitle, artistName);\n }\n\n async prewarmTrackInfo(urlOrTitle: string): Promise<void> {\n return this.musicInfo.prewarmTrackInfo(urlOrTitle);\n }\n\n async prewarmTracks(tracks: string[]): Promise<void> {\n return this.musicInfo.prewarmTracks(tracks);\n }\n\n // === Entity and Wikipedia helpers ===\n\n async detectEntities(text: string): Promise<DetectedMusicEntity[]> {\n return this.entityDetection.detectEntities(text);\n }\n\n async getWikipediaTrackInfo(trackName: string, artistName?: string) {\n return this.wikipedia.getTrackInfo(trackName, artistName);\n }\n\n async getWikipediaArtistInfo(artistName: string) {\n return this.wikipedia.getArtistInfo(artistName);\n }\n\n async getWikipediaAlbumInfo(albumTitle: string, artistName?: string) {\n return this.wikipedia.getAlbumInfo(albumTitle, artistName);\n }\n\n async extractFromWikipedia(\n entityName: string,\n entityType: \"artist\" | \"album\" | \"song\",\n context: WikipediaExtractionContext,\n ): Promise<ExtractedMusicInfo | null> {\n return this.wikipediaExtraction.extractFromWikipedia(\n entityName,\n entityType,\n context,\n );\n }\n}\n","import {\n type Component,\n createUniqueUuid,\n type IAgentRuntime,\n logger,\n type Room,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { requireRoomContext } from \"./storageContext\";\n\n/**\n * Analytics data for a guild/room\n */\nexport interface DJAnalytics {\n totalTracksPlayed: number;\n totalPlayTime: number; // milliseconds\n mostPlayedTracks: Array<{\n url: string;\n title: string;\n playCount: number;\n lastPlayed: number;\n }>;\n mostRequestedBy: Array<{\n entityId: UUID;\n name: string;\n requestCount: number;\n }>;\n popularTimes: Array<{ hour: number; playCount: number }>; // 0-23\n popularDays: Array<{ day: number; playCount: number }>; // 0-6 (Sunday = 0)\n milestones: Array<{ type: string; value: number; timestamp: number }>;\n sessionStats: {\n totalSessions: number;\n averageSessionDuration: number; // milliseconds\n longestSession: number; // milliseconds\n };\n}\n\ninterface ListenerSnapshot {\n timestamp: number;\n listenerCount: number;\n humanListenerCount: number;\n botListenerCount: number;\n}\n\nconst ANALYTICS_COMPONENT_TYPE = \"dj_analytics\";\nconst ANALYTICS_ENTITY_PREFIX = \"dj-analytics\";\n\nfunction createEmptyAnalytics(): DJAnalytics {\n return {\n totalTracksPlayed: 0,\n totalPlayTime: 0,\n mostPlayedTracks: [],\n mostRequestedBy: [],\n popularTimes: Array.from({ length: 24 }, (_, i) => ({\n hour: i,\n playCount: 0,\n })),\n popularDays: Array.from({ length: 7 }, (_, i) => ({\n day: i,\n playCount: 0,\n })),\n milestones: [],\n sessionStats: {\n totalSessions: 0,\n averageSessionDuration: 0,\n longestSession: 0,\n },\n };\n}\n\nfunction getAnalyticsEntityId(runtime: IAgentRuntime, roomId: UUID): UUID {\n return createUniqueUuid(runtime, `${ANALYTICS_ENTITY_PREFIX}-${roomId}`);\n}\n\nasync function ensureAnalyticsEntity(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<{\n entityId: UUID;\n room: Room;\n effectiveRoomId: UUID;\n effectiveWorldId: UUID;\n}> {\n const roomContext = await requireRoomContext(runtime, roomId, \"DJ Analytics\");\n const room = roomContext.room;\n const effectiveRoomId = roomContext.roomId;\n const effectiveWorldId = roomContext.worldId;\n\n const entityId = getAnalyticsEntityId(runtime, roomId);\n let entity = await runtime.getEntityById(entityId);\n\n if (!entity) {\n const created = await runtime.createEntity({\n id: entityId,\n names: [\n room?.name\n ? `DJ Analytics (${room.name})`\n : `DJ Analytics (${roomId.slice(0, 8)})`,\n ],\n metadata: {\n dj: {\n type: \"analytics\",\n roomId,\n roomName: room?.name,\n serverId: room?.serverId,\n },\n },\n agentId: runtime.agentId,\n });\n\n if (!created) {\n entity = await runtime.getEntityById(entityId);\n if (!entity) {\n logger.error(\n `[DJ Analytics] Failed to ensure analytics entity exists for room ${roomId}`,\n );\n throw new Error(\n `[DJ Analytics] Failed to ensure analytics entity exists for room ${roomId}`,\n );\n }\n }\n }\n\n return { entityId, room, effectiveRoomId, effectiveWorldId };\n}\n\n/**\n * Get analytics for a guild/room\n */\nexport async function getAnalytics(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<DJAnalytics | null> {\n const entityId = getAnalyticsEntityId(runtime, roomId);\n let component = await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n component = await runtime.getComponent(\n roomId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n }\n\n return getStoredField<DJAnalytics>(component, \"analytics\");\n}\n\n/**\n * Initialize analytics for a room\n */\nasync function initializeAnalytics(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<Component | null> {\n const context = await ensureAnalyticsEntity(runtime, roomId);\n const { entityId, effectiveRoomId, effectiveWorldId } = context;\n const now = Date.now();\n const initialAnalytics = createEmptyAnalytics();\n\n const success = await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: effectiveRoomId,\n worldId: effectiveWorldId,\n sourceEntityId: runtime.agentId,\n type: ANALYTICS_COMPONENT_TYPE,\n createdAt: now,\n data: createStoredField(\"analytics\", initialAnalytics),\n });\n\n if (!success) {\n throw new Error(\n `[DJ Analytics] Failed to create analytics component for room ${roomId}`,\n );\n }\n\n // Return the component we just created\n return await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n}\n\n/**\n * Track a track being played\n */\nexport async function trackTrackPlayed(\n runtime: IAgentRuntime,\n roomId: UUID,\n track: { url: string; title: string },\n duration: number,\n requestedBy?: { entityId: UUID; name: string },\n): Promise<void> {\n const entityId = getAnalyticsEntityId(runtime, roomId);\n let component = await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n component = await runtime.getComponent(\n roomId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n }\n\n if (!component) {\n const newComponent = await initializeAnalytics(runtime, roomId);\n if (!newComponent) {\n throw new Error(\n `[DJ Analytics] Failed to initialize analytics for room ${roomId}`,\n );\n }\n component = newComponent;\n }\n\n const analytics =\n getStoredField<DJAnalytics>(component, \"analytics\") ??\n createEmptyAnalytics();\n\n // Update totals\n analytics.totalTracksPlayed += 1;\n analytics.totalPlayTime += duration;\n\n // Update most played tracks\n const trackIndex = analytics.mostPlayedTracks.findIndex(\n (t) => t.url === track.url,\n );\n const now = Date.now();\n if (trackIndex >= 0) {\n analytics.mostPlayedTracks[trackIndex].playCount += 1;\n analytics.mostPlayedTracks[trackIndex].lastPlayed = now;\n } else {\n analytics.mostPlayedTracks.push({\n url: track.url,\n title: track.title,\n playCount: 1,\n lastPlayed: now,\n });\n }\n analytics.mostPlayedTracks.sort((a, b) => b.playCount - a.playCount);\n analytics.mostPlayedTracks = analytics.mostPlayedTracks.slice(0, 100); // Keep top 100\n\n // Update most requested by\n if (requestedBy) {\n const requesterIndex = analytics.mostRequestedBy.findIndex(\n (r) => r.entityId === requestedBy.entityId,\n );\n if (requesterIndex >= 0) {\n analytics.mostRequestedBy[requesterIndex].requestCount += 1;\n } else {\n analytics.mostRequestedBy.push({\n entityId: requestedBy.entityId,\n name: requestedBy.name,\n requestCount: 1,\n });\n }\n analytics.mostRequestedBy.sort((a, b) => b.requestCount - a.requestCount);\n analytics.mostRequestedBy = analytics.mostRequestedBy.slice(0, 50); // Keep top 50\n }\n\n // Update popular times\n const hour = new Date().getHours();\n analytics.popularTimes[hour].playCount += 1;\n\n // Update popular days\n const day = new Date().getDay();\n analytics.popularDays[day].playCount += 1;\n\n // Check for milestones\n const milestones = [\n { type: \"tracks_100\", value: 100 },\n { type: \"tracks_500\", value: 500 },\n { type: \"tracks_1000\", value: 1000 },\n { type: \"tracks_5000\", value: 5000 },\n { type: \"tracks_10000\", value: 10000 },\n ];\n\n for (const milestone of milestones) {\n if (\n analytics.totalTracksPlayed === milestone.value &&\n !analytics.milestones.some((m) => m.type === milestone.type)\n ) {\n analytics.milestones.push({\n type: milestone.type,\n value: milestone.value,\n timestamp: now,\n });\n\n // Emit milestone event\n await runtime.emitEvent([\"DJ_MILESTONE\"], {\n runtime,\n roomId,\n metadata: {\n type: milestone.type,\n value: milestone.value,\n timestamp: now,\n },\n } as Parameters<IAgentRuntime[\"emitEvent\"]>[1]);\n }\n }\n\n await runtime.updateComponent({\n ...component,\n data: mergeStoredField(component, \"analytics\", analytics),\n });\n}\n\n/**\n * Track a listening session\n */\nexport async function trackSession(\n runtime: IAgentRuntime,\n roomId: UUID,\n duration: number,\n): Promise<void> {\n const entityId = getAnalyticsEntityId(runtime, roomId);\n let component = await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n component = await runtime.getComponent(\n roomId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n }\n\n if (!component) {\n const newComponent = await initializeAnalytics(runtime, roomId);\n if (!newComponent) {\n throw new Error(\n `[DJ Analytics] Failed to initialize analytics for room ${roomId}`,\n );\n }\n component = newComponent;\n }\n\n const analytics =\n getStoredField<DJAnalytics>(component, \"analytics\") ??\n createEmptyAnalytics();\n\n analytics.sessionStats.totalSessions += 1;\n const totalDuration =\n analytics.sessionStats.averageSessionDuration *\n (analytics.sessionStats.totalSessions - 1) +\n duration;\n analytics.sessionStats.averageSessionDuration =\n totalDuration / analytics.sessionStats.totalSessions;\n analytics.sessionStats.longestSession = Math.max(\n analytics.sessionStats.longestSession,\n duration,\n );\n\n await runtime.updateComponent({\n ...component,\n data: mergeStoredField(component, \"analytics\", analytics),\n });\n}\n\n/**\n * Track a listener snapshot for analytics\n * Called by the listener tracking service in plugin-radio\n */\nexport async function trackListenerSnapshot(\n runtime: IAgentRuntime,\n roomId: UUID,\n snapshot: ListenerSnapshot,\n): Promise<void> {\n const setup = await ensureAnalyticsEntity(runtime, roomId);\n const { entityId, effectiveRoomId, effectiveWorldId } = setup;\n\n // Get or create component\n let component = await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n const created = await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: effectiveRoomId,\n worldId: effectiveWorldId,\n sourceEntityId: runtime.agentId,\n type: ANALYTICS_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: createStoredField(\"listenerHistory\", []),\n });\n\n if (!created) {\n throw new Error(\n `[DJ Analytics] Failed to create listener tracking component for room ${roomId}`,\n );\n }\n\n // Re-fetch the component\n component = await runtime.getComponent(\n entityId,\n ANALYTICS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n if (!component) {\n throw new Error(\n `[DJ Analytics] Listener tracking component missing after creation for room ${roomId}`,\n );\n }\n }\n\n // Append snapshot to history\n const listenerHistory =\n getStoredField<ListenerSnapshot[]>(component, \"listenerHistory\") ?? [];\n listenerHistory.push(snapshot);\n\n // Keep only last 24 hours of snapshots (assuming 1 per minute = 1440 snapshots)\n const MAX_SNAPSHOTS = 1440;\n if (listenerHistory.length > MAX_SNAPSHOTS) {\n listenerHistory.splice(0, listenerHistory.length - MAX_SNAPSHOTS);\n }\n\n await runtime.updateComponent({\n ...component,\n data: mergeStoredField(component, \"listenerHistory\", listenerHistory),\n });\n}\n","import {\n createUniqueUuid,\n type IAgentRuntime,\n logger,\n type Metadata,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport { ensureAgentStorageContext } from \"./storageContext\";\n\n/**\n * DJ Tip record\n */\nexport interface DJTip {\n from: string;\n fromUserId: string;\n amount: number;\n currency: string;\n message?: string;\n timestamp: number;\n transactionId?: string;\n roomId?: UUID;\n}\n\ninterface TopTipper {\n userId: string;\n username: string;\n totalAmount: number;\n currency: string;\n tipCount: number;\n}\n\n/**\n * DJ Tip Statistics\n */\nexport interface DJTipStats {\n totalTips: number;\n totalAmount: Record<string, number>; // {currency: amount}\n tips: DJTip[];\n topTippers: TopTipper[];\n}\n\nconst DJ_TIPS_COMPONENT_TYPE = \"dj_tips\";\nconst DJ_TIPS_ENTITY_PREFIX = \"dj-tips\";\n\nfunction getDJTipsEntityId(runtime: IAgentRuntime): UUID {\n return createUniqueUuid(\n runtime,\n `${DJ_TIPS_ENTITY_PREFIX}-${runtime.agentId}`,\n );\n}\n\nfunction createEmptyDJTipStats(): DJTipStats {\n return {\n totalTips: 0,\n totalAmount: {},\n tips: [],\n topTippers: [],\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction toDJTipStats(data: unknown): DJTipStats {\n if (!isRecord(data)) return createEmptyDJTipStats();\n return {\n totalTips: typeof data.totalTips === \"number\" ? data.totalTips : 0,\n totalAmount: isRecord(data.totalAmount)\n ? Object.fromEntries(\n Object.entries(data.totalAmount).filter(\n (entry): entry is [string, number] => typeof entry[1] === \"number\",\n ),\n )\n : {},\n tips: Array.isArray(data.tips) ? (data.tips as DJTip[]) : [],\n topTippers: Array.isArray(data.topTippers)\n ? (data.topTippers as TopTipper[])\n : [],\n };\n}\n\nfunction statsToMetadata(stats: DJTipStats): Metadata {\n return {\n totalTips: stats.totalTips,\n totalAmount: stats.totalAmount,\n tips: stats.tips.map((tip) => ({ ...tip })),\n topTippers: stats.topTippers.map((tipper) => ({ ...tipper })),\n };\n}\n\n/**\n * Track a DJ tip\n */\nexport async function trackDJTip(\n runtime: IAgentRuntime,\n roomId: UUID,\n tip: Omit<DJTip, \"roomId\">,\n): Promise<void> {\n const entityId = getDJTipsEntityId(runtime);\n let component = await runtime.getComponent(\n entityId,\n DJ_TIPS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n const storageContext = await ensureAgentStorageContext(\n runtime,\n \"dj-tips\",\n \"radio-plugin\",\n );\n\n component = {\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: storageContext.roomId,\n worldId: storageContext.worldId,\n sourceEntityId: runtime.agentId,\n type: DJ_TIPS_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: statsToMetadata(createEmptyDJTipStats()),\n };\n\n await runtime.createComponent(component);\n }\n\n const stats = toDJTipStats(component.data);\n\n // Add tip\n const tipWithRoom: DJTip = { ...tip, roomId };\n stats.tips.push(tipWithRoom);\n stats.totalTips++;\n\n // Update total amount by currency\n if (!stats.totalAmount) stats.totalAmount = {};\n stats.totalAmount[tip.currency] =\n (stats.totalAmount[tip.currency] || 0) + tip.amount;\n\n // Update top tippers\n if (!stats.topTippers) stats.topTippers = [];\n const tipperIndex = stats.topTippers.findIndex(\n (t) => t.userId === tip.fromUserId,\n );\n\n if (tipperIndex >= 0) {\n stats.topTippers[tipperIndex].totalAmount += tip.amount;\n stats.topTippers[tipperIndex].tipCount++;\n } else {\n stats.topTippers.push({\n userId: tip.fromUserId,\n username: tip.from,\n totalAmount: tip.amount,\n currency: tip.currency,\n tipCount: 1,\n });\n }\n\n // Sort top tippers\n stats.topTippers.sort((a, b) => b.totalAmount - a.totalAmount);\n\n // Keep only last 100 tips\n if (stats.tips.length > 100) {\n stats.tips = stats.tips.slice(-100);\n }\n\n await runtime.updateComponent({\n ...component,\n data: statsToMetadata(stats),\n });\n\n logger.info(`Tracked DJ tip: ${tip.amount} ${tip.currency} from ${tip.from}`);\n}\n\n/**\n * Get DJ tip statistics\n */\nexport async function getDJTipStats(\n runtime: IAgentRuntime,\n): Promise<DJTipStats> {\n const entityId = getDJTipsEntityId(runtime);\n const component = await runtime.getComponent(\n entityId,\n DJ_TIPS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component?.data) {\n return createEmptyDJTipStats();\n }\n\n return toDJTipStats(component.data);\n}\n\n/**\n * Get recent tips\n */\nexport async function getRecentTips(\n runtime: IAgentRuntime,\n limit: number = 10,\n): Promise<DJTip[]> {\n const stats = await getDJTipStats(runtime);\n return stats.tips.slice(-limit).reverse();\n}\n\n/**\n * Get top tippers\n */\nexport async function getTopTippers(\n runtime: IAgentRuntime,\n limit: number = 10,\n): Promise<DJTipStats[\"topTippers\"]> {\n const stats = await getDJTipStats(runtime);\n return stats.topTippers.slice(0, limit);\n}\n","import type { IAgentRuntime, UUID } from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { requireRoomContext } from \"./storageContext\";\n\n/**\n * User music preferences\n */\nexport interface UserMusicPreferences {\n favoriteGenres?: string[];\n favoriteArtists?: string[];\n favoriteTracks?: Array<{ url: string; title: string; playCount?: number }>;\n dislikedTracks?: string[]; // URLs\n skipHistory?: Array<{ url: string; timestamp: number }>;\n requestHistory?: Array<{ url: string; title: string; timestamp: number }>;\n listeningSessions?: Array<{\n startTime: number;\n endTime?: number;\n tracksPlayed: number;\n }>;\n}\n\nconst PREFERENCES_COMPONENT_TYPE = \"dj_preferences\";\n\n/**\n * Update user music preferences\n */\nexport async function updateUserPreferences(\n runtime: IAgentRuntime,\n entityId: UUID,\n preferences: Partial<UserMusicPreferences>,\n roomId?: UUID,\n worldId?: UUID,\n): Promise<UserMusicPreferences> {\n // Try to get existing component with proper filtering\n const existingComponent = await runtime.getComponent(\n entityId,\n PREFERENCES_COMPONENT_TYPE,\n worldId,\n runtime.agentId,\n );\n\n const current =\n getStoredField<UserMusicPreferences>(existingComponent, \"preferences\") ??\n {};\n\n const updated: UserMusicPreferences = {\n ...current,\n ...preferences,\n // Merge arrays\n favoriteGenres: [\n ...new Set([\n ...(current.favoriteGenres || []),\n ...(preferences.favoriteGenres || []),\n ]),\n ],\n favoriteArtists: [\n ...new Set([\n ...(current.favoriteArtists || []),\n ...(preferences.favoriteArtists || []),\n ]),\n ],\n favoriteTracks: mergeFavoriteTracks(\n current.favoriteTracks || [],\n preferences.favoriteTracks || [],\n ),\n dislikedTracks: [\n ...new Set([\n ...(current.dislikedTracks || []),\n ...(preferences.dislikedTracks || []),\n ]),\n ],\n skipHistory: [\n ...(current.skipHistory || []),\n ...(preferences.skipHistory || []),\n ].slice(-100), // Keep last 100\n requestHistory: [\n ...(current.requestHistory || []),\n ...(preferences.requestHistory || []),\n ].slice(-100),\n listeningSessions: [\n ...(current.listeningSessions || []),\n ...(preferences.listeningSessions || []),\n ].slice(-50),\n };\n\n if (existingComponent) {\n await runtime.updateComponent({\n ...existingComponent,\n data: mergeStoredField(existingComponent, \"preferences\", updated),\n });\n } else {\n const entity = await runtime.getEntityById(entityId);\n if (!entity) {\n throw new Error(`Entity ${entityId} not found`);\n }\n\n if (!roomId) {\n throw new Error(\n \"[DJ Preferences] roomId is required when creating a preferences component\",\n );\n }\n\n const roomContext = await requireRoomContext(\n runtime,\n roomId,\n \"DJ Preferences\",\n );\n if (worldId && worldId !== roomContext.worldId) {\n throw new Error(\n `[DJ Preferences] worldId ${worldId} does not match room ${roomId} world ${roomContext.worldId}`,\n );\n }\n\n await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: roomContext.roomId,\n worldId: roomContext.worldId,\n sourceEntityId: runtime.agentId,\n type: PREFERENCES_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: createStoredField(\"preferences\", updated),\n });\n }\n\n return updated;\n}\n\n/**\n * Get user music preferences\n */\nexport async function getUserPreferences(\n runtime: IAgentRuntime,\n entityId: UUID,\n): Promise<UserMusicPreferences | null> {\n const component = await runtime.getComponent(\n entityId,\n PREFERENCES_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n return getStoredField<UserMusicPreferences>(component, \"preferences\");\n}\n\n/**\n * Get preferences for all users in a room\n */\nexport async function getRoomPreferences(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<Map<UUID, UserMusicPreferences>> {\n const entities = await runtime.getEntitiesForRoom(roomId, true);\n const preferences = new Map<UUID, UserMusicPreferences>();\n\n for (const entity of entities) {\n if (!entity.id) {\n continue;\n }\n\n const prefs = await getUserPreferences(runtime, entity.id);\n if (prefs) {\n preferences.set(entity.id, prefs);\n }\n }\n\n return preferences;\n}\n\n/**\n * Merge favorite tracks, incrementing play count\n */\nfunction mergeFavoriteTracks(\n current: UserMusicPreferences[\"favoriteTracks\"],\n newTracks: UserMusicPreferences[\"favoriteTracks\"],\n): UserMusicPreferences[\"favoriteTracks\"] {\n if (!newTracks || newTracks.length === 0) {\n return current || [];\n }\n\n type FavoriteTrack = NonNullable<UserMusicPreferences[\"favoriteTracks\"]>[0];\n const trackMap = new Map<string, FavoriteTrack>();\n\n // Add existing tracks\n (current || []).forEach((track) => {\n trackMap.set(track.url, { ...track, playCount: track.playCount || 1 });\n });\n\n // Add/update new tracks\n newTracks.forEach((track) => {\n const existing = trackMap.get(track.url);\n if (existing) {\n trackMap.set(track.url, {\n ...existing,\n playCount: (existing.playCount || 1) + 1,\n });\n } else {\n trackMap.set(track.url, { ...track, playCount: 1 });\n }\n });\n\n return Array.from(trackMap.values()).sort(\n (a, b) => (b.playCount || 0) - (a.playCount || 0),\n );\n}\n\n/**\n * Track a track request\n */\nexport async function trackTrackRequest(\n runtime: IAgentRuntime,\n entityId: UUID,\n track: { url: string; title: string },\n roomId?: UUID,\n worldId?: UUID,\n): Promise<void> {\n await updateUserPreferences(\n runtime,\n entityId,\n {\n requestHistory: [\n {\n url: track.url,\n title: track.title,\n timestamp: Date.now(),\n },\n ],\n },\n roomId,\n worldId,\n );\n}\n\n/**\n * Track a skip\n */\nexport async function trackSkip(\n runtime: IAgentRuntime,\n entityId: UUID,\n trackUrl: string,\n roomId?: UUID,\n worldId?: UUID,\n): Promise<void> {\n await updateUserPreferences(\n runtime,\n entityId,\n {\n skipHistory: [\n {\n url: trackUrl,\n timestamp: Date.now(),\n },\n ],\n },\n roomId,\n worldId,\n );\n}\n\n/**\n * Track favorite track\n */\nexport async function trackFavorite(\n runtime: IAgentRuntime,\n entityId: UUID,\n track: { url: string; title: string },\n roomId?: UUID,\n worldId?: UUID,\n): Promise<void> {\n await updateUserPreferences(\n runtime,\n entityId,\n {\n favoriteTracks: [track],\n },\n roomId,\n worldId,\n );\n}\n","import { logger } from \"@elizaos/core\";\n\n/**\n * Track play history for repetition control\n */\ninterface PlayHistoryEntry {\n url: string;\n title: string;\n playedAt: number;\n}\n\n/**\n * Smart repetition control to avoid playing same songs too frequently\n */\nexport class RepetitionControl {\n private playHistory: Map<string, PlayHistoryEntry[]> = new Map(); // key: guildId\n private readonly MAX_HISTORY_SIZE = 100; // Keep last 100 plays per guild\n private readonly MIN_REPLAY_INTERVAL = 60 * 60 * 1000; // 1 hour minimum between replays\n\n /**\n * Record a track play\n */\n recordPlay(guildId: string, url: string, title: string): void {\n if (!this.playHistory.has(guildId)) {\n this.playHistory.set(guildId, []);\n }\n\n const history = this.playHistory.get(guildId);\n if (!history) {\n throw new Error(\n `[RepetitionControl] Missing play history bucket for guild ${guildId}`,\n );\n }\n history.push({\n url,\n title,\n playedAt: Date.now(),\n });\n\n // Trim history to max size\n if (history.length > this.MAX_HISTORY_SIZE) {\n history.shift();\n }\n }\n\n /**\n * Check if a track can be played (not played too recently)\n */\n canPlay(guildId: string, url: string, minInterval?: number): boolean {\n const history = this.playHistory.get(guildId);\n if (!history || history.length === 0) {\n return true;\n }\n\n const interval = minInterval || this.MIN_REPLAY_INTERVAL;\n const now = Date.now();\n\n // Find last time this track was played\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].url === url) {\n const timeSincePlay = now - history[i].playedAt;\n if (timeSincePlay < interval) {\n logger.debug(\n `Repetition control: ${url} played ${Math.round(timeSincePlay / 1000 / 60)} minutes ago (min: ${interval / 1000 / 60} minutes)`,\n );\n return false;\n }\n break;\n }\n }\n\n return true;\n }\n\n /**\n * Get recently played tracks for a guild\n */\n getRecentlyPlayed(guildId: string, count: number = 10): PlayHistoryEntry[] {\n const history = this.playHistory.get(guildId);\n if (!history || history.length === 0) {\n return [];\n }\n\n return history.slice(-count).reverse();\n }\n\n /**\n * Filter tracks to avoid repetition\n */\n filterRepetition(\n guildId: string,\n tracks: Array<{ url: string; title: string }>,\n minInterval?: number,\n ): Array<{ url: string; title: string }> {\n return tracks.filter((track) =>\n this.canPlay(guildId, track.url, minInterval),\n );\n }\n\n /**\n * Get play count for a track in recent history\n */\n getRecentPlayCount(\n guildId: string,\n url: string,\n timeWindow: number = 24 * 60 * 60 * 1000,\n ): number {\n const history = this.playHistory.get(guildId);\n if (!history || history.length === 0) {\n return 0;\n }\n\n const now = Date.now();\n let count = 0;\n\n for (let i = history.length - 1; i >= 0; i--) {\n const entry = history[i];\n if (now - entry.playedAt > timeWindow) {\n break; // History is ordered, stop checking\n }\n if (entry.url === url) {\n count++;\n }\n }\n\n return count;\n }\n\n /**\n * Score tracks based on variety (lower score = played less recently)\n */\n scoreByVariety(\n guildId: string,\n tracks: Array<{ url: string; title: string; playCount?: number }>,\n ): Array<{\n url: string;\n title: string;\n playCount?: number;\n varietyScore: number;\n }> {\n const history = this.playHistory.get(guildId);\n const now = Date.now();\n\n return tracks.map((track) => {\n let score = 0;\n\n if (history && history.length > 0) {\n // Find all plays of this track in history\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].url === track.url) {\n const timeSincePlay = now - history[i].playedAt;\n const hoursAgo = timeSincePlay / (1000 * 60 * 60);\n\n // Score penalty decreases over time\n // Recent plays: high penalty, older plays: low penalty\n if (hoursAgo < 1) {\n score += 100; // Played in last hour\n } else if (hoursAgo < 3) {\n score += 50; // Played in last 3 hours\n } else if (hoursAgo < 6) {\n score += 25; // Played in last 6 hours\n } else if (hoursAgo < 24) {\n score += 10; // Played in last day\n } else {\n score += 1; // Played more than a day ago\n }\n }\n }\n }\n\n return {\n ...track,\n varietyScore: score,\n };\n });\n }\n\n /**\n * Clear history for a guild\n */\n clearHistory(guildId: string): void {\n this.playHistory.delete(guildId);\n }\n\n /**\n * Get statistics for a guild\n */\n getStats(guildId: string): {\n totalPlays: number;\n uniqueTracks: number;\n averageRepeatInterval: number;\n } {\n const history = this.playHistory.get(guildId);\n if (!history || history.length === 0) {\n return {\n totalPlays: 0,\n uniqueTracks: 0,\n averageRepeatInterval: 0,\n };\n }\n\n const uniqueUrls = new Set(history.map((entry) => entry.url));\n const urlLastPlayed = new Map<string, number>();\n let totalInterval = 0;\n let intervalCount = 0;\n\n for (const entry of history) {\n const lastPlayed = urlLastPlayed.get(entry.url);\n if (lastPlayed !== undefined) {\n totalInterval += entry.playedAt - lastPlayed;\n intervalCount++;\n }\n urlLastPlayed.set(entry.url, entry.playedAt);\n }\n\n return {\n totalPlays: history.length,\n uniqueTracks: uniqueUrls.size,\n averageRepeatInterval:\n intervalCount > 0 ? totalInterval / intervalCount : 0,\n };\n }\n}\n\n// Global instance\nexport const repetitionControl = new RepetitionControl();\n","import { createUniqueUuid, type IAgentRuntime, type UUID } from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { ensureAgentStorageContext } from \"./storageContext\";\n\n/**\n * Detailed per-song memory and statistics\n * Tracks everything about a song across all rooms\n */\nexport interface SongMemory {\n // Identity\n url: string;\n title: string;\n artist?: string;\n album?: string;\n duration?: number;\n\n // Play Statistics\n totalPlays: number;\n totalPlayTime: number; // milliseconds\n lastPlayed: number;\n firstPlayed: number;\n\n // Request Statistics\n totalRequests: number;\n uniqueRequesters: number;\n topRequesters: Array<{ entityId: UUID; name: string; count: number }>;\n\n // Engagement\n totalLikes: number; // Future: reaction tracking\n totalDislikes: number;\n skipCount: number; // Times users skipped this song\n completionRate: number; // % of times played to completion\n\n // Context\n playedInRooms: Array<{ roomId: UUID; playCount: number; lastPlayed: number }>;\n dedicationCount: number;\n dedications: Array<{\n from: string;\n to: string;\n message?: string;\n timestamp: number;\n }>;\n\n // Timing Patterns\n popularHours: number[]; // 24-element array, play count per hour\n popularDays: number[]; // 7-element array, play count per day\n\n // Performance Metrics\n averageListenerCount: number;\n peakListenerCount: number;\n listenerEngagement: number; // 0-100 score\n\n // Metadata\n createdAt: number;\n updatedAt: number;\n tags?: string[]; // Genre, mood, etc\n notes?: string; // DJ notes about the song\n}\n\nconst SONG_MEMORY_COMPONENT_TYPE = \"song_memory\";\nconst SONG_MEMORY_ENTITY_PREFIX = \"song-memory\";\n\nfunction getSongMemoryEntityId(runtime: IAgentRuntime, url: string): UUID {\n return createUniqueUuid(runtime, `${SONG_MEMORY_ENTITY_PREFIX}-${url}`);\n}\n\n/**\n * Get song memory\n */\nexport async function getSongMemory(\n runtime: IAgentRuntime,\n url: string,\n): Promise<SongMemory | null> {\n const entityId = getSongMemoryEntityId(runtime, url);\n const component = await runtime.getComponent(\n entityId,\n SONG_MEMORY_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n return getStoredField<SongMemory>(component, \"memory\");\n}\n\n/**\n * Create initial song memory\n */\nasync function createSongMemory(\n runtime: IAgentRuntime,\n song: {\n url: string;\n title: string;\n artist?: string;\n album?: string;\n duration?: number;\n },\n): Promise<SongMemory> {\n const entityId = getSongMemoryEntityId(runtime, song.url);\n\n const memory: SongMemory = {\n url: song.url,\n title: song.title,\n artist: song.artist,\n album: song.album,\n duration: song.duration,\n totalPlays: 0,\n totalPlayTime: 0,\n lastPlayed: 0,\n firstPlayed: Date.now(),\n totalRequests: 0,\n uniqueRequesters: 0,\n topRequesters: [],\n totalLikes: 0,\n totalDislikes: 0,\n skipCount: 0,\n completionRate: 100,\n playedInRooms: [],\n dedicationCount: 0,\n dedications: [],\n popularHours: Array(24).fill(0),\n popularDays: Array(7).fill(0),\n averageListenerCount: 0,\n peakListenerCount: 0,\n listenerEngagement: 0,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n const storageContext = await ensureAgentStorageContext(\n runtime,\n \"song-memory\",\n \"music-library\",\n );\n\n await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: storageContext.roomId,\n worldId: storageContext.worldId,\n sourceEntityId: runtime.agentId,\n type: SONG_MEMORY_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: createStoredField(\"memory\", memory),\n });\n\n return memory;\n}\n\n/**\n * Record a play\n */\nexport async function recordSongPlay(\n runtime: IAgentRuntime,\n song: {\n url: string;\n title: string;\n artist?: string;\n album?: string;\n duration?: number;\n },\n context: {\n roomId?: UUID;\n playDuration: number; // milliseconds actually played\n listenerCount?: number;\n requestedBy?: { entityId: UUID; name: string };\n wasSkipped?: boolean;\n },\n): Promise<void> {\n let memory = await getSongMemory(runtime, song.url);\n\n if (!memory) {\n memory = await createSongMemory(runtime, song);\n }\n\n const now = Date.now();\n const hour = new Date(now).getHours();\n const day = new Date(now).getDay();\n\n // Update play statistics\n memory.totalPlays++;\n memory.totalPlayTime += context.playDuration;\n memory.lastPlayed = now;\n memory.updatedAt = now;\n\n // Update timing patterns\n memory.popularHours[hour]++;\n memory.popularDays[day]++;\n\n // Update room statistics\n if (context.roomId) {\n const roomStat = memory.playedInRooms.find(\n (r) => r.roomId === context.roomId,\n );\n if (roomStat) {\n roomStat.playCount++;\n roomStat.lastPlayed = now;\n } else {\n memory.playedInRooms.push({\n roomId: context.roomId,\n playCount: 1,\n lastPlayed: now,\n });\n }\n }\n\n // Update listener metrics\n if (context.listenerCount !== undefined) {\n const totalListeners =\n memory.averageListenerCount * (memory.totalPlays - 1) +\n context.listenerCount;\n memory.averageListenerCount = totalListeners / memory.totalPlays;\n memory.peakListenerCount = Math.max(\n memory.peakListenerCount,\n context.listenerCount,\n );\n }\n\n // Update skip/completion rate\n if (context.wasSkipped) {\n memory.skipCount++;\n }\n\n const expectedDuration = song.duration || 180000; // Default 3 minutes\n const completionPercent = (context.playDuration / expectedDuration) * 100;\n const totalCompletion =\n memory.completionRate * (memory.totalPlays - 1) + completionPercent;\n memory.completionRate = totalCompletion / memory.totalPlays;\n\n // Save\n await updateSongMemory(runtime, song.url, memory);\n}\n\n/**\n * Record a request\n */\nexport async function recordSongRequest(\n runtime: IAgentRuntime,\n song: {\n url: string;\n title: string;\n artist?: string;\n album?: string;\n duration?: number;\n },\n requester: {\n entityId: UUID;\n name: string;\n },\n): Promise<void> {\n let memory = await getSongMemory(runtime, song.url);\n\n if (!memory) {\n memory = await createSongMemory(runtime, song);\n }\n\n memory.totalRequests++;\n memory.updatedAt = Date.now();\n\n // Update top requesters\n const requesterStat = memory.topRequesters.find(\n (r) => r.entityId === requester.entityId,\n );\n if (requesterStat) {\n requesterStat.count++;\n } else {\n memory.topRequesters.push({\n entityId: requester.entityId,\n name: requester.name,\n count: 1,\n });\n memory.uniqueRequesters++;\n }\n\n // Sort and keep top 10\n memory.topRequesters.sort((a, b) => b.count - a.count);\n memory.topRequesters = memory.topRequesters.slice(0, 10);\n\n await updateSongMemory(runtime, song.url, memory);\n}\n\n/**\n * Record a dedication\n */\nexport async function recordSongDedication(\n runtime: IAgentRuntime,\n url: string,\n dedication: {\n from: string;\n to: string;\n message?: string;\n },\n): Promise<void> {\n const memory = await getSongMemory(runtime, url);\n if (!memory) {\n throw new Error(\n `[Song Memory] Cannot record dedication for unknown song ${url}`,\n );\n }\n\n memory.dedicationCount++;\n memory.dedications.push({\n ...dedication,\n timestamp: Date.now(),\n });\n\n // Keep only last 50 dedications\n if (memory.dedications.length > 50) {\n memory.dedications = memory.dedications.slice(-50);\n }\n\n memory.updatedAt = Date.now();\n await updateSongMemory(runtime, url, memory);\n}\n\n/**\n * Update song memory\n */\nasync function updateSongMemory(\n runtime: IAgentRuntime,\n url: string,\n memory: SongMemory,\n): Promise<void> {\n const entityId = getSongMemoryEntityId(runtime, url);\n const component = await runtime.getComponent(\n entityId,\n SONG_MEMORY_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n if (!component) {\n throw new Error(`[Song Memory] Component not found for ${url}`);\n }\n\n await runtime.updateComponent({\n ...component,\n data: mergeStoredField(component, \"memory\", memory),\n });\n}\n\n/**\n * Get top songs by play count\n */\nexport async function getTopSongs(\n _runtime: IAgentRuntime,\n _limit: number = 10,\n): Promise<SongMemory[]> {\n throw new Error(\n \"[Song Memory] getTopSongs requires runtime-level component indexing support\",\n );\n}\n\n/**\n * Get most requested songs\n */\nexport async function getMostRequestedSongs(\n _runtime: IAgentRuntime,\n _limit: number = 10,\n): Promise<SongMemory[]> {\n throw new Error(\n \"[Song Memory] getMostRequestedSongs requires runtime-level component indexing support\",\n );\n}\n","import { type IAgentRuntime, logger, ModelType } from \"@elizaos/core\";\nimport { parseJsonObjectResponse } from \"../utils/json\";\n\nconst MUSIC_ENTITY_DETECTION_SERVICE_NAME = \"musicEntityDetection\";\n\nexport interface DetectedMusicEntity {\n type: \"artist\" | \"album\" | \"song\";\n name: string;\n confidence: number; // 0-1\n context?: string; // Surrounding text\n}\n\ninterface RawDetectedMusicEntity {\n type?: unknown;\n name?: unknown;\n confidence?: unknown;\n context?: unknown;\n}\n\n/**\n * Service for detecting music entity names (artists, albums, songs) from text\n * Uses LLM for intelligent extraction with caching\n */\nexport class MusicEntityDetectionHelper {\n capabilityDescription =\n \"Detects music entity names (artists, albums, songs) from text using LLM\";\n\n private cache: Map<\n string,\n { entities: DetectedMusicEntity[]; timestamp: number }\n > = new Map();\n private readonly CACHE_TTL = 3600000; // 1 hour in milliseconds\n private readonly runtime?: IAgentRuntime;\n\n constructor(runtime?: IAgentRuntime) {\n this.runtime = runtime;\n }\n\n async stop(): Promise<void> {\n this.clearCache();\n }\n\n /**\n * Detect music entities from text using LLM\n */\n async detectEntities(text: string): Promise<DetectedMusicEntity[]> {\n if (!text || text.trim().length === 0) {\n return [];\n }\n\n // Check cache\n const cacheKey = `detect:${text.substring(0, 200)}`; // Use first 200 chars as key\n const cached = this.cache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {\n return cached.entities;\n }\n\n if (!this.runtime) {\n throw new Error(\"Music entity detection requires a runtime\");\n }\n\n try {\n const prompt = `Extract music-related entities from the following text. Identify artists, albums, and songs.\n\nText: \"${text}\"\n\nReturn detected entities as JSON. Each entity should have:\n- type: \"artist\", \"album\", or \"song\"\n- name: the entity name (exact as mentioned)\n- confidence: a number between 0 and 1 indicating confidence\n- context: a brief snippet of surrounding text (optional)\n\nIMPORTANT RULES:\n- Do NOT include URLs (like YouTube links, Spotify links, etc.) as entities\n- Only extract actual artist names, album titles, or song titles\n- URLs should be completely ignored\n\nExample format:\n{\n \"entities\": [\n {\"type\": \"artist\", \"name\": \"The Beatles\", \"confidence\": 0.9, \"context\": \"mentioned in conversation\"},\n {\"type\": \"song\", \"name\": \"Bohemian Rhapsody\", \"confidence\": 0.8}\n ]\n}\n\nIf no music entities are found, return:\n{\"entities\": []}\n\nIMPORTANT: Only return JSON. Do not include explanation or extra text.`;\n\n const response = await this.runtime.useModel(ModelType.TEXT_SMALL, {\n prompt,\n maxTokens: 500,\n });\n\n // Parse JSON response\n let entities: DetectedMusicEntity[] = [];\n try {\n const cleaned = String(response).trim();\n let parsedEntities: RawDetectedMusicEntity[] = [];\n const parsedJson = parseJsonObjectResponse<{\n entities?: RawDetectedMusicEntity[];\n }>(cleaned);\n if (Array.isArray(parsedJson?.entities)) {\n parsedEntities = parsedJson.entities;\n }\n\n // Validate and filter entities\n const urlPattern = /^https?:\\/\\//i;\n entities = parsedEntities\n .filter((e: RawDetectedMusicEntity) => {\n // Basic validation\n if (!e || typeof e !== \"object\") return false;\n if (\n typeof e.type !== \"string\" ||\n ![\"artist\", \"album\", \"song\"].includes(e.type)\n ) {\n return false;\n }\n if (typeof e.name !== \"string\" || e.name.trim().length === 0)\n return false;\n if (\n typeof e.confidence !== \"number\" ||\n e.confidence < 0 ||\n e.confidence > 1\n )\n return false;\n\n // Filter out URLs\n if (urlPattern.test(e.name)) {\n return false;\n }\n\n return true;\n })\n .map((e: RawDetectedMusicEntity): DetectedMusicEntity => {\n const context =\n typeof e.context === \"string\" ? e.context.trim() : undefined;\n\n return {\n type: e.type as DetectedMusicEntity[\"type\"],\n name: (e.name as string).trim(),\n confidence: e.confidence as number,\n context,\n };\n })\n .filter((e: DetectedMusicEntity) => e.confidence > 0.3); // Filter low confidence\n } catch (parseError) {\n throw new Error(\n `Failed to parse music entity detection response: ${\n parseError instanceof Error\n ? parseError.message\n : String(parseError)\n }`,\n );\n }\n\n // Cache results\n this.cache.set(cacheKey, {\n entities,\n timestamp: Date.now(),\n });\n\n return entities;\n } catch (error) {\n logger.error(`Error detecting music entities: ${error}`);\n throw error;\n }\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Clear expired cache entries\n */\n clearExpiredCache(): void {\n const now = Date.now();\n for (const [key, value] of this.cache.entries()) {\n if (now - value.timestamp >= this.CACHE_TTL) {\n this.cache.delete(key);\n }\n }\n }\n}\n\nexport const MUSIC_ENTITY_DETECTION_HELPER_NAME =\n MUSIC_ENTITY_DETECTION_SERVICE_NAME;\n\nexport { MusicEntityDetectionHelper as MusicEntityDetectionService };\n","import { type IAgentRuntime, logger } from \"@elizaos/core\";\nimport type {\n AlbumInfo,\n ArtistInfo,\n MusicInfoResult,\n TrackInfo,\n} from \"../types\";\nimport { GeniusClient } from \"./geniusClient\";\nimport { LastFmClient } from \"./lastFmClient\";\nimport { MusicBrainzClient } from \"./musicBrainzClient\";\nimport type { MusicInfoServiceStatus, ServiceStatus } from \"./serviceStatus\";\nimport { TheAudioDbClient } from \"./theAudioDbClient\";\nimport type { WikipediaClient } from \"./wikipediaClient\";\n\nconst MUSIC_INFO_SERVICE_NAME = \"musicInfo\";\n\n/**\n * Service for fetching music information from authoritative sources.\n * Track metadata comes from YouTube for direct URLs and MusicBrainz for\n * text queries; artist and album metadata comes from MusicBrainz only.\n */\nexport class MusicInfoHelper {\n capabilityDescription =\n \"Fetches music metadata (tracks, artists, albums) from authoritative sources\";\n\n private cache: Map<string, { data: MusicInfoResult; timestamp: number }> =\n new Map();\n private readonly CACHE_TTL = 3600000; // 1 hour in milliseconds\n private musicBrainzClient: MusicBrainzClient | null = null;\n private lastFmClient: LastFmClient | null = null;\n private geniusClient: GeniusClient | null = null;\n private theAudioDbClient: TheAudioDbClient | null = null;\n private readonly wikipediaClient: WikipediaClient | null;\n private serviceStatus: MusicInfoServiceStatus = {\n musicBrainz: { status: \"not_configured\" as ServiceStatus, lastChecked: 0 },\n lastFm: { status: \"not_configured\" as ServiceStatus, lastChecked: 0 },\n genius: { status: \"not_configured\" as ServiceStatus, lastChecked: 0 },\n theAudioDb: { status: \"not_configured\" as ServiceStatus, lastChecked: 0 },\n wikipedia: { status: \"not_configured\" as ServiceStatus, lastChecked: 0 },\n };\n\n constructor(\n runtime?: IAgentRuntime,\n wikipediaClient?: WikipediaClient | null,\n ) {\n this.wikipediaClient = wikipediaClient ?? null;\n\n // Initialize MusicBrainz (free, no API key needed)\n const userAgent =\n (runtime?.getSetting(\"MUSICBRAINZ_USER_AGENT\") as string) ||\n \"ElizaOS-MusicInfo/1.0.0 (https://github.com/elizaos/eliza)\";\n this.musicBrainzClient = new MusicBrainzClient(userAgent);\n this.serviceStatus.musicBrainz = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n };\n\n // Initialize Last.fm if API key is provided\n const lastFmApiKey = runtime?.getSetting(\"LASTFM_API_KEY\") as string;\n if (lastFmApiKey) {\n try {\n this.lastFmClient = new LastFmClient(lastFmApiKey);\n this.serviceStatus.lastFm = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n };\n } catch (error) {\n logger.warn(`Last.fm client not initialized: ${error}`);\n this.serviceStatus.lastFm = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n }\n }\n\n // Initialize Genius if API key is provided\n const geniusApiKey = runtime?.getSetting(\"GENIUS_API_KEY\") as string;\n if (geniusApiKey) {\n try {\n this.geniusClient = new GeniusClient(geniusApiKey);\n this.serviceStatus.genius = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n };\n } catch (error) {\n logger.warn(`Genius client not initialized: ${error}`);\n this.serviceStatus.genius = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n }\n }\n\n // Initialize TheAudioDB if API key is provided\n const theAudioDbApiKey = runtime?.getSetting(\n \"THEAUDIODB_API_KEY\",\n ) as string;\n if (theAudioDbApiKey) {\n try {\n this.theAudioDbClient = new TheAudioDbClient(theAudioDbApiKey);\n this.serviceStatus.theAudioDb = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n };\n } catch (error) {\n logger.warn(`TheAudioDB client not initialized: ${error}`);\n this.serviceStatus.theAudioDb = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n }\n }\n\n // Check Wikipedia service availability\n if (this.wikipediaClient) {\n this.serviceStatus.wikipedia = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n };\n }\n\n // Validate API keys asynchronously (don't block initialization)\n this.validateApiKeys().catch((error) => {\n logger.debug(`API key validation completed with some issues: ${error}`);\n });\n }\n\n async stop(): Promise<void> {\n this.clearCache();\n }\n\n /**\n * Get service status for all integrated APIs\n */\n getServiceStatus(): MusicInfoServiceStatus {\n return { ...this.serviceStatus };\n }\n\n /**\n * Validate API keys for all configured services\n * Updates service status based on validation results\n */\n private async validateApiKeys(): Promise<void> {\n // Validate Last.fm\n if (this.lastFmClient) {\n try {\n const startTime = Date.now();\n // Test with a well-known artist\n const testResult = await this.lastFmClient.getArtistInfo(\"The Beatles\");\n const responseTime = Date.now() - startTime;\n if (testResult) {\n this.serviceStatus.lastFm = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n responseTime,\n };\n } else {\n this.serviceStatus.lastFm = {\n status: \"degraded\" as ServiceStatus,\n lastChecked: Date.now(),\n responseTime,\n lastError: \"API returned no results\",\n };\n }\n } catch (error) {\n this.serviceStatus.lastFm = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n logger.warn(`Last.fm API validation failed: ${error}`);\n }\n }\n\n // Validate Genius\n if (this.geniusClient) {\n try {\n const startTime = Date.now();\n const isValid = await this.geniusClient.validateApiKey();\n const responseTime = Date.now() - startTime;\n this.serviceStatus.genius = {\n status: isValid\n ? (\"active\" as ServiceStatus)\n : (\"unavailable\" as ServiceStatus),\n lastChecked: Date.now(),\n responseTime,\n lastError: isValid ? undefined : \"Invalid API key\",\n };\n if (!isValid) {\n logger.warn(\"Genius API key validation failed\");\n }\n } catch (error) {\n this.serviceStatus.genius = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n logger.warn(`Genius API validation failed: ${error}`);\n }\n }\n\n // Validate TheAudioDB\n if (this.theAudioDbClient) {\n try {\n const startTime = Date.now();\n const isValid = await this.theAudioDbClient.validateApiKey();\n const responseTime = Date.now() - startTime;\n this.serviceStatus.theAudioDb = {\n status: isValid\n ? (\"active\" as ServiceStatus)\n : (\"unavailable\" as ServiceStatus),\n lastChecked: Date.now(),\n responseTime,\n lastError: isValid ? undefined : \"Invalid API key\",\n };\n if (!isValid) {\n logger.warn(\"TheAudioDB API key validation failed\");\n }\n } catch (error) {\n this.serviceStatus.theAudioDb = {\n status: \"unavailable\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n logger.warn(`TheAudioDB API validation failed: ${error}`);\n }\n }\n\n // Validate MusicBrainz (always available, but test connectivity)\n if (this.musicBrainzClient) {\n try {\n const startTime = Date.now();\n await this.musicBrainzClient.searchRecording(\"Test\", \"Test\");\n const responseTime = Date.now() - startTime;\n this.serviceStatus.musicBrainz = {\n status: \"active\" as ServiceStatus,\n lastChecked: Date.now(),\n responseTime,\n };\n } catch (error) {\n this.serviceStatus.musicBrainz = {\n status: \"degraded\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n logger.warn(`MusicBrainz connectivity check failed: ${error}`);\n }\n }\n\n // Validate Wikipedia\n if (this.wikipediaClient) {\n try {\n const startTime = Date.now();\n const testResult =\n await this.wikipediaClient.getArtistInfo(\"The Beatles\");\n const responseTime = Date.now() - startTime;\n this.serviceStatus.wikipedia = {\n status: testResult\n ? (\"active\" as ServiceStatus)\n : (\"degraded\" as ServiceStatus),\n lastChecked: Date.now(),\n responseTime,\n };\n } catch (error) {\n this.serviceStatus.wikipedia = {\n status: \"degraded\" as ServiceStatus,\n lastChecked: Date.now(),\n lastError: String(error),\n };\n logger.warn(`Wikipedia service check failed: ${error}`);\n }\n }\n }\n\n /**\n * Extract track information from a YouTube URL or MusicBrainz lookup.\n */\n async getTrackInfo(urlOrTitle: string): Promise<MusicInfoResult | null> {\n const cacheKey = `track:${urlOrTitle}`;\n const cached = this.cache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {\n return cached.data;\n }\n\n try {\n if (this.isYouTubeUrl(urlOrTitle)) {\n const info = await this.getInfoFromYouTube(urlOrTitle);\n if (info?.track) {\n const result: MusicInfoResult = {\n track: info.track,\n source: info.source,\n };\n this.cache.set(cacheKey, { data: result, timestamp: Date.now() });\n return result;\n }\n return null;\n }\n\n const parsed = this.parseTitle(urlOrTitle);\n if (!parsed.title) {\n throw new Error(`Track title is required for lookup: ${urlOrTitle}`);\n }\n\n if (!this.musicBrainzClient) {\n throw new Error(\"MusicBrainz client is unavailable\");\n }\n\n const mbTrack = await this.musicBrainzClient.searchRecording(\n parsed.title,\n parsed.artist,\n );\n if (!mbTrack) {\n return null;\n }\n\n const result: MusicInfoResult = {\n track: mbTrack,\n source: \"musicbrainz\",\n };\n this.cache.set(cacheKey, { data: result, timestamp: Date.now() });\n return result;\n } catch (error) {\n logger.error(`Error fetching track info for ${urlOrTitle}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Parse title string to extract artist and track name\n */\n private parseTitle(title: string): { title: string; artist?: string } {\n const patterns = [\n /^(.+?)\\s*-\\s*(.+)$/, // \"Artist - Title\"\n /^(.+?)\\s+by\\s+(.+)$/i, // \"Title by Artist\"\n ];\n\n for (const pattern of patterns) {\n const match = title.match(pattern);\n if (match) {\n const [, part1, part2] = match;\n // Determine which is artist and which is title based on pattern\n if (pattern.source.includes(\"by\")) {\n return { title: part1.trim(), artist: part2.trim() };\n } else {\n return { title: part2.trim(), artist: part1.trim() };\n }\n }\n }\n\n return { title: title.trim() };\n }\n\n /**\n * Get artist information from MusicBrainz.\n */\n async getArtistInfo(artistName: string): Promise<ArtistInfo | null> {\n const cacheKey = `artist:${artistName}`;\n const cached = this.cache.get(cacheKey);\n if (cached?.data.artist) {\n if (Date.now() - cached.timestamp < this.CACHE_TTL) {\n return cached.data.artist;\n }\n }\n\n try {\n if (!this.musicBrainzClient) {\n throw new Error(\"MusicBrainz client is unavailable\");\n }\n\n const mbArtist = await this.musicBrainzClient.getArtist(artistName);\n if (!mbArtist) {\n return null;\n }\n\n this.cache.set(cacheKey, {\n data: { artist: mbArtist, source: \"musicbrainz\" },\n timestamp: Date.now(),\n });\n\n return mbArtist;\n } catch (error) {\n logger.error(`Error fetching artist info for ${artistName}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Get album information from MusicBrainz.\n */\n async getAlbumInfo(\n albumTitle: string,\n artistName?: string,\n ): Promise<AlbumInfo | null> {\n const cacheKey = `album:${albumTitle}:${artistName || \"\"}`;\n const cached = this.cache.get(cacheKey);\n if (cached?.data.album) {\n if (Date.now() - cached.timestamp < this.CACHE_TTL) {\n return cached.data.album;\n }\n }\n\n try {\n if (!this.musicBrainzClient) {\n throw new Error(\"MusicBrainz client is unavailable\");\n }\n\n const mbAlbum = await this.musicBrainzClient.getRelease(\n albumTitle,\n artistName,\n );\n if (!mbAlbum) {\n return null;\n }\n\n this.cache.set(cacheKey, {\n data: { album: mbAlbum, source: \"musicbrainz\" },\n timestamp: Date.now(),\n });\n\n return mbAlbum;\n } catch (error) {\n logger.error(`Error fetching album info for ${albumTitle}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Check if a string is a YouTube URL\n */\n private isYouTubeUrl(str: string): boolean {\n const youtubeRegex =\n /^(https?:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.be)\\/(watch\\?v=|embed\\/|v\\/)?([a-zA-Z0-9_-]{11})/;\n return youtubeRegex.test(str);\n }\n\n /**\n * Extract information from YouTube URL using play-dl\n */\n private async getInfoFromYouTube(\n url: string,\n ): Promise<MusicInfoResult | null> {\n try {\n // Dynamic import to avoid bundling issues\n const play = await import(\"@vookav2/play-dl\").then((m) => m.default || m);\n const videoInfo = await play.video_info(url);\n\n const trackInfo: TrackInfo = {\n title: videoInfo.video_details.title || \"Unknown Title\",\n artist: videoInfo.video_details.channel?.name || \"Unknown Artist\",\n duration: videoInfo.video_details.durationInSec || undefined,\n url: url,\n thumbnail: videoInfo.video_details.thumbnails?.[0]?.url || undefined,\n description: videoInfo.video_details.description || undefined,\n };\n\n return {\n track: trackInfo,\n source: \"youtube\",\n };\n } catch (error) {\n logger.error(`Error extracting YouTube info: ${error}`);\n throw error;\n }\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Clear expired cache entries\n */\n clearExpiredCache(): void {\n const now = Date.now();\n for (const [key, value] of this.cache.entries()) {\n if (now - value.timestamp >= this.CACHE_TTL) {\n this.cache.delete(key);\n }\n }\n }\n\n /**\n * Pre-warm cache for a track (non-blocking)\n * This is called by plugin-dj to prepare caches before tracks are played\n * @param urlOrTitle - YouTube URL or track title\n */\n async prewarmTrackInfo(urlOrTitle: string): Promise<void> {\n // Check if already cached\n const cacheKey = `track:${urlOrTitle}`;\n const cached = this.cache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {\n // Already cached and fresh, no need to pre-warm\n return;\n }\n\n // Pre-warm asynchronously (fire and forget)\n this.getTrackInfo(urlOrTitle).catch((error) => {\n // Silently log errors - pre-warming is best effort\n logger.debug(`Failed to pre-warm track info for ${urlOrTitle}: ${error}`);\n });\n }\n\n /**\n * Pre-warm cache for multiple tracks (non-blocking)\n * @param tracks - Array of YouTube URLs or track titles\n */\n async prewarmTracks(tracks: string[]): Promise<void> {\n // Pre-warm all tracks in parallel (non-blocking)\n const promises = tracks.map((track) => this.prewarmTrackInfo(track));\n await Promise.allSettled(promises);\n }\n}\n\nexport const MUSIC_INFO_HELPER_NAME = MUSIC_INFO_SERVICE_NAME;\n\nexport { MusicInfoHelper as MusicInfoService };\n","import { logger } from \"@elizaos/core\";\nimport { type RetryableError, retryWithBackoff } from \"../utils/retry\";\n\ntype GeniusHttpError = Error &\n RetryableError & {\n response?: {\n status?: number;\n statusText?: string;\n headers?: Headers;\n };\n };\n\ninterface GeniusSearchHit {\n result: {\n id: number;\n title: string;\n primary_artist: {\n name: string;\n };\n url: string;\n };\n}\n\ninterface GeniusSearchResponse {\n response?: {\n hits?: GeniusSearchHit[];\n };\n}\n\ninterface GeniusSongResponse {\n response?: {\n song?: {\n title: string;\n primary_artist: {\n name: string;\n };\n url: string;\n };\n };\n}\n\nfunction buildGeniusHttpError(response: Response): GeniusHttpError {\n const error = new Error(\n `Genius API error: ${response.status} ${response.statusText}`,\n ) as GeniusHttpError;\n error.response = {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n };\n return error;\n}\n\n/**\n * Client for Genius API\n * Free tier with API key\n * Rate limit: Reasonable for free tier\n * Documentation: https://docs.genius.com/\n */\nexport class GeniusClient {\n private readonly baseUrl = \"https://api.genius.com\";\n private readonly apiKey: string;\n private lastRequestTime = 0;\n private readonly minRequestInterval = 200; // 200ms = 5 requests per second (conservative)\n\n constructor(apiKey: string) {\n if (!apiKey) {\n throw new Error(\"Genius API key is required\");\n }\n this.apiKey = apiKey;\n }\n\n /**\n * Rate limit: ensure we don't exceed rate limits\n */\n private async rateLimit(): Promise<void> {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n this.lastRequestTime = Date.now();\n }\n\n /**\n * Search for a song\n */\n async searchSong(query: string): Promise<Array<{\n id: number;\n title: string;\n artist: string;\n url: string;\n }> | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const url = `${this.baseUrl}/search?q=${encodeURIComponent(query)}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"User-Agent\": \"ElizaOS-MusicInfo/1.0.0\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n if (response.status === 401) {\n // Don't retry on authentication errors\n logger.warn(\"Genius API: Invalid API key\");\n return null;\n }\n throw buildGeniusHttpError(response);\n }\n\n const data = (await response.json()) as GeniusSearchResponse;\n if (!data.response?.hits) {\n return null;\n }\n\n return data.response.hits\n .map((hit) => ({\n id: hit.result.id,\n title: hit.result.title,\n artist: hit.result.primary_artist.name,\n url: hit.result.url,\n }))\n .slice(0, 5); // Limit to top 5 results\n }).catch((error) => {\n logger.error(`Error searching Genius after retries: ${error}`);\n return null;\n });\n }\n\n /**\n * Get lyrics for a song by ID\n * Note: Genius API doesn't directly provide lyrics, but we can get the URL\n * For actual lyrics, we'd need to scrape the page (which requires separate implementation)\n */\n async getSongInfo(songId: number): Promise<{\n title: string;\n artist: string;\n url: string;\n lyricsUrl: string;\n } | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const url = `${this.baseUrl}/songs/${songId}`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"User-Agent\": \"ElizaOS-MusicInfo/1.0.0\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildGeniusHttpError(response);\n }\n\n const data = (await response.json()) as GeniusSongResponse;\n if (!data.response?.song) {\n return null;\n }\n\n const song = data.response.song;\n return {\n title: song.title,\n artist: song.primary_artist.name,\n url: song.url,\n lyricsUrl: song.url, // Genius URLs point to lyrics pages\n };\n }).catch((error) => {\n logger.error(`Error getting Genius song info after retries: ${error}`);\n return null;\n });\n }\n\n /**\n * Get lyrics for a track (searches first, then gets song info)\n */\n async getLyrics(\n trackName: string,\n artistName?: string,\n ): Promise<string | null> {\n try {\n const query = artistName ? `${trackName} ${artistName}` : trackName;\n const searchResults = await this.searchSong(query);\n\n if (!searchResults || searchResults.length === 0) {\n return null;\n }\n\n // Try to find exact match\n let songId: number | null = null;\n const trackLower = trackName.toLowerCase();\n const artistLower = artistName?.toLowerCase();\n\n for (const result of searchResults) {\n if (result.title.toLowerCase().includes(trackLower)) {\n if (\n !artistLower ||\n result.artist.toLowerCase().includes(artistLower)\n ) {\n songId = result.id;\n break;\n }\n }\n }\n\n // If no exact match, use first result\n if (!songId && searchResults.length > 0) {\n songId = searchResults[0].id;\n }\n\n if (!songId) {\n return null;\n }\n\n const songInfo = await this.getSongInfo(songId);\n if (!songInfo) {\n return null;\n }\n\n // Note: Genius API doesn't provide lyrics directly via API\n // The lyricsUrl can be used to scrape lyrics if needed\n // For now, we return the URL as a reference\n // A lyrics scraping service would need to be implemented separately\n return songInfo.lyricsUrl;\n } catch (error) {\n logger.error(`Error getting lyrics from Genius: ${error}`);\n return null;\n }\n }\n\n /**\n * Validate API key by making a test request\n */\n async validateApiKey(): Promise<boolean> {\n return retryWithBackoff(\n async () => {\n await this.rateLimit();\n const url = `${this.baseUrl}/account`;\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"User-Agent\": \"ElizaOS-MusicInfo/1.0.0\",\n Accept: \"application/json\",\n },\n });\n return response.ok;\n },\n {\n maxRetries: 2, // Fewer retries for validation\n retryableErrors: (error: RetryableError) => {\n const status = error.response?.status;\n\n // Only retry on network errors, not auth errors\n return (\n error?.code === \"ECONNRESET\" ||\n error?.code === \"ETIMEDOUT\" ||\n error?.code === \"ENOTFOUND\" ||\n (typeof status === \"number\" && status >= 500 && status < 600)\n );\n },\n },\n ).catch(() => false);\n }\n}\n","/**\n * Retry utility with exponential backoff\n */\n\nexport interface RetryableError {\n code?: string;\n response?: {\n status?: number;\n headers?: Record<string, string> | Headers;\n };\n}\n\nexport interface RetryOptions {\n maxRetries?: number;\n initialDelay?: number; // in milliseconds\n maxDelay?: number; // in milliseconds\n backoffMultiplier?: number;\n retryableErrors?: (error: RetryableError) => boolean;\n}\n\nconst DEFAULT_OPTIONS: Required<RetryOptions> = {\n maxRetries: 3,\n initialDelay: 1000, // 1 second\n maxDelay: 30000, // 30 seconds\n backoffMultiplier: 2,\n retryableErrors: (error: RetryableError) => {\n const status = error.response?.status;\n\n // Retry on network errors, timeouts, and 5xx errors\n if (\n error?.code === \"ECONNRESET\" ||\n error?.code === \"ETIMEDOUT\" ||\n error?.code === \"ENOTFOUND\"\n ) {\n return true;\n }\n if (typeof status === \"number\" && status >= 500 && status < 600) {\n return true;\n }\n // Retry on rate limit errors (429)\n if (status === 429) {\n return true;\n }\n return false;\n },\n};\n\n/**\n * Sleep for the specified number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate delay with exponential backoff\n */\nfunction calculateDelay(\n attempt: number,\n options: Required<RetryOptions>,\n): number {\n const delay = options.initialDelay * options.backoffMultiplier ** attempt;\n return Math.min(delay, options.maxDelay);\n}\n\n/**\n * Retry a function with exponential backoff\n * @param fn The async function to retry\n * @param options Retry configuration options\n * @returns The result of the function\n * @throws The last error if all retries fail\n */\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n options: RetryOptions = {},\n): Promise<T> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error: unknown) {\n const retryableError = error as RetryableError;\n lastError = error;\n\n // Don't retry if we've exhausted all attempts\n if (attempt >= opts.maxRetries) {\n break;\n }\n\n // Don't retry if the error is not retryable\n if (!opts.retryableErrors(retryableError)) {\n break;\n }\n\n // Calculate delay with exponential backoff\n const delay = calculateDelay(attempt, opts);\n\n // For rate limit errors, use the Retry-After header if available\n if (retryableError.response?.status === 429) {\n const headers = retryableError.response.headers;\n const retryAfter =\n headers instanceof Headers\n ? headers.get(\"retry-after\") || undefined\n : headers?.[\"retry-after\"];\n if (retryAfter) {\n const retryAfterMs = parseInt(retryAfter, 10) * 1000;\n await sleep(Math.max(retryAfterMs, delay));\n continue;\n }\n }\n\n // Wait before retrying\n await sleep(delay);\n }\n }\n\n throw lastError instanceof Error\n ? lastError\n : new Error(String(lastError ?? \"Retry attempts exhausted\"));\n}\n","import { logger } from \"@elizaos/core\";\nimport type { AlbumInfo, ArtistInfo, TrackInfo } from \"../types\";\nimport { type RetryableError, retryWithBackoff } from \"../utils/retry\";\n\ntype LastFmHttpError = Error & RetryableError;\n\ninterface LastFmTag {\n name: string;\n}\n\ninterface LastFmImage {\n size?: string;\n \"#text\"?: string;\n}\n\ninterface LastFmNamedEntity {\n name: string;\n}\n\ninterface LastFmTrackPayload {\n name: string;\n artist?: { name?: string };\n album?: { title?: string };\n duration?: string;\n toptags?: { tag?: LastFmTag[] };\n url?: string;\n wiki?: { content?: string };\n}\n\ninterface LastFmArtistPayload {\n name: string;\n tags?: { tag?: LastFmTag[] };\n bio?: { content?: string };\n image?: LastFmImage[];\n similar?: { artist?: LastFmNamedEntity[] };\n toptracks?: { track?: LastFmNamedEntity[] };\n albums?: { album?: LastFmNamedEntity[] };\n}\n\ninterface LastFmAlbumPayload {\n name: string;\n artist?: string;\n wiki?: { published?: string; content?: string };\n tags?: { tag?: LastFmTag[] };\n tracks?: { track?: LastFmNamedEntity[] };\n image?: LastFmImage[];\n}\n\ninterface LastFmTrackInfoResponse {\n error?: number;\n message?: string;\n track?: LastFmTrackPayload;\n}\n\ninterface LastFmArtistInfoResponse {\n error?: number;\n message?: string;\n artist?: LastFmArtistPayload;\n}\n\ninterface LastFmAlbumInfoResponse {\n error?: number;\n message?: string;\n album?: LastFmAlbumPayload;\n}\n\nfunction buildLastFmHttpError(response: Response): LastFmHttpError {\n const error = new Error(\n `Last.fm API error: ${response.status} ${response.statusText}`,\n ) as LastFmHttpError;\n error.response = {\n status: response.status,\n headers: response.headers,\n };\n return error;\n}\n\n/**\n * Client for Last.fm API\n * Free tier with API key\n * Rate limit: 5 requests per second\n */\nexport class LastFmClient {\n private readonly baseUrl = \"https://ws.audioscrobbler.com/2.0\";\n private readonly apiKey: string;\n private lastRequestTime = 0;\n private readonly minRequestInterval = 200; // 200ms = 5 requests per second\n\n constructor(apiKey: string) {\n if (!apiKey) {\n throw new Error(\"Last.fm API key is required\");\n }\n this.apiKey = apiKey;\n }\n\n /**\n * Rate limit: ensure we don't exceed 5 requests per second\n */\n private async rateLimit(): Promise<void> {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n this.lastRequestTime = Date.now();\n }\n\n /**\n * Get track information\n */\n async getTrackInfo(\n trackName: string,\n artistName: string,\n ): Promise<TrackInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const params = new URLSearchParams({\n method: \"track.getInfo\",\n api_key: this.apiKey,\n track: trackName,\n artist: artistName,\n format: \"json\",\n });\n\n const url = `${this.baseUrl}/?${params.toString()}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw buildLastFmHttpError(response);\n }\n\n const data = (await response.json()) as LastFmTrackInfoResponse;\n if (data.error) {\n // Don't retry on API errors (invalid key, not found, etc.)\n logger.debug(`Last.fm error: ${data.message}`);\n return null;\n }\n\n const track = data.track;\n if (!track) {\n return null;\n }\n\n const trackInfo: TrackInfo = {\n title: track.name,\n artist: track.artist?.name || artistName,\n album: track.album?.title,\n duration: track.duration\n ? Math.floor(parseInt(track.duration, 10) / 1000)\n : undefined,\n tags: track.toptags?.tag?.map((tag) => tag.name) || [],\n url: track.url,\n description: track.wiki?.content\n ? track.wiki.content.substring(0, 500).replace(/<[^>]*>/g, \"\")\n : undefined,\n };\n\n return trackInfo;\n }).catch((error) => {\n logger.error(`Error fetching Last.fm track info after retries: ${error}`);\n return null;\n });\n }\n\n /**\n * Get artist information\n */\n async getArtistInfo(artistName: string): Promise<ArtistInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const params = new URLSearchParams({\n method: \"artist.getInfo\",\n api_key: this.apiKey,\n artist: artistName,\n format: \"json\",\n });\n\n const url = `${this.baseUrl}/?${params.toString()}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw buildLastFmHttpError(response);\n }\n\n const data = (await response.json()) as LastFmArtistInfoResponse;\n if (data.error || !data.artist) {\n // Don't retry on API errors (invalid key, not found, etc.)\n return null;\n }\n\n const artist = data.artist;\n const artistInfo: ArtistInfo = {\n name: artist.name,\n genres: artist.tags?.tag?.map((tag) => tag.name) || [],\n bio: artist.bio?.content\n ? artist.bio.content.substring(0, 1000).replace(/<[^>]*>/g, \"\")\n : undefined,\n image: artist.image?.find((img) => img.size === \"large\")?.[\"#text\"],\n similarArtists: artist.similar?.artist?.map((a) => a.name) || [],\n topTracks: artist.toptracks?.track?.map((track) => track.name) || [],\n albums: artist.albums?.album?.map((album) => album.name) || [],\n };\n\n return artistInfo;\n }).catch((error) => {\n logger.error(\n `Error fetching Last.fm artist info after retries: ${error}`,\n );\n return null;\n });\n }\n\n /**\n * Get album information\n */\n async getAlbumInfo(\n albumName: string,\n artistName: string,\n ): Promise<AlbumInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const params = new URLSearchParams({\n method: \"album.getInfo\",\n api_key: this.apiKey,\n album: albumName,\n artist: artistName,\n format: \"json\",\n });\n\n const url = `${this.baseUrl}/?${params.toString()}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw buildLastFmHttpError(response);\n }\n\n const data = (await response.json()) as LastFmAlbumInfoResponse;\n if (data.error || !data.album) {\n // Don't retry on API errors (invalid key, not found, etc.)\n return null;\n }\n\n const album = data.album;\n const albumInfo: AlbumInfo = {\n title: album.name,\n artist: album.artist || artistName,\n year: album.wiki?.published\n ? parseInt(album.wiki.published.substring(0, 4), 10)\n : undefined,\n genre: album.tags?.tag?.map((tag) => tag.name) || [],\n tracks: album.tracks?.track?.map((track) => track.name) || [],\n coverArt: album.image?.find((img) => img.size === \"large\")?.[\"#text\"],\n description: album.wiki?.content\n ? album.wiki.content.substring(0, 500).replace(/<[^>]*>/g, \"\")\n : undefined,\n };\n\n return albumInfo;\n }).catch((error) => {\n logger.error(`Error fetching Last.fm album info after retries: ${error}`);\n return null;\n });\n }\n}\n","import { logger } from \"@elizaos/core\";\nimport type { AlbumInfo, ArtistInfo, TrackInfo } from \"../types\";\nimport { retryWithBackoff } from \"../utils/retry\";\n\ninterface MusicBrainzTag {\n name: string;\n}\n\ninterface MusicBrainzArtistCredit {\n name?: string;\n}\n\ninterface MusicBrainzRelease {\n title: string;\n date?: string;\n tags?: MusicBrainzTag[];\n \"artist-credit\"?: MusicBrainzArtistCredit[];\n}\n\ninterface MusicBrainzRecording {\n title: string;\n length?: number;\n tags?: MusicBrainzTag[];\n releases?: MusicBrainzRelease[];\n \"artist-credit\"?: MusicBrainzArtistCredit[];\n}\n\ninterface MusicBrainzArtistAlias {\n name: string;\n}\n\ninterface MusicBrainzArtist {\n name: string;\n tags?: MusicBrainzTag[];\n aliases?: MusicBrainzArtistAlias[];\n}\n\ninterface MusicBrainzRecordingResponse {\n recordings?: MusicBrainzRecording[];\n}\n\ninterface MusicBrainzArtistResponse {\n artists?: MusicBrainzArtist[];\n}\n\ninterface MusicBrainzReleaseResponse {\n releases?: MusicBrainzRelease[];\n}\n\ntype MusicBrainzHttpError = Error & {\n response?: {\n status: number;\n statusText: string;\n };\n};\n\nfunction buildMusicBrainzHttpError(response: Response): MusicBrainzHttpError {\n const error = new Error(\n `MusicBrainz API error: ${response.status} ${response.statusText}`,\n ) as MusicBrainzHttpError;\n error.response = {\n status: response.status,\n statusText: response.statusText,\n };\n return error;\n}\n\n/**\n * Client for MusicBrainz API\n * Free, no authentication required (just User-Agent header)\n * Rate limit: 1 request per second\n */\nexport class MusicBrainzClient {\n private readonly baseUrl = \"https://musicbrainz.org/ws/2\";\n private readonly userAgent: string;\n private lastRequestTime = 0;\n private readonly minRequestInterval = 1000; // 1 second\n\n constructor(\n userAgent: string = \"ElizaOS-MusicInfo/1.0.0 (https://github.com/elizaos/eliza)\",\n ) {\n this.userAgent = userAgent;\n }\n\n /**\n * Rate limit: ensure we wait at least 1 second between requests\n */\n private async rateLimit(): Promise<void> {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n this.lastRequestTime = Date.now();\n }\n\n /**\n * Search for a recording (track) by title and artist\n */\n async searchRecording(\n title: string,\n artist?: string,\n ): Promise<TrackInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n let query = `recording:\"${title}\"`;\n if (artist) {\n query += ` AND artist:\"${artist}\"`;\n }\n\n const url = `${this.baseUrl}/recording?query=${encodeURIComponent(query)}&fmt=json&limit=1`;\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": this.userAgent,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildMusicBrainzHttpError(response);\n }\n\n const data = (await response.json()) as MusicBrainzRecordingResponse;\n const recordings = data.recordings ?? [];\n if (recordings.length === 0) {\n return null;\n }\n\n const recording = recordings[0];\n const trackInfo: TrackInfo = {\n title: recording.title,\n artist:\n recording[\"artist-credit\"]?.[0]?.name || artist || \"Unknown Artist\",\n duration: recording.length\n ? Math.floor(recording.length / 1000)\n : undefined, // Convert ms to seconds\n tags: recording.tags?.map((tag) => tag.name) || [],\n };\n\n // Get release (album) info if available\n if (recording.releases && recording.releases.length > 0) {\n const release = recording.releases[0];\n trackInfo.album = release.title;\n if (release.date) {\n trackInfo.year = parseInt(release.date.substring(0, 4), 10);\n }\n }\n\n return trackInfo;\n }).catch((error) => {\n logger.error(\n `Error fetching MusicBrainz recording after retries: ${error}`,\n );\n throw error;\n });\n }\n\n /**\n * Get artist information by name\n */\n async getArtist(artistName: string): Promise<ArtistInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const url = `${this.baseUrl}/artist?query=artist:\"${encodeURIComponent(artistName)}\"&fmt=json&limit=1`;\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": this.userAgent,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildMusicBrainzHttpError(response);\n }\n\n const data = (await response.json()) as MusicBrainzArtistResponse;\n const artists = data.artists ?? [];\n if (artists.length === 0) {\n return null;\n }\n\n const artist = artists[0];\n const artistInfo: ArtistInfo = {\n name: artist.name,\n genres: artist.tags?.map((tag) => tag.name) || [],\n };\n\n // Get aliases if available\n if (artist.aliases && artist.aliases.length > 0) {\n artistInfo.similarArtists = artist.aliases.map((alias) => alias.name);\n }\n\n return artistInfo;\n }).catch((error) => {\n logger.error(`Error fetching MusicBrainz artist after retries: ${error}`);\n throw error;\n });\n }\n\n /**\n * Get release (album) information\n */\n async getRelease(\n albumTitle: string,\n artistName?: string,\n ): Promise<AlbumInfo | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n let query = `release:\"${albumTitle}\"`;\n if (artistName) {\n query += ` AND artist:\"${artistName}\"`;\n }\n\n const url = `${this.baseUrl}/release?query=${encodeURIComponent(query)}&fmt=json&limit=1`;\n const response = await fetch(url, {\n headers: {\n \"User-Agent\": this.userAgent,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildMusicBrainzHttpError(response);\n }\n\n const data = (await response.json()) as MusicBrainzReleaseResponse;\n const releases = data.releases ?? [];\n if (releases.length === 0) {\n return null;\n }\n\n const release = releases[0];\n const albumInfo: AlbumInfo = {\n title: release.title,\n artist:\n release[\"artist-credit\"]?.[0]?.name || artistName || \"Unknown Artist\",\n genre: release.tags?.map((tag) => tag.name) || [],\n };\n\n if (release.date) {\n albumInfo.year = parseInt(release.date.substring(0, 4), 10);\n }\n\n return albumInfo;\n }).catch((error) => {\n logger.error(\n `Error fetching MusicBrainz release after retries: ${error}`,\n );\n throw error;\n });\n }\n}\n","import { logger } from \"@elizaos/core\";\nimport { type RetryableError, retryWithBackoff } from \"../utils/retry\";\n\ntype TheAudioDbHttpError = Error & RetryableError;\n\ninterface AudioDbArtistSummary {\n idArtist: string;\n strArtist: string;\n strArtistThumb: string;\n strArtistLogo: string;\n strArtistFanart: string;\n strArtistBanner: string;\n}\n\ninterface AudioDbArtistDetail extends AudioDbArtistSummary {\n strBiographyEN?: string;\n intFormedYear?: string;\n strGenre?: string;\n strCountry?: string;\n}\n\ninterface AudioDbAlbumSummary {\n idAlbum: string;\n strAlbum: string;\n strArtist: string;\n strAlbumThumb: string;\n strAlbumCDart: string;\n}\n\ninterface AudioDbAlbumDetail extends AudioDbAlbumSummary {\n intYearReleased?: string;\n strGenre?: string;\n strDescriptionEN?: string;\n}\n\ninterface AudioDbArtistSearchResponse {\n artists?: AudioDbArtistSummary[];\n}\n\ninterface AudioDbArtistDetailResponse {\n artists?: AudioDbArtistDetail[];\n}\n\ninterface AudioDbAlbumSearchResponse {\n album?: AudioDbAlbumSummary[];\n}\n\ninterface AudioDbAlbumDetailResponse {\n album?: AudioDbAlbumDetail[];\n}\n\nfunction buildTheAudioDbHttpError(response: Response): TheAudioDbHttpError {\n const error = new Error(\n `TheAudioDB API error: ${response.status} ${response.statusText}`,\n ) as TheAudioDbHttpError;\n error.response = {\n status: response.status,\n headers: response.headers,\n };\n return error;\n}\n\n/**\n * Client for TheAudioDB API\n * Free tier with API key\n * Rate limit: Generous for free tier\n * Documentation: https://www.theaudiodb.com/api_guide.php\n */\nexport class TheAudioDbClient {\n private readonly baseUrl = \"https://theaudiodb.com/api/v1/json\";\n private readonly apiKey: string;\n private lastRequestTime = 0;\n private readonly minRequestInterval = 100; // 100ms = 10 requests per second (conservative)\n\n constructor(apiKey: string) {\n if (!apiKey) {\n throw new Error(\"TheAudioDB API key is required\");\n }\n this.apiKey = apiKey;\n }\n\n /**\n * Rate limit: ensure we don't exceed rate limits\n */\n private async rateLimit(): Promise<void> {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n this.lastRequestTime = Date.now();\n }\n\n /**\n * Search for an artist\n */\n async searchArtist(artistName: string): Promise<Array<{\n idArtist: string;\n strArtist: string;\n strArtistThumb: string;\n strArtistLogo: string;\n strArtistFanart: string;\n strArtistBanner: string;\n }> | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const url = `${this.baseUrl}/${this.apiKey}/search.php?s=${encodeURIComponent(artistName)}`;\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildTheAudioDbHttpError(response);\n }\n\n const data = (await response.json()) as AudioDbArtistSearchResponse;\n const artists = data.artists ?? [];\n if (artists.length === 0) {\n return null;\n }\n\n return artists;\n }).catch((error) => {\n logger.error(`Error searching TheAudioDB artist after retries: ${error}`);\n return null;\n });\n }\n\n /**\n * Get artist information including high-quality images\n */\n async getArtistInfo(artistName: string): Promise<{\n strArtist: string;\n strArtistThumb: string;\n strArtistLogo: string;\n strArtistFanart: string;\n strArtistBanner: string;\n strBiographyEN?: string;\n intFormedYear?: string;\n strGenre?: string;\n strCountry?: string;\n } | null> {\n try {\n const artists = await this.searchArtist(artistName);\n if (!artists || artists.length === 0) {\n return null;\n }\n\n // Use the first result (most likely match)\n const artist = artists[0];\n\n // Get detailed artist info\n await this.rateLimit();\n const detailUrl = `${this.baseUrl}/${this.apiKey}/artist.php?i=${artist.idArtist}`;\n\n const detailData = await retryWithBackoff(async () => {\n const detailResponse = await fetch(detailUrl, {\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!detailResponse.ok) {\n throw buildTheAudioDbHttpError(detailResponse);\n }\n\n return (await detailResponse.json()) as AudioDbArtistDetailResponse;\n });\n\n const detailArtists = detailData.artists ?? [];\n if (detailArtists.length === 0) {\n return null;\n }\n\n const detailArtist = detailArtists[0];\n return {\n strArtist: detailArtist.strArtist || artist.strArtist,\n strArtistThumb: detailArtist.strArtistThumb || artist.strArtistThumb,\n strArtistLogo: detailArtist.strArtistLogo || artist.strArtistLogo,\n strArtistFanart: detailArtist.strArtistFanart || artist.strArtistFanart,\n strArtistBanner: detailArtist.strArtistBanner || artist.strArtistBanner,\n strBiographyEN: detailArtist.strBiographyEN,\n intFormedYear: detailArtist.intFormedYear,\n strGenre: detailArtist.strGenre,\n strCountry: detailArtist.strCountry,\n };\n } catch (error) {\n logger.error(`Error getting TheAudioDB artist info: ${error}`);\n return null;\n }\n }\n\n /**\n * Search for an album\n */\n async searchAlbum(\n albumName: string,\n artistName?: string,\n ): Promise<Array<{\n idAlbum: string;\n strAlbum: string;\n strArtist: string;\n strAlbumThumb: string;\n strAlbumCDart: string;\n }> | null> {\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n let url: string;\n if (artistName) {\n url = `${this.baseUrl}/${this.apiKey}/searchalbum.php?s=${encodeURIComponent(artistName)}&a=${encodeURIComponent(albumName)}`;\n } else {\n url = `${this.baseUrl}/${this.apiKey}/searchalbum.php?a=${encodeURIComponent(albumName)}`;\n }\n\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw buildTheAudioDbHttpError(response);\n }\n\n const data = (await response.json()) as AudioDbAlbumSearchResponse;\n const albums = data.album ?? [];\n if (albums.length === 0) {\n return null;\n }\n\n return albums;\n }).catch((error) => {\n logger.error(`Error searching TheAudioDB album after retries: ${error}`);\n return null;\n });\n }\n\n /**\n * Get album information including high-quality artwork\n */\n async getAlbumInfo(\n albumName: string,\n artistName?: string,\n ): Promise<{\n strAlbum: string;\n strArtist: string;\n strAlbumThumb: string;\n strAlbumCDart: string;\n intYearReleased?: string;\n strGenre?: string;\n strDescriptionEN?: string;\n } | null> {\n try {\n const albums = await this.searchAlbum(albumName, artistName);\n if (!albums || albums.length === 0) {\n return null;\n }\n\n // Use the first result (most likely match)\n const album = albums[0];\n\n // Get detailed album info\n await this.rateLimit();\n const detailUrl = `${this.baseUrl}/${this.apiKey}/album.php?m=${album.idAlbum}`;\n\n const detailData = await retryWithBackoff(async () => {\n const detailResponse = await fetch(detailUrl, {\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!detailResponse.ok) {\n throw buildTheAudioDbHttpError(detailResponse);\n }\n\n return (await detailResponse.json()) as AudioDbAlbumDetailResponse;\n });\n\n const detailAlbums = detailData.album ?? [];\n if (detailAlbums.length === 0) {\n return null;\n }\n\n const detailAlbum = detailAlbums[0];\n return {\n strAlbum: detailAlbum.strAlbum || album.strAlbum,\n strArtist: detailAlbum.strArtist || album.strArtist,\n strAlbumThumb: detailAlbum.strAlbumThumb || album.strAlbumThumb,\n strAlbumCDart: detailAlbum.strAlbumCDart || album.strAlbumCDart,\n intYearReleased: detailAlbum.intYearReleased,\n strGenre: detailAlbum.strGenre,\n strDescriptionEN: detailAlbum.strDescriptionEN,\n };\n } catch (error) {\n logger.error(`Error getting TheAudioDB album info: ${error}`);\n return null;\n }\n }\n\n /**\n * Validate API key by making a test request\n */\n async validateApiKey(): Promise<boolean> {\n return retryWithBackoff(\n async () => {\n await this.rateLimit();\n // Test with a well-known artist\n const url = `${this.baseUrl}/${this.apiKey}/search.php?s=The Beatles`;\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n },\n });\n return response.ok;\n },\n {\n maxRetries: 2, // Fewer retries for validation\n retryableErrors: (error: RetryableError) => {\n const status = error.response?.status;\n\n // Only retry on network errors, not auth errors\n return (\n error?.code === \"ECONNRESET\" ||\n error?.code === \"ETIMEDOUT\" ||\n error?.code === \"ENOTFOUND\" ||\n (typeof status === \"number\" && status >= 500 && status < 600)\n );\n },\n },\n ).catch(() => false);\n }\n}\n","import { logger } from \"@elizaos/core\";\nimport { Buffer } from \"buffer\";\nimport type {\n AudioFeatures,\n RecommendationRequest,\n TrackRecommendation,\n} from \"../types/audioFeatures\";\nimport { retryWithBackoff } from \"../utils/retry\";\n\ninterface SpotifyToken {\n accessToken: string;\n expiresAt: number;\n}\n\ninterface SpotifyTrack {\n id: string;\n name: string;\n artists: { name: string }[];\n album: { name: string };\n external_urls: { spotify: string };\n preview_url?: string;\n popularity: number;\n}\n\ninterface SpotifyAudioFeatures {\n id: string;\n danceability: number;\n energy: number;\n key: number;\n loudness: number;\n mode: number;\n speechiness: number;\n acousticness: number;\n instrumentalness: number;\n liveness: number;\n valence: number;\n tempo: number;\n duration_ms: number;\n time_signature: number;\n}\n\n/**\n * Client for Spotify Web API\n * Provides access to audio features and track recommendations\n */\nexport class SpotifyClient {\n private clientId: string | null = null;\n private clientSecret: string | null = null;\n private token: SpotifyToken | null = null;\n private baseUrl = \"https://api.spotify.com/v1\";\n private authUrl = \"https://accounts.spotify.com/api/token\";\n\n // Rate limiting: 180 requests per minute for web API\n private lastRequestTime = 0;\n private minRequestInterval = 334; // ~180 requests per minute\n\n constructor(clientId?: string, clientSecret?: string) {\n this.clientId = clientId || null;\n this.clientSecret = clientSecret || null;\n }\n\n /**\n * Check if API credentials are configured\n */\n isConfigured(): boolean {\n return !!(this.clientId && this.clientSecret);\n }\n\n /**\n * Get or refresh access token\n */\n private async getAccessToken(): Promise<string | null> {\n if (!this.clientId || !this.clientSecret) {\n return null;\n }\n\n // Check if current token is still valid\n if (this.token && this.token.expiresAt > Date.now()) {\n return this.token.accessToken;\n }\n\n try {\n const response = await fetch(this.authUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Authorization: `Basic ${Buffer.from(`${this.clientId}:${this.clientSecret}`).toString(\"base64\")}`,\n },\n body: \"grant_type=client_credentials\",\n });\n\n if (!response.ok) {\n logger.warn(\n `Failed to get Spotify access token: ${response.status} ${response.statusText}`,\n );\n return null;\n }\n\n const data = (await response.json()) as {\n access_token: string;\n expires_in: number;\n };\n\n this.token = {\n accessToken: data.access_token,\n expiresAt: Date.now() + data.expires_in * 1000 - 60000, // Refresh 1 minute early\n };\n\n return this.token.accessToken;\n } catch (error) {\n logger.error(`Error getting Spotify access token: ${error}`);\n return null;\n }\n }\n\n /**\n * Rate limiting helper\n */\n private async rateLimit(): Promise<void> {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n\n this.lastRequestTime = Date.now();\n }\n\n /**\n * Search for a track on Spotify\n */\n async searchTrack(query: string): Promise<string | null> {\n if (!this.isConfigured()) {\n return null;\n }\n\n const accessToken = await this.getAccessToken();\n if (!accessToken) {\n return null;\n }\n\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const encodedQuery = encodeURIComponent(query);\n const response = await fetch(\n `${this.baseUrl}/search?q=${encodedQuery}&type=track&limit=1`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired, clear it\n this.token = null;\n throw new Error(\"Spotify token expired\");\n }\n throw new Error(\n `Spotify search failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = (await response.json()) as {\n tracks: { items: SpotifyTrack[] };\n };\n\n if (data.tracks.items.length === 0) {\n return null;\n }\n\n return data.tracks.items[0].id;\n });\n }\n\n /**\n * Get audio features for a track\n */\n async getAudioFeatures(trackId: string): Promise<AudioFeatures | null> {\n if (!this.isConfigured()) {\n return null;\n }\n\n const accessToken = await this.getAccessToken();\n if (!accessToken) {\n return null;\n }\n\n await this.rateLimit();\n\n return retryWithBackoff(async () => {\n const response = await fetch(\n `${this.baseUrl}/audio-features/${trackId}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n this.token = null;\n throw new Error(\"Spotify token expired\");\n }\n if (response.status === 404) {\n return null;\n }\n throw new Error(\n `Spotify audio features request failed: ${response.status}`,\n );\n }\n\n const data = (await response.json()) as SpotifyAudioFeatures;\n\n return {\n trackId: data.id,\n danceability: data.danceability,\n energy: data.energy,\n valence: data.valence,\n acousticness: data.acousticness,\n instrumentalness: data.instrumentalness,\n liveness: data.liveness,\n speechiness: data.speechiness,\n key: data.key,\n mode: data.mode,\n tempo: data.tempo,\n timeSignature: data.time_signature,\n loudness: data.loudness,\n duration: data.duration_ms,\n source: \"spotify\",\n };\n });\n }\n\n /**\n * Get audio features for a track by search query\n */\n async getAudioFeaturesByQuery(query: string): Promise<AudioFeatures | null> {\n const trackId = await this.searchTrack(query);\n if (!trackId) {\n return null;\n }\n\n return this.getAudioFeatures(trackId);\n }\n\n /**\n * Get track recommendations\n */\n async getRecommendations(\n request: RecommendationRequest,\n ): Promise<TrackRecommendation[]> {\n if (!this.isConfigured()) {\n return [];\n }\n\n const accessToken = await this.getAccessToken();\n if (!accessToken) {\n return [];\n }\n\n // Build query parameters\n const params = new URLSearchParams();\n\n // Seeds (combined must be <= 5)\n if (request.seedArtists && request.seedArtists.length > 0) {\n params.append(\"seed_artists\", request.seedArtists.slice(0, 5).join(\",\"));\n }\n if (request.seedTracks && request.seedTracks.length > 0) {\n params.append(\"seed_tracks\", request.seedTracks.slice(0, 5).join(\",\"));\n }\n if (request.seedGenres && request.seedGenres.length > 0) {\n params.append(\"seed_genres\", request.seedGenres.slice(0, 5).join(\",\"));\n }\n\n // Audio feature targets\n if (request.audioFeatures) {\n const features = request.audioFeatures;\n if (features.targetDanceability !== undefined) {\n params.append(\n \"target_danceability\",\n features.targetDanceability.toString(),\n );\n }\n if (features.targetEnergy !== undefined) {\n params.append(\"target_energy\", features.targetEnergy.toString());\n }\n if (features.targetValence !== undefined) {\n params.append(\"target_valence\", features.targetValence.toString());\n }\n if (features.targetTempo !== undefined) {\n params.append(\"target_tempo\", features.targetTempo.toString());\n }\n if (features.targetLoudness !== undefined) {\n params.append(\"target_loudness\", features.targetLoudness.toString());\n }\n if (features.targetAcousticness !== undefined) {\n params.append(\n \"target_acousticness\",\n features.targetAcousticness.toString(),\n );\n }\n if (features.targetInstrumentalness !== undefined) {\n params.append(\n \"target_instrumentalness\",\n features.targetInstrumentalness.toString(),\n );\n }\n if (features.targetPopularity !== undefined) {\n params.append(\n \"target_popularity\",\n features.targetPopularity.toString(),\n );\n }\n }\n\n // Limit\n params.append(\"limit\", (request.limit || 20).toString());\n\n await this.rateLimit();\n\n return retryWithBackoff(\n async () => {\n const response = await fetch(\n `${this.baseUrl}/recommendations?${params.toString()}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n },\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n this.token = null;\n throw new Error(\"Spotify token expired\");\n }\n throw new Error(\n `Spotify recommendations request failed: ${response.status}`,\n );\n }\n\n const data = (await response.json()) as { tracks: SpotifyTrack[] };\n\n return data.tracks.map((track) => ({\n trackName: track.name,\n artistName: track.artists.map((a) => a.name).join(\", \"),\n albumName: track.album.name,\n url: track.external_urls.spotify,\n previewUrl: track.preview_url,\n popularity: track.popularity,\n }));\n },\n {\n maxRetries: 2, // Fewer retries for recommendations\n },\n );\n }\n\n /**\n * Validate API credentials\n */\n async validateCredentials(): Promise<boolean> {\n if (!this.isConfigured()) {\n return false;\n }\n\n const token = await this.getAccessToken();\n return token !== null;\n }\n}\n","import { logger } from \"@elizaos/core\";\nimport type { AlbumInfo, ArtistInfo, TrackInfo } from \"../types\";\n\nconst WIKIPEDIA_SERVICE_NAME = \"wikipedia\";\n\ninterface WikipediaSummaryPage {\n extract?: string;\n thumbnail?: {\n source?: string;\n };\n content_urls?: {\n desktop?: {\n page?: string;\n };\n };\n}\n\ninterface WikipediaSearchEntry {\n title: string;\n snippet: string;\n}\n\ninterface WikipediaSearchResponse {\n query?: {\n search?: WikipediaSearchEntry[];\n };\n}\n\n/**\n * Shared rate limiter for Wikipedia API across all agents per instance\n * Wikipedia recommends: max 200 requests per second per IP\n * We'll be more conservative: 2 requests per second (120 per minute)\n */\nclass WikipediaRateLimiter {\n private static instance: WikipediaRateLimiter | null = null;\n private lastRequestTime = 0;\n private readonly minRequestInterval = 500; // 500ms = 2 requests per second\n private requestQueue: Array<() => void> = [];\n private processing = false;\n\n static getInstance(): WikipediaRateLimiter {\n if (!WikipediaRateLimiter.instance) {\n WikipediaRateLimiter.instance = new WikipediaRateLimiter();\n }\n return WikipediaRateLimiter.instance;\n }\n\n /**\n * Wait for rate limit before making a request\n */\n async waitForRateLimit(): Promise<void> {\n return new Promise((resolve) => {\n this.requestQueue.push(resolve);\n this.processQueue();\n });\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.requestQueue.length === 0) {\n return;\n }\n\n this.processing = true;\n\n while (this.requestQueue.length > 0) {\n const now = Date.now();\n const timeSinceLastRequest = now - this.lastRequestTime;\n\n if (timeSinceLastRequest < this.minRequestInterval) {\n await new Promise((resolve) =>\n setTimeout(resolve, this.minRequestInterval - timeSinceLastRequest),\n );\n }\n\n this.lastRequestTime = Date.now();\n const resolve = this.requestQueue.shift();\n if (resolve) {\n resolve();\n }\n }\n\n this.processing = false;\n }\n}\n\n/**\n * Service for Wikipedia API access\n * Free, no authentication required\n * Rate limit: 2 requests per second (shared across all agents per instance)\n */\nexport class WikipediaClient {\n capabilityDescription = \"Provides access to Wikipedia API with rate limiting\";\n\n private readonly baseUrl = \"https://en.wikipedia.org/api/rest_v1\";\n private readonly searchUrl = \"https://en.wikipedia.org/w/api.php\";\n private readonly rateLimiter = WikipediaRateLimiter.getInstance();\n\n async stop(): Promise<void> {\n // No cleanup needed - rate limiter is a singleton\n }\n\n /**\n * Search for a Wikipedia page and get summary\n */\n private async getPageSummary(\n title: string,\n ): Promise<WikipediaSummaryPage | null> {\n await this.rateLimiter.waitForRateLimit();\n\n try {\n const encodedTitle = encodeURIComponent(title);\n const url = `${this.baseUrl}/page/summary/${encodedTitle}`;\n\n const response = await fetch(url, {\n headers: {\n Accept: \"application/json\",\n \"User-Agent\":\n \"ElizaOS-MusicInfo/1.0.0 (https://github.com/elizaos/eliza)\",\n },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null; // Page not found\n }\n logger.warn(\n `Wikipedia API error: ${response.status} ${response.statusText}`,\n );\n return null;\n }\n\n return (await response.json()) as WikipediaSummaryPage;\n } catch (error) {\n logger.error(`Error fetching Wikipedia page: ${error}`);\n return null;\n }\n }\n\n /**\n * Search Wikipedia for pages matching a query\n */\n private async searchPages(\n query: string,\n limit: number = 3,\n ): Promise<Array<{ title: string; snippet: string }>> {\n await this.rateLimiter.waitForRateLimit();\n\n try {\n const params = new URLSearchParams({\n action: \"query\",\n list: \"search\",\n srsearch: query,\n srlimit: limit.toString(),\n format: \"json\",\n origin: \"*\",\n });\n\n const url = `${this.searchUrl}?${params.toString()}`;\n const response = await fetch(url, {\n headers: {\n \"User-Agent\":\n \"ElizaOS-MusicInfo/1.0.0 (https://github.com/elizaos/eliza)\",\n },\n });\n\n if (!response.ok) {\n return [];\n }\n\n const data = (await response.json()) as WikipediaSearchResponse;\n const searchResults = data.query?.search;\n if (!searchResults) {\n return [];\n }\n\n return searchResults.map((result) => ({\n title: result.title,\n snippet: result.snippet,\n }));\n } catch (error) {\n logger.error(`Error searching Wikipedia: ${error}`);\n return [];\n }\n }\n\n /**\n * Get track information from Wikipedia\n */\n async getTrackInfo(\n trackName: string,\n artistName?: string,\n ): Promise<TrackInfo | null> {\n try {\n // Try searching for the song\n let searchQuery = trackName;\n if (artistName) {\n searchQuery = `${trackName} ${artistName}`;\n }\n\n const searchResults = await this.searchPages(searchQuery, 5);\n\n // Look for a result that seems to be about the song\n for (const result of searchResults) {\n const title = result.title.toLowerCase();\n const snippet = result.snippet.toLowerCase();\n const trackLower = trackName.toLowerCase();\n\n // Check if this looks like a song page\n if (title.includes(trackLower) || snippet.includes(trackLower)) {\n const page = await this.getPageSummary(result.title);\n if (page?.extract) {\n const trackInfo: TrackInfo = {\n title: trackName,\n artist:\n artistName ||\n this.extractArtistFromExtract(page.extract) ||\n \"Unknown Artist\",\n description: this.cleanExtract(page.extract),\n url: page.content_urls?.desktop?.page,\n };\n\n // Try to extract additional info from extract\n const yearMatch = page.extract.match(/\\b(19|20)\\d{2}\\b/);\n if (yearMatch) {\n trackInfo.year = parseInt(yearMatch[0], 10);\n }\n\n return trackInfo;\n }\n }\n }\n\n return null;\n } catch (error) {\n logger.error(`Error getting Wikipedia track info: ${error}`);\n return null;\n }\n }\n\n /**\n * Get artist information from Wikipedia\n */\n async getArtistInfo(artistName: string): Promise<ArtistInfo | null> {\n try {\n // Try direct page lookup first\n let page = await this.getPageSummary(artistName);\n\n // If not found, try searching\n if (!page) {\n const searchResults = await this.searchPages(artistName, 3);\n if (searchResults.length > 0) {\n // Try the first result\n page = await this.getPageSummary(searchResults[0].title);\n }\n }\n\n if (!page?.extract) {\n return null;\n }\n\n const extract = page.extract;\n const artistInfo: ArtistInfo = {\n name: artistName,\n bio: this.cleanExtract(extract),\n image: page.thumbnail?.source,\n };\n\n // Try to extract genres from the extract\n const genreKeywords = [\n \"rock\",\n \"pop\",\n \"jazz\",\n \"hip hop\",\n \"rap\",\n \"country\",\n \"electronic\",\n \"classical\",\n \"blues\",\n \"folk\",\n \"metal\",\n \"punk\",\n \"reggae\",\n \"r&b\",\n \"soul\",\n \"funk\",\n \"disco\",\n \"indie\",\n \"alternative\",\n ];\n const foundGenres: string[] = [];\n const extractLower = extract.toLowerCase();\n\n for (const genre of genreKeywords) {\n if (extractLower.includes(genre)) {\n foundGenres.push(genre);\n }\n }\n\n if (foundGenres.length > 0) {\n artistInfo.genres = foundGenres.slice(0, 5); // Limit to 5 genres\n }\n\n // Extract related artists and influences from Wikipedia extract\n // This helps with music discovery and selection\n const relatedArtists = this.extractRelatedArtists(extract);\n if (relatedArtists.length > 0) {\n artistInfo.similarArtists = relatedArtists;\n }\n\n return artistInfo;\n } catch (error) {\n logger.error(`Error getting Wikipedia artist info: ${error}`);\n return null;\n }\n }\n\n /**\n * Get album information from Wikipedia\n */\n async getAlbumInfo(\n albumTitle: string,\n artistName?: string,\n ): Promise<AlbumInfo | null> {\n try {\n // Try searching for the album\n let searchQuery = albumTitle;\n if (artistName) {\n searchQuery = `${albumTitle} ${artistName}`;\n }\n\n const searchResults = await this.searchPages(searchQuery, 5);\n\n // Look for a result that seems to be about the album\n for (const result of searchResults) {\n const title = result.title.toLowerCase();\n const snippet = result.snippet.toLowerCase();\n const albumLower = albumTitle.toLowerCase();\n\n // Check if this looks like an album page\n if (title.includes(albumLower) || snippet.includes(albumLower)) {\n const page = await this.getPageSummary(result.title);\n if (page?.extract) {\n const albumInfo: AlbumInfo = {\n title: albumTitle,\n artist:\n artistName ||\n this.extractArtistFromExtract(page.extract) ||\n \"Unknown Artist\",\n description: this.cleanExtract(page.extract),\n coverArt: page.thumbnail?.source,\n };\n\n // Try to extract year from extract\n const yearMatch = page.extract.match(/\\b(19|20)\\d{2}\\b/);\n if (yearMatch) {\n albumInfo.year = parseInt(yearMatch[0], 10);\n }\n\n return albumInfo;\n }\n }\n }\n\n return null;\n } catch (error) {\n logger.error(`Error getting Wikipedia album info: ${error}`);\n return null;\n }\n }\n\n /**\n * Clean Wikipedia extract text (remove HTML, truncate)\n */\n private cleanExtract(extract: string, maxLength: number = 1000): string {\n // Remove HTML tags\n let cleaned = extract.replace(/<[^>]*>/g, \"\");\n // Remove reference markers like [1], [2], etc.\n cleaned = cleaned.replace(/\\[\\d+\\]/g, \"\");\n // Truncate if too long\n if (cleaned.length > maxLength) {\n cleaned = `${cleaned.substring(0, maxLength).trim()}...`;\n }\n return cleaned;\n }\n\n /**\n * Try to extract artist name from Wikipedia extract\n */\n private extractArtistFromExtract(extract: string): string | null {\n // Look for common patterns like \"by Artist\" or \"Artist's song\"\n const patterns = [\n /\\bby\\s+([A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*)\\b/,\n /([A-Z][a-z]+(?:\\s+[A-Z][a-z]+)*)'s\\s+(?:song|album|track)/i,\n ];\n\n for (const pattern of patterns) {\n const match = extract.match(pattern);\n if (match?.[1]) {\n return match[1].trim();\n }\n }\n\n return null;\n }\n\n /**\n * Extract related artists, influences, and similar acts from Wikipedia extract\n * Looks for patterns like \"influenced by\", \"similar to\", \"associated acts\", etc.\n * This data helps drive intelligent music selection and discovery\n */\n private extractRelatedArtists(extract: string): string[] {\n const relatedArtists: Set<string> = new Set();\n\n // Patterns to find related artists\n const patterns = [\n // Influences\n /(?:influenced by|inspired by|drew inspiration from)[:\\s]+([^.]+)/i,\n // Similar artists\n /(?:similar to|comparable to|like)[:\\s]+([^.]+)/i,\n // Associated acts\n /(?:associated acts|associated with|collaborated with)[:\\s]+([^.]+)/i,\n // Musical influences\n /(?:musical influences|influences include)[:\\s]+([^.]+)/i,\n // Genre peers\n /(?:alongside|along with|together with)[:\\s]+([^.]+)/i,\n ];\n\n for (const pattern of patterns) {\n const matches = extract.matchAll(new RegExp(pattern.source, \"gi\"));\n for (const match of matches) {\n if (match[1]) {\n // Extract artist names from the matched text\n const artistsText = match[1];\n // Split by common separators\n const artists = artistsText\n .split(/[,;]| and | & /)\n .map((a) => a.trim())\n .filter((a) => a.length > 0 && a.length < 100); // Reasonable length\n\n for (const artist of artists) {\n // Clean up the artist name\n const cleanArtist = artist\n .replace(/\\[.*?\\]/g, \"\") // Remove Wikipedia references\n .replace(/\\(.*?\\)/g, \"\") // Remove parenthetical info\n .trim();\n\n // Only add if it looks like an artist name (starts with capital, reasonable length)\n if (\n cleanArtist.length > 2 &&\n cleanArtist.length < 80 &&\n /^[A-Z]/.test(cleanArtist)\n ) {\n relatedArtists.add(cleanArtist);\n }\n }\n }\n }\n }\n\n // Limit to top 10 related artists\n return Array.from(relatedArtists).slice(0, 10);\n }\n}\n\nexport const WIKIPEDIA_HELPER_NAME = WIKIPEDIA_SERVICE_NAME;\n\nexport { WikipediaClient as WikipediaService };\n","import { type IAgentRuntime, logger, ModelType } from \"@elizaos/core\";\nimport type { AlbumInfo, ArtistInfo, TrackInfo } from \"../types\";\nimport { parseJsonObjectResponse } from \"../utils/json\";\nimport type { WikipediaClient } from \"./wikipediaClient\";\n\nconst WIKIPEDIA_EXTRACTION_SERVICE_NAME = \"wikipediaExtraction\";\n\nexport interface WikipediaExtractionContext {\n purpose: \"dj_intro\" | \"music_selection\" | \"general_info\" | \"related_artists\";\n currentArtist?: string;\n currentTrack?: string;\n currentAlbum?: string;\n}\n\nexport interface ExtractedMusicInfo {\n artist?: Partial<ArtistInfo>;\n track?: Partial<TrackInfo>;\n album?: Partial<AlbumInfo>;\n relatedArtists?: string[];\n influences?: string[];\n genres?: string[];\n interestingFacts?: string[];\n selectionSuggestions?: string[];\n}\n\ntype WikipediaExtractionSourceData =\n | {\n type: \"artist\";\n name: string;\n bio?: string;\n genres?: string[];\n similarArtists?: string[];\n image?: string;\n }\n | {\n type: \"song\";\n name: string;\n description?: string;\n artist?: string;\n album?: string;\n year?: number;\n genre?: string[];\n }\n | {\n type: \"album\";\n name: string;\n description?: string;\n artist?: string;\n year?: number;\n genre?: string[];\n };\n\nfunction formatPromptValue(value: unknown, depth = 0): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value.replace(/\\s+/g, \" \").trim();\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n return value\n .filter((item) => item != null)\n .slice(0, 12)\n .map((item) => {\n const rendered = formatPromptValue(item, depth + 1);\n return rendered ? `${\" \".repeat(depth)}- ${rendered}` : \"\";\n })\n .filter(Boolean)\n .join(\"\\n\");\n }\n if (typeof value === \"object\") {\n return Object.entries(value as Record<string, unknown>)\n .filter(([, entry]) => entry != null && entry !== \"\")\n .slice(0, 16)\n .map(([key, entry]) => {\n const rendered = formatPromptValue(entry, depth + 1);\n if (!rendered) return \"\";\n if (rendered.includes(\"\\n\")) {\n return `${\" \".repeat(depth)}${key}:\\n${rendered}`;\n }\n return `${\" \".repeat(depth)}${key}: ${rendered}`;\n })\n .filter(Boolean)\n .join(\"\\n\");\n }\n return String(value);\n}\n\nfunction formatWikipediaDataForPrompt(\n wikiData: WikipediaExtractionSourceData,\n): string {\n return formatPromptValue(wikiData);\n}\n\nfunction toStringList(value: unknown): string[] | undefined {\n if (Array.isArray(value)) {\n const items = value\n .map((item) => String(item).trim())\n .filter(Boolean)\n .slice(0, 10);\n return items.length > 0 ? items : undefined;\n }\n if (typeof value === \"string\") {\n const items = value\n .split(/[,;]|\\n| and | & /)\n .map((item) => item.trim())\n .filter(Boolean)\n .slice(0, 10);\n return items.length > 0 ? items : undefined;\n }\n return undefined;\n}\n\n/**\n * Service that uses LLMs to dynamically extract relevant information from Wikipedia\n * Based on context (e.g., DJ intro, music selection), extracts different information\n */\nexport class WikipediaExtractionHelper {\n capabilityDescription =\n \"Uses LLM to dynamically extract music information from Wikipedia based on context\";\n\n private cache: Map<string, { data: ExtractedMusicInfo; timestamp: number }> =\n new Map();\n private readonly CACHE_TTL = 3600000; // 1 hour in milliseconds\n private readonly runtime?: IAgentRuntime;\n private readonly wikipediaClient: WikipediaClient | null;\n\n constructor(\n runtime?: IAgentRuntime,\n wikipediaClient?: WikipediaClient | null,\n ) {\n this.runtime = runtime;\n this.wikipediaClient = wikipediaClient ?? null;\n }\n\n private getWikipediaService(): WikipediaClient | null {\n return this.wikipediaClient;\n }\n\n async stop(): Promise<void> {\n this.clearCache();\n }\n\n /**\n * Extract music information from Wikipedia using LLM based on context\n */\n async extractFromWikipedia(\n entityName: string,\n entityType: \"artist\" | \"album\" | \"song\",\n context: WikipediaExtractionContext,\n ): Promise<ExtractedMusicInfo | null> {\n if (!this.runtime) {\n return null;\n }\n\n // Create cache key\n const cacheKey = `${entityType}:${entityName}:${context.purpose}`;\n const cached = this.cache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {\n return cached.data;\n }\n\n try {\n // Fetch full Wikipedia page\n const wikipediaService = this.getWikipediaService();\n if (!wikipediaService) {\n return null;\n }\n\n let wikiData: WikipediaExtractionSourceData | null = null;\n if (entityType === \"artist\") {\n const artistInfo = await wikipediaService.getArtistInfo(entityName);\n if (artistInfo) {\n wikiData = {\n type: \"artist\",\n name: entityName,\n bio: artistInfo.bio,\n genres: artistInfo.genres,\n similarArtists: artistInfo.similarArtists,\n image: artistInfo.image,\n };\n }\n } else if (entityType === \"song\") {\n const trackInfo = await wikipediaService.getTrackInfo(entityName);\n if (trackInfo) {\n wikiData = {\n type: \"song\",\n name: entityName,\n description: trackInfo.description,\n artist: trackInfo.artist,\n album: trackInfo.album,\n year: trackInfo.year,\n genre: trackInfo.genre,\n };\n }\n } else if (entityType === \"album\") {\n // Would need artist name for albums\n const albumInfo = await wikipediaService.getAlbumInfo(\n entityName,\n context.currentArtist,\n );\n if (albumInfo) {\n wikiData = {\n type: \"album\",\n name: entityName,\n description: albumInfo.description,\n artist: albumInfo.artist,\n year: albumInfo.year,\n genre: albumInfo.genre,\n };\n }\n }\n\n if (!wikiData) {\n return null;\n }\n\n // Use LLM to extract relevant information based on context\n const extractionPrompt = this.buildExtractionPrompt(wikiData, context);\n const extractionResponse = await this.runtime.useModel(\n ModelType.TEXT_LARGE,\n {\n prompt: extractionPrompt,\n maxTokens: 500,\n },\n );\n\n // Parse LLM response\n const extracted = this.parseExtractionResponse(\n extractionResponse as string,\n context,\n );\n\n // Cache result\n this.cache.set(cacheKey, {\n data: extracted,\n timestamp: Date.now(),\n });\n\n return extracted;\n } catch (error) {\n logger.error(`Error extracting Wikipedia info: ${error}`);\n return null;\n }\n }\n\n /**\n * Build extraction prompt based on context\n */\n private buildExtractionPrompt(\n wikiData: WikipediaExtractionSourceData,\n context: WikipediaExtractionContext,\n ): string {\n const basePrompt = `Extract relevant music information from the following Wikipedia data based on the context.\n\nWikipedia data:\n${formatWikipediaDataForPrompt(wikiData)}\n\nContext: ${context.purpose}\n${context.currentArtist ? `Current Artist: ${context.currentArtist}` : \"\"}\n${context.currentTrack ? `Current Track: ${context.currentTrack}` : \"\"}\n${context.currentAlbum ? `Current Album: ${context.currentAlbum}` : \"\"}\n\n`;\n\n switch (context.purpose) {\n case \"dj_intro\":\n return (\n basePrompt +\n `Extract information that would be interesting for a radio DJ introduction:\n- Interesting facts and trivia about the artist/song (prioritize fun, surprising, or noteworthy facts)\n- Genre and style information\n- Related artists or influences\n- Release year or historical context\n- Any notable achievements, awards, or interesting backstories\n- Fun anecdotes or stories about the song/artist\n- Chart positions or commercial success (if notable)\n- Cultural impact or significance\n\nReturn JSON with this shape:\n{\n \"interestingFacts\": [\"most interesting fact\"],\n \"genres\": [\"genre\"],\n \"relatedArtists\": [\"artist\"],\n \"influences\": [\"influence\"],\n \"year\": 2000\n}`\n );\n\n case \"music_selection\":\n return (\n basePrompt +\n `Extract information that would help with intelligent music selection:\n- Related artists and influences (for discovering similar music)\n- Genre information\n- Musical style characteristics\n- Artists that influenced this artist\n- Artists that were influenced by this artist\n\nReturn JSON with this shape:\n{\n \"relatedArtists\": [\"artist\"],\n \"influences\": [\"influence\"],\n \"genres\": [\"genre\"],\n \"selectionSuggestions\": [\"artist or song name\"]\n}`\n );\n\n case \"related_artists\":\n return (\n basePrompt +\n `Extract all related artists, influences, and similar acts:\n- Artists that influenced this artist\n- Artists influenced by this artist\n- Similar artists or genre peers\n- Associated acts or collaborators\n\nReturn JSON with this shape:\n{\n \"influences\": [\"influence\"],\n \"relatedArtists\": [\"artist\"],\n \"similarArtists\": [\"similar artist\"]\n}`\n );\n\n default:\n return (\n basePrompt +\n `Extract general music information:\n- Genre and style\n- Related artists\n- Influences\n- Interesting facts\n\nReturn JSON with this shape:\n{\n \"genres\": [\"genre\"],\n \"relatedArtists\": [\"artist\"],\n \"influences\": [\"influence\"],\n \"interestingFacts\": [\"fact\"]\n}`\n );\n }\n }\n\n /**\n * Parse LLM extraction response\n */\n private parseExtractionResponse(\n response: string,\n _context: WikipediaExtractionContext,\n ): ExtractedMusicInfo {\n const extracted: ExtractedMusicInfo = {};\n\n try {\n const parsedJson =\n parseJsonObjectResponse<Record<string, unknown>>(response);\n if (parsedJson) {\n extracted.relatedArtists =\n toStringList(parsedJson.relatedArtists) ||\n toStringList(parsedJson.similarArtists);\n extracted.influences = toStringList(parsedJson.influences);\n extracted.genres = toStringList(parsedJson.genres);\n extracted.interestingFacts = toStringList(parsedJson.interestingFacts);\n extracted.selectionSuggestions = toStringList(\n parsedJson.selectionSuggestions,\n );\n return extracted;\n }\n\n // Fallback: try to extract lists from straight text.\n extracted.relatedArtists = this.extractList(\n response,\n /related[:\\s]+(.*?)(?:\\n|$)/i,\n );\n extracted.influences = this.extractList(\n response,\n /influenc[es]*[:\\s]+(.*?)(?:\\n|$)/i,\n );\n extracted.genres = this.extractList(\n response,\n /genre[s]*[:\\s]+(.*?)(?:\\n|$)/i,\n );\n extracted.interestingFacts = this.extractList(\n response,\n /fact[s]*[:\\s]+(.*?)(?:\\n|$)/i,\n );\n } catch (error) {\n logger.warn(`Failed to parse extraction response: ${error}`);\n }\n\n return extracted;\n }\n\n /**\n * Extract list items from text using pattern\n */\n private extractList(text: string, pattern: RegExp): string[] {\n const match = text.match(pattern);\n if (!match?.[1]) {\n return [];\n }\n\n return match[1]\n .split(/[,;]| and | & /)\n .map((item) => item.trim())\n .filter((item) => item.length > 0)\n .slice(0, 10); // Limit to 10 items\n }\n\n /**\n * Clear cache\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Clear expired cache entries\n */\n clearExpiredCache(): void {\n const now = Date.now();\n for (const [key, value] of this.cache.entries()) {\n if (now - value.timestamp >= this.CACHE_TTL) {\n this.cache.delete(key);\n }\n }\n }\n}\n\nexport const WIKIPEDIA_EXTRACTION_HELPER_NAME =\n WIKIPEDIA_EXTRACTION_SERVICE_NAME;\n\nexport { WikipediaExtractionHelper as WikipediaExtractionService };\n","import { logger } from \"@elizaos/core\";\n\nconst YOUTUBE_SEARCH_SERVICE_NAME = \"youtubeSearch\";\n\ninterface PlayDlSearchResult {\n url?: string;\n title?: string;\n durationInSec?: number;\n channel?: {\n name?: string;\n };\n views?: number;\n}\n\nexport interface YouTubeSearchResult {\n url: string;\n title: string;\n duration?: number;\n channel?: string;\n views?: number;\n}\n\n/**\n * Service for searching YouTube videos\n * Centralizes YouTube search logic for reuse across multiple actions\n */\nexport class YouTubeSearchHelper {\n capabilityDescription = \"Searches YouTube for videos and returns metadata\";\n\n private cache: Map<\n string,\n { results: YouTubeSearchResult[]; timestamp: number }\n > = new Map();\n private readonly CACHE_TTL = 3600000; // 1 hour\n\n async stop(): Promise<void> {\n this.clearCache();\n }\n\n /**\n * Clear all cached searches\n */\n clearCache(): void {\n this.cache.clear();\n }\n\n /**\n * Search YouTube for videos\n * @param query - Search query string\n * @param options - Search options\n * @returns Array of search results\n */\n async search(\n query: string,\n options: {\n limit?: number;\n includeShorts?: boolean;\n } = {},\n ): Promise<YouTubeSearchResult[]> {\n const { limit = 5, includeShorts = false } = options;\n\n // Check cache\n const cacheKey = `${query}:${limit}:${includeShorts}`;\n const cached = this.cache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {\n logger.debug(`YouTube search cache hit for: ${query}`);\n return cached.results;\n }\n\n try {\n // Lazy-load play-dl to avoid initialization issues during plugin load\n const play = await import(\"@vookav2/play-dl\").then((m) => m.default || m);\n\n logger.debug(`Searching YouTube for: ${query} (limit: ${limit})`);\n\n let searchResults: PlayDlSearchResult[];\n try {\n searchResults = await play.search(query, {\n limit: limit * 2, // Get extra results for filtering\n source: { youtube: \"video\" },\n });\n } catch (error) {\n logger.error(\n \"Error in YouTube search API:\",\n error instanceof Error ? error.message : String(error),\n );\n throw new Error(\n `YouTube search failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n\n if (!searchResults || searchResults.length === 0) {\n logger.warn(`No YouTube results found for: ${query}`);\n return [];\n }\n\n // Filter and format results\n const results: YouTubeSearchResult[] = [];\n for (const result of searchResults) {\n // Skip if not a valid video URL\n if (!result.url?.includes(\"youtube.com/watch\")) {\n continue;\n }\n\n // Skip shorts if not included\n if (!includeShorts && result.url.includes(\"/shorts/\")) {\n continue;\n }\n\n // Add to results\n const channelName = result.channel?.name;\n const views =\n typeof result.views === \"number\" ? result.views : undefined;\n results.push({\n url: result.url,\n title: result.title || \"Unknown Title\",\n duration:\n typeof result.durationInSec === \"number\"\n ? result.durationInSec\n : undefined,\n channel: typeof channelName === \"string\" ? channelName : undefined,\n views,\n });\n\n // Stop when we have enough results\n if (results.length >= limit) {\n break;\n }\n }\n\n // Cache results\n this.cache.set(cacheKey, {\n results,\n timestamp: Date.now(),\n });\n\n logger.info(`Found ${results.length} YouTube results for: ${query}`);\n return results;\n } catch (error) {\n logger.error(\n \"Error in YouTubeSearchService:\",\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n }\n }\n\n /**\n * Search and return the top result\n * @param query - Search query\n * @returns Top search result or null\n */\n async searchOne(query: string): Promise<YouTubeSearchResult | null> {\n const results = await this.search(query, { limit: 1 });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Validate if a URL is a valid YouTube video\n */\n async validateUrl(url: string): Promise<boolean> {\n try {\n const play = await import(\"@vookav2/play-dl\").then((m) => m.default || m);\n return play.yt_validate(url) === \"video\";\n } catch (error) {\n logger.error(\n \"Error validating YouTube URL:\",\n error instanceof Error ? error.message : String(error),\n );\n return false;\n }\n }\n\n /**\n * Get video info from URL\n */\n async getVideoInfo(url: string): Promise<YouTubeSearchResult | null> {\n try {\n const play = await import(\"@vookav2/play-dl\").then((m) => m.default || m);\n\n if (!play.yt_validate(url)) {\n return null;\n }\n\n const videoInfo = await play.video_info(url);\n if (!videoInfo) {\n return null;\n }\n\n const details = videoInfo.video_details;\n const channelName = details.channel?.name;\n return {\n url: details.url,\n title: details.title || \"Unknown Title\",\n duration: details.durationInSec,\n channel: typeof channelName === \"string\" ? channelName : undefined,\n views: details.views,\n };\n } catch (error) {\n logger.error(\n \"Error getting video info:\",\n error instanceof Error ? error.message : String(error),\n );\n return null;\n }\n }\n}\n\nexport const YOUTUBE_SEARCH_HELPER_NAME = YOUTUBE_SEARCH_SERVICE_NAME;\n\nexport { YouTubeSearchHelper as YouTubeSearchService };\n\nexport default YouTubeSearchHelper;\n","import {\n createUniqueUuid,\n type IAgentRuntime,\n logger,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { requireRoomContext } from \"./storageContext\";\n\n/**\n * Per-Guild DJ Settings\n * Comprehensive settings for DJ behavior specific to each guild/room\n */\nexport interface DJGuildSettings {\n // Autonomy & Behavior\n autonomyLevel?: \"MANUAL\" | \"BALANCED\" | \"AUTONOMOUS\" | \"RADIO\";\n enabled: boolean; // DJ features enabled for this guild\n\n // Auto-Fill Configuration\n autoFillEnabled: boolean;\n autoFillThreshold?: number; // Number of tracks to trigger auto-fill\n timeBasedProgramming: boolean;\n repetitionControlEnabled: boolean;\n\n // DJ Commentary\n introsEnabled: boolean;\n commentaryEnabled: boolean;\n useLLMForCommentary: boolean;\n jokesSetting: boolean;\n\n // Automatic Events\n autoTriviaEnabled: boolean;\n autoTriviaMinTracks?: number;\n autoTriviaMinMinutes?: number;\n\n autoJokesEnabled: boolean;\n autoJokesMinTracks?: number;\n autoJokesMinMinutes?: number;\n\n autoRecapEnabled: boolean;\n autoRecapMinTracks?: number;\n autoRecapMinMinutes?: number;\n\n // Radio Station Info\n stationName?: string;\n stationDescription?: string;\n\n // Advanced\n listenerTrackingEnabled: boolean;\n audioQuality?: \"high\" | \"medium\" | \"low\";\n crossFadeEnabled: boolean;\n crossFadeDuration?: number; // milliseconds\n\n // Metadata\n createdAt: number;\n updatedAt: number;\n createdBy?: UUID;\n lastModifiedBy?: UUID;\n}\n\n/**\n * Default guild settings\n */\nexport const DEFAULT_GUILD_SETTINGS: DJGuildSettings = {\n autonomyLevel: \"BALANCED\",\n enabled: true,\n autoFillEnabled: true,\n autoFillThreshold: 3,\n timeBasedProgramming: true,\n repetitionControlEnabled: true,\n introsEnabled: true,\n commentaryEnabled: false,\n useLLMForCommentary: true,\n jokesSetting: true,\n autoTriviaEnabled: true,\n autoTriviaMinTracks: 5,\n autoTriviaMinMinutes: 30,\n autoJokesEnabled: true,\n autoJokesMinTracks: 7,\n autoJokesMinMinutes: 20,\n autoRecapEnabled: true,\n autoRecapMinTracks: 4,\n autoRecapMinMinutes: 15,\n listenerTrackingEnabled: true,\n audioQuality: \"high\",\n crossFadeEnabled: true,\n crossFadeDuration: 3000,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n};\n\nconst DJ_GUILD_SETTINGS_COMPONENT_TYPE = \"dj_guild_settings\";\nconst DJ_GUILD_SETTINGS_ENTITY_PREFIX = \"dj-guild-settings\";\n\nfunction getDJGuildSettingsEntityId(\n runtime: IAgentRuntime,\n roomId: UUID,\n): UUID {\n return createUniqueUuid(\n runtime,\n `${DJ_GUILD_SETTINGS_ENTITY_PREFIX}-${roomId}`,\n );\n}\n\n/**\n * Get DJ guild settings for a room\n */\nexport async function getDJGuildSettings(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<DJGuildSettings> {\n const entityId = getDJGuildSettingsEntityId(runtime, roomId);\n const component = await runtime.getComponent(\n entityId,\n DJ_GUILD_SETTINGS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n const storedSettings = getStoredField<DJGuildSettings>(component, \"settings\");\n if (!storedSettings) {\n return DEFAULT_GUILD_SETTINGS;\n }\n\n return {\n ...DEFAULT_GUILD_SETTINGS,\n ...storedSettings,\n };\n}\n\n/**\n * Set DJ guild settings for a room\n */\nexport async function setDJGuildSettings(\n runtime: IAgentRuntime,\n roomId: UUID,\n settings: Partial<DJGuildSettings>,\n modifiedBy?: UUID,\n): Promise<void> {\n try {\n const entityId = getDJGuildSettingsEntityId(runtime, roomId);\n const existingComponent = await runtime.getComponent(\n entityId,\n DJ_GUILD_SETTINGS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n const storedSettings = getStoredField<DJGuildSettings>(\n existingComponent,\n \"settings\",\n );\n const currentSettings = storedSettings\n ? { ...DEFAULT_GUILD_SETTINGS, ...storedSettings }\n : DEFAULT_GUILD_SETTINGS;\n\n const newSettings: DJGuildSettings = {\n ...currentSettings,\n ...settings,\n updatedAt: Date.now(),\n lastModifiedBy: modifiedBy || runtime.agentId,\n };\n\n if (existingComponent) {\n // Update existing component\n await runtime.updateComponent({\n ...existingComponent,\n data: mergeStoredField(existingComponent, \"settings\", newSettings),\n });\n } else {\n // Create new component\n const roomContext = await requireRoomContext(\n runtime,\n roomId,\n \"DJ Guild Settings\",\n );\n\n newSettings.createdAt = Date.now();\n newSettings.createdBy = modifiedBy || runtime.agentId;\n\n await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: roomContext.roomId,\n worldId: roomContext.worldId,\n sourceEntityId: runtime.agentId,\n type: DJ_GUILD_SETTINGS_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: createStoredField(\"settings\", newSettings),\n });\n }\n\n logger.info(`Updated DJ guild settings for room ${roomId}`);\n } catch (error) {\n logger.error(`Error setting DJ guild settings: ${error}`);\n throw error;\n }\n}\n\n/**\n * Reset DJ guild settings to defaults\n */\nexport async function resetDJGuildSettings(\n runtime: IAgentRuntime,\n roomId: UUID,\n modifiedBy?: UUID,\n): Promise<void> {\n await setDJGuildSettings(runtime, roomId, DEFAULT_GUILD_SETTINGS, modifiedBy);\n}\n\n/**\n * Enable/disable DJ for a guild\n */\nexport async function toggleDJ(\n runtime: IAgentRuntime,\n roomId: UUID,\n enabled: boolean,\n modifiedBy?: UUID,\n): Promise<void> {\n await setDJGuildSettings(runtime, roomId, { enabled }, modifiedBy);\n}\n\n/**\n * Set autonomy level for a guild\n */\nexport async function setAutonomyLevel(\n runtime: IAgentRuntime,\n roomId: UUID,\n level: \"MANUAL\" | \"BALANCED\" | \"AUTONOMOUS\" | \"RADIO\",\n modifiedBy?: UUID,\n): Promise<void> {\n await setDJGuildSettings(\n runtime,\n roomId,\n { autonomyLevel: level },\n modifiedBy,\n );\n}\n\n/**\n * Get all configured guilds\n */\nexport async function getAllConfiguredGuilds(\n _runtime: IAgentRuntime,\n): Promise<Array<{ roomId: UUID; settings: DJGuildSettings }>> {\n throw new Error(\n \"[DJ Guild Settings] getAllConfiguredGuilds requires runtime-level component indexing support\",\n );\n}\n","import {\n createUniqueUuid,\n type IAgentRuntime,\n logger,\n type UUID,\n} from \"@elizaos/core\";\nimport { v4 } from \"uuid\";\nimport {\n createStoredField,\n getStoredField,\n mergeStoredField,\n} from \"./componentData\";\nimport { requireRoomContext } from \"./storageContext\";\n\n/**\n * DJ Intro Options\n * Advanced configuration for DJ track introductions and commentary\n */\nexport interface DJIntroOptions {\n // Generation Method\n useLLM: boolean; // Use LLM vs templates\n llmModel?: string; // Specific model to use (optional)\n\n // Style & Tone\n style:\n | \"concise\"\n | \"detailed\"\n | \"casual\"\n | \"professional\"\n | \"energetic\"\n | \"chill\";\n personality?: string; // Custom personality description\n includeJokes: boolean;\n jokeFrequency?: number; // 0-100, percentage chance of joke\n\n // Content Options\n includeFunFacts: boolean;\n includeArtistInfo: boolean;\n includeDedications: boolean;\n dedicationStyle?: \"brief\" | \"heartfelt\" | \"casual\";\n\n // Timing & Frequency\n introDuration: \"short\" | \"medium\" | \"long\"; // 10-20s, 20-40s, 40-60s\n skipIntroChance?: number; // 0-100, percentage chance to skip intro\n minTracksBetweenIntros?: number; // Minimum tracks before doing another intro\n\n // Template Options (when useLLM = false)\n customTemplates?: string[]; // Custom intro templates\n templateVariety: boolean; // Rotate through templates or random\n\n // Advanced\n contextWindow?: number; // Number of previous tracks to consider\n musicInfoIntegration: boolean; // Use music-library metadata\n listenerCountIntegration: boolean; // Mention listener counts\n}\n\n/**\n * Default DJ intro options\n */\nexport const DEFAULT_DJ_INTRO_OPTIONS: DJIntroOptions = {\n useLLM: true,\n style: \"energetic\",\n includeJokes: true,\n jokeFrequency: 20,\n includeFunFacts: true,\n includeArtistInfo: false,\n includeDedications: true,\n dedicationStyle: \"heartfelt\",\n introDuration: \"short\",\n skipIntroChance: 0,\n minTracksBetweenIntros: 0,\n templateVariety: true,\n contextWindow: 3,\n musicInfoIntegration: true,\n listenerCountIntegration: false,\n};\n\nconst DJ_INTRO_OPTIONS_COMPONENT_TYPE = \"dj_intro_options\";\nconst DJ_INTRO_OPTIONS_ENTITY_PREFIX = \"dj-intro-options\";\n\nfunction getDJIntroOptionsEntityId(runtime: IAgentRuntime, roomId: UUID): UUID {\n return createUniqueUuid(\n runtime,\n `${DJ_INTRO_OPTIONS_ENTITY_PREFIX}-${roomId}`,\n );\n}\n\n/**\n * Get DJ intro options for a room\n */\nexport async function getDJIntroOptions(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<DJIntroOptions> {\n const entityId = getDJIntroOptionsEntityId(runtime, roomId);\n const component = await runtime.getComponent(\n entityId,\n DJ_INTRO_OPTIONS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n const storedOptions = getStoredField<DJIntroOptions>(component, \"options\");\n if (!storedOptions) {\n return DEFAULT_DJ_INTRO_OPTIONS;\n }\n\n return {\n ...DEFAULT_DJ_INTRO_OPTIONS,\n ...storedOptions,\n };\n}\n\n/**\n * Set DJ intro options for a room\n */\nexport async function setDJIntroOptions(\n runtime: IAgentRuntime,\n roomId: UUID,\n options: Partial<DJIntroOptions>,\n): Promise<void> {\n try {\n const entityId = getDJIntroOptionsEntityId(runtime, roomId);\n const existingComponent = await runtime.getComponent(\n entityId,\n DJ_INTRO_OPTIONS_COMPONENT_TYPE,\n undefined,\n runtime.agentId,\n );\n\n const storedOptions = getStoredField<DJIntroOptions>(\n existingComponent,\n \"options\",\n );\n const currentOptions = storedOptions\n ? { ...DEFAULT_DJ_INTRO_OPTIONS, ...storedOptions }\n : DEFAULT_DJ_INTRO_OPTIONS;\n\n const newOptions = { ...currentOptions, ...options };\n\n if (existingComponent) {\n // Update existing component\n await runtime.updateComponent({\n ...existingComponent,\n data: mergeStoredField(existingComponent, \"options\", newOptions),\n });\n } else {\n // Create new component\n const roomContext = await requireRoomContext(\n runtime,\n roomId,\n \"DJ Intro Options\",\n );\n\n await runtime.createComponent({\n id: v4() as UUID,\n entityId,\n agentId: runtime.agentId,\n roomId: roomContext.roomId,\n worldId: roomContext.worldId,\n sourceEntityId: runtime.agentId,\n type: DJ_INTRO_OPTIONS_COMPONENT_TYPE,\n createdAt: Date.now(),\n data: createStoredField(\"options\", newOptions),\n });\n }\n\n logger.info(`Updated DJ intro options for room ${roomId}`);\n } catch (error) {\n logger.error(`Error setting DJ intro options: ${error}`);\n throw error;\n }\n}\n\n/**\n * Reset DJ intro options to defaults for a room\n */\nexport async function resetDJIntroOptions(\n runtime: IAgentRuntime,\n roomId: UUID,\n): Promise<void> {\n await setDJIntroOptions(runtime, roomId, DEFAULT_DJ_INTRO_OPTIONS);\n}\n\n/**\n * Get intro prompt based on options\n */\nexport function buildIntroPrompt(\n options: DJIntroOptions,\n context: {\n characterName: string;\n trackTitle: string;\n artistName?: string;\n albumName?: string;\n dedicatedTo?: string;\n dedicationMessage?: string;\n listenerCount?: number;\n previousTracks?: string[];\n },\n): string {\n const {\n characterName,\n trackTitle,\n artistName,\n albumName,\n dedicatedTo,\n dedicationMessage,\n listenerCount,\n previousTracks,\n } = context;\n\n let prompt = `You are ${characterName}, a radio DJ introducing the next song.\\n\\n`;\n\n // Track info\n prompt += `Track: \"${trackTitle}\"`;\n if (options.includeArtistInfo && artistName) {\n prompt += ` by ${artistName}`;\n }\n if (albumName) {\n prompt += ` from the album \"${albumName}\"`;\n }\n prompt += \"\\n\";\n\n // Dedication\n if (options.includeDedications && dedicatedTo) {\n prompt += `\\nDedication: This track is dedicated to ${dedicatedTo}`;\n if (dedicationMessage) {\n prompt += ` with the message: \"${dedicationMessage}\"`;\n }\n prompt += \"\\n\";\n }\n\n // Listener count\n if (\n options.listenerCountIntegration &&\n listenerCount !== undefined &&\n listenerCount > 0\n ) {\n prompt += `\\nListeners: ${listenerCount} people tuned in right now\\n`;\n }\n\n // Previous tracks context\n if (options.contextWindow && previousTracks && previousTracks.length > 0) {\n const recentTracks = previousTracks.slice(-options.contextWindow);\n prompt += `\\nRecently played: ${recentTracks.join(\", \")}\\n`;\n }\n\n // Style instructions\n prompt += `\\nGenerate a ${options.introDuration.toUpperCase()} radio DJ introduction.\\n\\n`;\n prompt += `Guidelines:\\n`;\n\n // Duration guidelines\n if (options.introDuration === \"short\") {\n prompt += `1. Keep it VERY SHORT - 10-20 seconds when spoken (20-40 words MAX)\\n`;\n } else if (options.introDuration === \"medium\") {\n prompt += `1. Keep it MODERATE - 20-40 seconds when spoken (40-80 words MAX)\\n`;\n } else {\n prompt += `1. Keep it DETAILED - 40-60 seconds when spoken (80-120 words MAX)\\n`;\n }\n\n // Style guidelines\n const styleGuides: Record<typeof options.style, string> = {\n concise: \"Be brief and to-the-point\",\n detailed: \"Provide rich context and background\",\n casual: \"Sound relaxed and conversational\",\n professional: \"Maintain a polished, professional tone\",\n energetic: \"Be enthusiastic and high-energy\",\n chill: \"Keep it laid-back and mellow\",\n };\n prompt += `2. Style: ${styleGuides[options.style]}\\n`;\n\n // Content options\n if (\n options.includeJokes &&\n Math.random() * 100 < (options.jokeFrequency || 20)\n ) {\n prompt += `3. Include a quick, witty joke or clever observation\\n`;\n }\n if (options.includeFunFacts) {\n prompt += `4. Consider adding a brief fun fact about the artist or song\\n`;\n }\n if (dedicatedTo && options.dedicationStyle) {\n const dedStyles: Record<typeof options.dedicationStyle, string> = {\n brief: \"Mention the dedication briefly\",\n heartfelt: \"Deliver the dedication with warmth\",\n casual: \"Casually shout out the dedication\",\n };\n prompt += `5. ${dedStyles[options.dedicationStyle]}\\n`;\n }\n\n prompt += `6. NO meta-commentary - just deliver the intro naturally\\n`;\n prompt += `7. Sound spontaneous and conversational\\n`;\n\n if (options.personality) {\n prompt += `\\nPersonality note: ${options.personality}\\n`;\n }\n\n prompt += `\\nGenerate the intro now:`;\n\n return prompt;\n}\n","import type { ServerResponse } from \"node:http\";\nimport type { AgentRuntime } from \"@elizaos/core\";\n\ntype MusicTrack = {\n id?: string;\n title?: string;\n url?: string;\n duration?: number;\n requestedBy?: string;\n addedAt?: number;\n};\n\ntype MusicServiceLike = {\n getQueues?: () => Map<string, unknown> | Iterable<[string, unknown]>;\n getCurrentTrack?: (guildId: string) => MusicTrack | null | undefined;\n getIsPaused?: (guildId: string) => boolean;\n};\n\nfunction sendJson(\n res: ServerResponse,\n statusCode: number,\n body: unknown,\n): void {\n res.statusCode = statusCode;\n res.setHeader(\"Content-Type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n}\n\nfunction findActiveGuild(musicService: MusicServiceLike): {\n guildId: string;\n track: MusicTrack;\n} | null {\n const queues = musicService.getQueues?.();\n if (!queues) {\n return null;\n }\n\n for (const entry of queues) {\n const guildId = Array.isArray(entry) ? entry[0] : null;\n if (typeof guildId !== \"string\" || guildId.length === 0) {\n continue;\n }\n const track = musicService.getCurrentTrack?.(guildId);\n if (track) {\n return { guildId, track };\n }\n }\n\n return null;\n}\n\n/**\n * Compatibility fallback for `/music-player/status`.\n *\n * The UI polls this route even when plugin-music-player is not enabled. When the\n * plugin route is absent, returning a stable JSON payload is better than a noisy\n * 404 loop in desktop logs.\n */\nexport function tryHandleMusicPlayerStatusFallback(options: {\n pathname: string;\n method: string;\n runtime: AgentRuntime | null | undefined;\n res: ServerResponse;\n}): boolean {\n if (options.method !== \"GET\" || options.pathname !== \"/music-player/status\") {\n return false;\n }\n\n const musicService = options.runtime?.getService?.(\"music\") as\n | MusicServiceLike\n | null\n | undefined;\n\n if (!musicService) {\n sendJson(options.res, 200, {\n available: false,\n error: \"Music player plugin is not enabled\",\n });\n return true;\n }\n\n const active = findActiveGuild(musicService);\n if (!active) {\n sendJson(options.res, 200, {\n available: true,\n error: \"No track is currently playing\",\n });\n return true;\n }\n\n sendJson(options.res, 200, {\n available: true,\n guildId: active.guildId,\n track: {\n id: active.track.id,\n title: active.track.title,\n url: active.track.url,\n duration: active.track.duration,\n requestedBy: active.track.requestedBy,\n addedAt: active.track.addedAt,\n },\n isPaused: musicService.getIsPaused?.(active.guildId) === true,\n streamUrl: `/music-player/stream?guildId=${encodeURIComponent(active.guildId)}`,\n });\n return true;\n}\n","import { exec } from \"node:child_process\";\nimport {\n createReadStream,\n existsSync,\n mkdirSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport { promisify } from \"node:util\";\nimport { logger } from \"@elizaos/core\";\n\nconst execAsync = promisify(exec);\n\nexport interface StoredTrack {\n url: string;\n title: string;\n artist?: string;\n album?: string;\n filePath: string;\n format: string; // Original format (e.g., 'webm', 'mp4', 'opus')\n size: number;\n duration?: number;\n bitrate?: number;\n storedAt: number;\n}\n\n/**\n * Music Storage Service\n * Stores high-quality original music files for archival and library purposes\n *\n * Unlike the Discord-optimized cache in music-player, this stores:\n * - Original quality files (no transcoding)\n * - Permanent storage (not temporary cache)\n * - Organized by artist/album/track\n * - Indexed for library browsing\n */\nexport class MusicStorageService {\n private storageDir: string;\n private highQuality: boolean; // Store highest quality available\n private index: Map<string, StoredTrack> = new Map(); // key: url\n private readonly INDEX_FILE = \"storage_index.json\";\n\n constructor(storageDir?: string, highQuality: boolean = true) {\n this.storageDir = storageDir || join(process.cwd(), \"storage\", \"music\");\n this.highQuality = highQuality;\n\n // Ensure storage directory exists\n if (!existsSync(this.storageDir)) {\n mkdirSync(this.storageDir, { recursive: true });\n logger.info(`Created music storage directory: ${this.storageDir}`);\n }\n\n // Load existing index\n this.loadIndex();\n }\n\n /**\n * Check if a track is stored\n */\n isStored(url: string): boolean {\n const track = this.index.get(url);\n return track ? existsSync(track.filePath) : false;\n }\n\n /**\n * Get stored track info\n */\n getStoredTrack(url: string): StoredTrack | null {\n const track = this.index.get(url);\n if (!track || !existsSync(track.filePath)) {\n return null;\n }\n return track;\n }\n\n /**\n * Get all stored tracks\n */\n getAllTracks(): StoredTrack[] {\n return Array.from(this.index.values()).filter((track) =>\n existsSync(track.filePath),\n );\n }\n\n /**\n * Store a track from YouTube URL\n */\n async storeTrack(\n url: string,\n metadata: {\n title: string;\n artist?: string;\n album?: string;\n },\n ): Promise<StoredTrack> {\n // Check if already stored\n if (this.isStored(url)) {\n const existing = this.getStoredTrack(url);\n if (existing) {\n logger.debug(`Track already stored: ${metadata.title}`);\n return existing;\n }\n }\n\n logger.info(`Storing track: ${metadata.title}`);\n\n // Generate storage path\n const sanitize = (str: string) =>\n str.replace(/[^a-zA-Z0-9]/g, \"_\").substring(0, 100);\n const artist = sanitize(metadata.artist || \"Unknown\");\n const album = sanitize(metadata.album || \"Unknown\");\n const title = sanitize(metadata.title);\n\n const trackDir = join(this.storageDir, artist, album);\n if (!existsSync(trackDir)) {\n mkdirSync(trackDir, { recursive: true });\n }\n\n const filePath = join(trackDir, `${title}.webm`);\n\n // Download using yt-dlp\n try {\n const quality = this.highQuality ? \"bestaudio\" : \"worstaudio\";\n const command = `yt-dlp -f \"${quality}\" --no-playlist -o \"${filePath}\" \"${url}\"`;\n\n logger.debug(`Downloading: ${command}`);\n await execAsync(command, { timeout: 300000 }); // 5 minute timeout\n\n // Get file info\n const stats = statSync(filePath);\n const metadata_cmd = `ffprobe -v quiet -print_format json -show_format \"${filePath}\"`;\n const { stdout } = await execAsync(metadata_cmd);\n const info = JSON.parse(stdout);\n\n const storedTrack: StoredTrack = {\n url,\n title: metadata.title,\n artist: metadata.artist,\n album: metadata.album,\n filePath,\n format: info.format?.format_name || \"webm\",\n size: stats.size,\n duration: parseFloat(info.format?.duration) || undefined,\n bitrate: parseInt(info.format?.bit_rate, 10) || undefined,\n storedAt: Date.now(),\n };\n\n // Add to index\n this.index.set(url, storedTrack);\n this.saveIndex();\n\n logger.info(\n `Stored track: ${metadata.title} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`,\n );\n return storedTrack;\n } catch (error) {\n logger.error(`Error storing track ${metadata.title}: ${error}`);\n // Clean up partial file\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n throw error;\n }\n }\n\n /**\n * Get a readable stream for a stored track\n */\n getStream(url: string): Readable | null {\n const track = this.getStoredTrack(url);\n if (!track) {\n return null;\n }\n\n return createReadStream(track.filePath);\n }\n\n /**\n * Delete a stored track\n */\n deleteTrack(url: string): boolean {\n const track = this.index.get(url);\n if (!track) {\n return false;\n }\n\n try {\n if (existsSync(track.filePath)) {\n unlinkSync(track.filePath);\n }\n this.index.delete(url);\n this.saveIndex();\n logger.info(`Deleted stored track: ${track.title}`);\n return true;\n } catch (error) {\n logger.error(`Error deleting track: ${error}`);\n return false;\n }\n }\n\n /**\n * Get total storage size in bytes\n */\n getTotalSize(): number {\n let total = 0;\n for (const track of this.index.values()) {\n if (existsSync(track.filePath)) {\n total += track.size;\n }\n }\n return total;\n }\n\n /**\n * Get storage statistics\n */\n getStats(): {\n totalTracks: number;\n totalSize: number;\n totalDuration: number;\n byArtist: Map<string, number>;\n } {\n const tracks = this.getAllTracks();\n const byArtist = new Map<string, number>();\n\n let totalSize = 0;\n let totalDuration = 0;\n\n for (const track of tracks) {\n totalSize += track.size;\n totalDuration += track.duration || 0;\n\n const artist = track.artist || \"Unknown\";\n byArtist.set(artist, (byArtist.get(artist) || 0) + 1);\n }\n\n return {\n totalTracks: tracks.length,\n totalSize,\n totalDuration,\n byArtist,\n };\n }\n\n /**\n * Load index from disk\n */\n private loadIndex(): void {\n const indexPath = join(this.storageDir, this.INDEX_FILE);\n if (!existsSync(indexPath)) {\n return;\n }\n\n try {\n const data = readFileSync(indexPath, \"utf8\");\n const entries = JSON.parse(data);\n this.index = new Map(Object.entries(entries));\n logger.debug(`Loaded ${this.index.size} tracks from storage index`);\n } catch (error) {\n logger.error(`Error loading storage index: ${error}`);\n }\n }\n\n /**\n * Save index to disk\n */\n private saveIndex(): void {\n const indexPath = join(this.storageDir, this.INDEX_FILE);\n try {\n const entries = Object.fromEntries(this.index);\n const data = JSON.stringify(entries, null, 2);\n writeFileSync(indexPath, data, \"utf8\");\n } catch (error) {\n logger.error(`Error saving storage index: ${error}`);\n }\n }\n}\n"],"mappings":"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","names":["logger","logger","selectedContextMatches","emit","readParams","logger","logger","logger","logger","logger","logger","logger","logger","logger","selectedContextMatches","logger","logger","EventEmitter","logger","PassThrough","logger","logger","require","logger","logger","PassThrough","logger","PassThrough","logger","EventEmitter","logger","EventEmitter","PassThrough","logger","v4","logger","spawn","existsSync","PassThrough","logger","existsSync","dirname","logger","dirname","existsSync","logger","resolve","spawn","PassThrough","stderrOutput","logger","EventEmitter","PassThrough","logger","v4","resolve","PassThrough","logger","logger","logger","createReadStream","existsSync","unlinkSync","join","logger","getYouTubeCookiesPath","existsSync","logger","getProxyUrl","join","createReadStream","unlinkSync","filePath","stats","MUSIC_SERVICE_NAME","logger","broadcast","selectedContextMatches","logger","extractSearchQuery","resolve","logger","logger","Service","resolve","MUSIC_SERVICE_NAME","failureResult","selectedContextMatches","normalizeOp","inferOpFromText","findActiveGuildId","MUSIC_SERVICE_NAME","failureResult","text","logger","normalizeOp","inferOpFromText","normalizeOp","inferOpFromText","selectedContextMatches","logger","logger","logger","recentSongs","logger","logger","MUSIC_LIBRARY_SERVICE_NAME","logger","MUSIC_SERVICE_NAME","DEFAULT_LIMIT","logger","logger","Service","createUniqueUuid","logger","v4","createUniqueUuid","logger","v4","createUniqueUuid","logger","v4","createUniqueUuid","isRecord","v4","logger","v4","v4","logger","createUniqueUuid","v4","createUniqueUuid","v4","logger","ModelType","ModelType","logger","logger","logger","resolve","resolve","logger","logger","resolve","logger","logger","resolve","logger","logger","resolve","logger","logger","result","logger","Buffer","Buffer","logger","resolve","logger","resolve","logger","logger","ModelType","formatPromptValue","ModelType","logger","logger","logger","MUSIC_LIBRARY_SERVICE_NAME","Service","logger","createUniqueUuid","logger","v4","createUniqueUuid","v4","logger","createUniqueUuid","logger","v4","createUniqueUuid","v4","logger","findActiveGuild","exec","createReadStream","existsSync","mkdirSync","statSync","unlinkSync","join","promisify","logger","execAsync","logger"]}