@sriinnu/harmon-youtube 0.1.0

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/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @sriinnu/harmon-youtube
2
+
3
+ ![logo](./logo.svg)
4
+
5
+ > YouTube Music integration for the currently implemented Data API v3 surface.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @sriinnu/harmon-youtube
11
+ ```
12
+
13
+ ## Pack Auth
14
+
15
+ These commands are for the provider pack checkout or a profile-aware loader that executes inside the pack root. They are not exposed as a global installed binary. From a fresh repo checkout, run `pnpm build` once before you use them.
16
+
17
+ ```bash
18
+ npm run auth
19
+ npm run auth:status
20
+ npm run auth:refresh
21
+ ```
22
+
23
+ I keep Google OAuth state in `~/.chitragupta/harmon/provider-packs/harmon-youtube/youtube-oauth.json` by default. Set `HARMON_PACK_STATE_DIR` if you need a different local root. I also ship `.chitragupta-ecosystem/.profile.json` so Chitragupta-style loaders can find the auth entrypoints, README, skill, and logo.
24
+
25
+ I support two runtime shapes:
26
+
27
+ - OAuth bootstrap with `YOUTUBE_MUSIC_CLIENT_ID` for liked tracks, owned playlists, and playlist-derived session seeds
28
+ - API-key mode with `YOUTUBE_MUSIC_API_KEY` for catalog-only search when user surfaces are not needed
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import { createYouTubeMusicClient } from '@sriinnu/harmon-youtube';
34
+
35
+ const client = createYouTubeMusicClient({ apiKey: process.env.YOUTUBE_MUSIC_API_KEY });
36
+ const results = await client.search('synthwave focus');
37
+ console.log(results.songs[0].name);
38
+ ```
39
+
40
+ ## API
41
+
42
+ | Export | Description |
43
+ |---|---|
44
+ | `createYouTubeMusicClient(config)` | Create a client for catalog search, liked-video derived access, owned playlist browsing, and related-track recommendations |
45
+ | `YouTubeMusicClient` | Interface: `search()`, `getSong()`, `getLibrarySongs()`, `getPlaylists()`, `getPlaylistTracks()`, and recommendation helpers |
46
+ | `mapSongToTrackInfo(song)` | Convert to provider-agnostic `TrackInfo` |
47
+ | `YouTubeMusicConfig` | `{ accessToken?, apiKey?, cookies? }` with `cookies` reserved for future internal-API coverage |
48
+ | `YouTubeMusicSong` | Song with id, name, artistName, durationMs, thumbnailUrl |
49
+ | `YouTubeMusicSearchResult` | Grouped results for songs, album-like playlist results, artists, and playlists |
50
+
51
+ ## Architecture
52
+
53
+ harmon-youtube bridges YouTube Music into the harmon ecosystem through the official YouTube Data API v3. It supports song, playlist, and artist discovery; liked-video derived tracks and owned playlists when an OAuth access token is configured; playlist-track retrieval; and related-track recommendations via `relatedToVideoId`. The daemon pairs this package with a browser-handoff playback controller for provider-aware sessions. Pause remains intentionally unsupported in browser-handoff mode, and I do not expose a synthetic `topTracks` surface because the official API does not distinguish it honestly from recent or liked activity. The `mapSongToTrackInfo` utility converts results into the protocol's `TrackInfo` format for engine consumption.
54
+
55
+ ## License
56
+
57
+ GNU Affero General Public License v3.0 only. See [LICENSE](../../LICENSE).
package/SKILL.md ADDED
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: harmon-youtube
3
+ description: YouTube Music client for the currently implemented Data API v3 surface with MusicProvider adapter
4
+ capabilities:
5
+ - Search YouTube Music for songs
6
+ - Look up a single song/video by id
7
+ - Adapt YouTube Music to the harmon-core MusicProvider interface
8
+ tags:
9
+ - youtube-music
10
+ - music
11
+ - api
12
+ - streaming
13
+ provider: harmon
14
+ version: 0.1.0
15
+ ---
16
+
17
+ # Harmon YouTube
18
+
19
+ ## What this does
20
+ harmon-youtube integrates YouTube Music into the harmon ecosystem through the official YouTube Data API v3. It supports song search, owned playlist reads, liked-library tracks, playlist-track retrieval, and related-track recommendations when OAuth is configured, plus catalog-only search when an API key is sufficient. Songs are mapped to the shared TrackInfo format, and the provider adapter lets harmon-core treat YouTube Music as a standard track source without depending on private YTM endpoints.
21
+
22
+ ## Pack auth
23
+ - `npm run auth` bootstraps Google OAuth for playlist, liked-track, and user-library access
24
+ - `npm run auth:refresh` refreshes the stored Google token
25
+ - `npm run auth:status` prints whether the pack is in OAuth, API-key, or disconnected mode
26
+ - `.chitragupta-ecosystem/.profile.json` is the loader-facing metadata contract for this pack
27
+
28
+ ## When to use
29
+ - Searching YouTube Music songs through the Data API
30
+ - Reading liked tracks or owned playlists with Google OAuth
31
+ - Adding YouTube Music as a candidate source in a harmon session through search, liked, playlist, or related-track seeds
32
+
33
+ ## Key exports
34
+ - `createYouTubeMusicClient` — factory that returns a YouTubeMusicClient (needs an accessToken or apiKey)
35
+ - `createYouTubeMusicProvider` — MusicProvider adapter for harmon-core
36
+ - `mapSongToTrackInfo` — converts a YouTubeMusicSong to the provider-agnostic TrackInfo format
37
+
38
+ ## Example
39
+ ```typescript
40
+ import { createYouTubeMusicClient, createYouTubeMusicProvider } from '@sriinnu/harmon-youtube';
41
+
42
+ const client = createYouTubeMusicClient({ apiKey: 'AIza...' });
43
+ const results = await client.search('Bonobo', ['songs'], { limit: 10 });
44
+ const provider = createYouTubeMusicProvider(client);
45
+ const tracks = await provider.search('Bonobo', 10);
46
+ ```
@@ -0,0 +1,5 @@
1
+ /**
2
+ * I bootstrap, refresh, and report Google OAuth for the YouTube Music provider pack.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=auth-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-cli.d.ts","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,287 @@
1
+ /**
2
+ * I bootstrap, refresh, and report Google OAuth for the YouTube Music provider pack.
3
+ */
4
+ import { spawn } from 'node:child_process';
5
+ import { createHash, randomBytes } from 'node:crypto';
6
+ import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
7
+ import { createServer } from 'node:http';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import process from 'node:process';
11
+ const authPath = resolveAuthPath('youtube-oauth.json');
12
+ const tokenUrl = 'https://oauth2.googleapis.com/token';
13
+ const authUrl = 'https://accounts.google.com/o/oauth2/v2/auth';
14
+ /**
15
+ * I keep Google OAuth state under user-local storage instead of the package
16
+ * tree so installed packs stay immutable and publish-safe.
17
+ */
18
+ function resolveAuthPath(fileName) {
19
+ const overrideRoot = process.env.HARMON_PACK_STATE_DIR?.trim();
20
+ const stateRoot = overrideRoot && overrideRoot.length > 0
21
+ ? overrideRoot
22
+ : path.join(os.homedir(), '.chitragupta', 'harmon', 'provider-packs');
23
+ return path.join(stateRoot, 'harmon-youtube', fileName);
24
+ }
25
+ async function readJson(filePath) {
26
+ try {
27
+ return JSON.parse(await readFile(filePath, 'utf8'));
28
+ }
29
+ catch (error) {
30
+ if (isNodeError(error) && error.code === 'ENOENT') {
31
+ return null;
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ async function writeJson(filePath, value) {
37
+ await mkdir(path.dirname(filePath), { mode: 0o700, recursive: true });
38
+ if (value == null) {
39
+ await rm(filePath, { force: true });
40
+ return;
41
+ }
42
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
43
+ await chmod(filePath, 0o600);
44
+ }
45
+ async function openUrl(url) {
46
+ if (process.env.HARMON_NO_BROWSER === '1') {
47
+ return;
48
+ }
49
+ const opener = process.platform === 'darwin'
50
+ ? ['open', [url]]
51
+ : process.platform === 'win32'
52
+ ? ['cmd', ['/c', 'start', '', url]]
53
+ : ['xdg-open', [url]];
54
+ await new Promise(resolve => {
55
+ const child = spawn(opener[0], opener[1], { stdio: 'ignore' });
56
+ child.on('error', () => resolve());
57
+ child.on('close', () => resolve());
58
+ });
59
+ }
60
+ function readConfig() {
61
+ const port = Number.parseInt(process.env.YOUTUBE_MUSIC_AUTH_PORT || '8789', 10);
62
+ return {
63
+ clientId: process.env.YOUTUBE_MUSIC_CLIENT_ID || '',
64
+ clientSecret: process.env.YOUTUBE_MUSIC_CLIENT_SECRET,
65
+ redirectUri: process.env.YOUTUBE_MUSIC_REDIRECT_URI || `http://127.0.0.1:${port}/callback`,
66
+ scopes: (process.env.YOUTUBE_MUSIC_OAUTH_SCOPES || 'https://www.googleapis.com/auth/youtube.readonly').split(/\s+/).filter(Boolean),
67
+ apiKey: process.env.YOUTUBE_MUSIC_API_KEY,
68
+ };
69
+ }
70
+ function createPkcePair() {
71
+ const verifier = randomBytes(48).toString('base64url');
72
+ const challenge = createHash('sha256').update(verifier).digest('base64url');
73
+ return { verifier, challenge };
74
+ }
75
+ async function exchangeCode(config, code, verifier) {
76
+ const body = new URLSearchParams({
77
+ client_id: config.clientId,
78
+ code,
79
+ code_verifier: verifier,
80
+ grant_type: 'authorization_code',
81
+ redirect_uri: config.redirectUri,
82
+ });
83
+ if (config.clientSecret) {
84
+ body.set('client_secret', config.clientSecret);
85
+ }
86
+ const response = await fetch(tokenUrl, {
87
+ method: 'POST',
88
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
89
+ body,
90
+ });
91
+ if (!response.ok) {
92
+ throw new Error(`Google token exchange failed: ${response.status} ${await response.text()}`);
93
+ }
94
+ return await response.json();
95
+ }
96
+ async function refreshToken(config, state) {
97
+ const body = new URLSearchParams({
98
+ client_id: config.clientId,
99
+ grant_type: 'refresh_token',
100
+ refresh_token: state.refreshToken || '',
101
+ });
102
+ if (config.clientSecret) {
103
+ body.set('client_secret', config.clientSecret);
104
+ }
105
+ const response = await fetch(tokenUrl, {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
108
+ body,
109
+ });
110
+ if (!response.ok) {
111
+ throw new Error(`Google token refresh failed: ${response.status} ${await response.text()}`);
112
+ }
113
+ const payload = await response.json();
114
+ return {
115
+ ...state,
116
+ accessToken: payload.access_token,
117
+ scope: payload.scope || state.scope,
118
+ updatedAt: new Date().toISOString(),
119
+ expiresAt: new Date(Date.now() + (payload.expires_in || 3600) * 1000).toISOString(),
120
+ tokenType: payload.token_type || state.tokenType || 'Bearer',
121
+ };
122
+ }
123
+ /**
124
+ * I validate the current YouTube auth posture against the public Data API.
125
+ */
126
+ async function validateYouTube(state, apiKey) {
127
+ const url = new URL('https://www.googleapis.com/youtube/v3/search');
128
+ url.searchParams.set('part', 'snippet');
129
+ url.searchParams.set('maxResults', '1');
130
+ url.searchParams.set('q', 'ambient');
131
+ url.searchParams.set('type', 'video');
132
+ const headers = {};
133
+ if (state?.accessToken) {
134
+ headers.Authorization = `Bearer ${state.accessToken}`;
135
+ }
136
+ else if (apiKey) {
137
+ url.searchParams.set('key', apiKey);
138
+ }
139
+ else {
140
+ throw new Error('YouTube auth status requires either OAuth state or YOUTUBE_MUSIC_API_KEY.');
141
+ }
142
+ const response = await fetch(url, { headers });
143
+ if (!response.ok) {
144
+ throw new Error(`YouTube validation failed: ${response.status} ${await response.text()}`);
145
+ }
146
+ }
147
+ /**
148
+ * I wait for the localhost Google OAuth callback and exchange the auth code.
149
+ */
150
+ async function waitForCallback(config, verifier, expectedState) {
151
+ const redirectUrl = new URL(config.redirectUri);
152
+ return await new Promise((resolve, reject) => {
153
+ const server = createServer((request, response) => {
154
+ const requestUrl = new URL(request.url || '/', redirectUrl);
155
+ if (requestUrl.pathname !== redirectUrl.pathname) {
156
+ response.writeHead(404).end('Not found');
157
+ return;
158
+ }
159
+ const code = requestUrl.searchParams.get('code');
160
+ const state = requestUrl.searchParams.get('state');
161
+ if (!code) {
162
+ response.writeHead(400).end('Missing code');
163
+ reject(new Error('Google OAuth callback did not include a code.'));
164
+ server.close();
165
+ return;
166
+ }
167
+ if (!state) {
168
+ response.writeHead(400).end('Missing state');
169
+ reject(new Error('Google OAuth callback did not include a state parameter.'));
170
+ server.close();
171
+ return;
172
+ }
173
+ if (state !== expectedState) {
174
+ response.writeHead(400).end('Invalid state');
175
+ reject(new Error('Google OAuth state did not match the login attempt.'));
176
+ server.close();
177
+ return;
178
+ }
179
+ response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
180
+ response.end('<h1>YouTube Music auth complete</h1><p>I saved your token locally. You can close this tab.</p>');
181
+ void exchangeCode(config, code, verifier).then(resolve, reject).finally(() => {
182
+ server.close();
183
+ });
184
+ });
185
+ server.on('error', reject);
186
+ server.listen(Number(redirectUrl.port || 80), redirectUrl.hostname);
187
+ });
188
+ }
189
+ /**
190
+ * I print the current YouTube auth posture in a machine-friendly format.
191
+ */
192
+ async function printStatus(state, lifecycle, apiKey) {
193
+ console.log(JSON.stringify({
194
+ provider: 'youtube-music',
195
+ state: lifecycle,
196
+ mode: state?.accessToken ? 'oauth' : apiKey ? 'api-key' : 'disconnected',
197
+ connected: Boolean(state?.accessToken || apiKey),
198
+ hasRefreshToken: Boolean(state?.refreshToken),
199
+ expiresAt: state?.expiresAt || null,
200
+ scope: state?.scope || null,
201
+ authFile: authPath,
202
+ apiKeyConfigured: Boolean(apiKey),
203
+ }, null, 2));
204
+ }
205
+ async function bootstrap() {
206
+ const config = readConfig();
207
+ const existing = await readJson(authPath);
208
+ if (existing?.accessToken && process.env.HARMON_AUTH_FORCE !== '1') {
209
+ await validateYouTube(existing, config.apiKey);
210
+ await printStatus(existing, 'already-authenticated', config.apiKey);
211
+ return;
212
+ }
213
+ if (!config.clientId) {
214
+ if (config.apiKey) {
215
+ await validateYouTube(null, config.apiKey);
216
+ await printStatus(null, 'api-key-only', config.apiKey);
217
+ return;
218
+ }
219
+ throw new Error('Set YOUTUBE_MUSIC_CLIENT_ID for OAuth bootstrap, or configure YOUTUBE_MUSIC_API_KEY for catalog-only mode.');
220
+ }
221
+ const { verifier, challenge } = createPkcePair();
222
+ const oauthState = randomBytes(24).toString('base64url');
223
+ const url = new URL(authUrl);
224
+ url.searchParams.set('client_id', config.clientId);
225
+ url.searchParams.set('redirect_uri', config.redirectUri);
226
+ url.searchParams.set('response_type', 'code');
227
+ url.searchParams.set('scope', config.scopes.join(' '));
228
+ url.searchParams.set('access_type', 'offline');
229
+ url.searchParams.set('prompt', 'consent');
230
+ url.searchParams.set('code_challenge', challenge);
231
+ url.searchParams.set('code_challenge_method', 'S256');
232
+ url.searchParams.set('state', oauthState);
233
+ console.error(`YouTube Music login URL: ${url.toString()}`);
234
+ const callback = waitForCallback(config, verifier, oauthState);
235
+ await openUrl(url.toString());
236
+ const payload = await callback;
237
+ const authState = {
238
+ provider: 'youtube-music',
239
+ updatedAt: new Date().toISOString(),
240
+ accessToken: payload.access_token,
241
+ refreshToken: payload.refresh_token || existing?.refreshToken || null,
242
+ expiresAt: new Date(Date.now() + (payload.expires_in || 3600) * 1000).toISOString(),
243
+ scope: payload.scope || config.scopes.join(' '),
244
+ tokenType: payload.token_type || 'Bearer',
245
+ };
246
+ await validateYouTube(authState, config.apiKey);
247
+ await writeJson(authPath, authState);
248
+ await printStatus(authState, 'bootstrapped', config.apiKey);
249
+ }
250
+ async function refresh() {
251
+ const config = readConfig();
252
+ const existing = await readJson(authPath);
253
+ if (!existing?.refreshToken) {
254
+ throw new Error('I cannot refresh YouTube Music auth without a stored refresh token. Run npm run auth first.');
255
+ }
256
+ if (!config.clientId) {
257
+ throw new Error('YOUTUBE_MUSIC_CLIENT_ID is required to refresh the stored Google token.');
258
+ }
259
+ const state = await refreshToken(config, existing);
260
+ await validateYouTube(state, config.apiKey);
261
+ await writeJson(authPath, state);
262
+ await printStatus(state, 'refreshed', config.apiKey);
263
+ }
264
+ async function status() {
265
+ const config = readConfig();
266
+ const state = await readJson(authPath);
267
+ if (state?.accessToken || config.apiKey) {
268
+ await validateYouTube(state, config.apiKey);
269
+ }
270
+ await printStatus(state, 'status', config.apiKey);
271
+ }
272
+ function isNodeError(error) {
273
+ return error !== null && typeof error === 'object' && 'code' in error;
274
+ }
275
+ const action = process.argv[2] || 'bootstrap';
276
+ const handlers = { bootstrap, refresh, status };
277
+ if (!handlers[action]) {
278
+ console.error(`Unknown YouTube Music auth command: ${action}`);
279
+ process.exitCode = 1;
280
+ }
281
+ else {
282
+ handlers[action]().catch(error => {
283
+ console.error(error instanceof Error ? error.message : String(error));
284
+ process.exitCode = 1;
285
+ });
286
+ }
287
+ //# sourceMappingURL=auth-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-cli.js","sourceRoot":"","sources":["../src/auth-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Harmon YouTube - YouTube Music integration
3
+ *
4
+ * I keep this adapter on the official YouTube Data API.
5
+ * That means I can support search, owned playlists, liked-library tracks,
6
+ * playlist tracks, and related-track recommendations without pulling in
7
+ * private YouTube Music endpoints that would make the runtime brittle.
8
+ */
9
+ import type { TrackInfo } from '@sriinnu/harmon-protocol';
10
+ import type { MusicProvider, AudioFeatures } from '@sriinnu/harmon-core';
11
+ export interface YouTubeMusicConfig {
12
+ /** Google OAuth2 access token */
13
+ accessToken?: string;
14
+ /** YouTube Data API key (for search without auth) */
15
+ apiKey?: string;
16
+ /** Reserved for future YTM-internal coverage; not sufficient on its own today */
17
+ cookies?: string;
18
+ }
19
+ export interface YouTubeMusicSong {
20
+ id: string;
21
+ name: string;
22
+ artistName: string;
23
+ artistId?: string;
24
+ albumName?: string;
25
+ albumId?: string;
26
+ durationMs?: number;
27
+ thumbnailUrl?: string;
28
+ }
29
+ export interface YouTubeMusicAlbum {
30
+ id: string;
31
+ name: string;
32
+ artistName: string;
33
+ year?: string;
34
+ thumbnailUrl?: string;
35
+ }
36
+ export interface YouTubeMusicArtist {
37
+ id: string;
38
+ name: string;
39
+ thumbnailUrl?: string;
40
+ }
41
+ export interface YouTubeMusicPlaylist {
42
+ id: string;
43
+ name: string;
44
+ author?: string;
45
+ trackCount?: number;
46
+ thumbnailUrl?: string;
47
+ }
48
+ export interface YouTubeMusicSearchResult {
49
+ songs: YouTubeMusicSong[];
50
+ albums: YouTubeMusicAlbum[];
51
+ artists: YouTubeMusicArtist[];
52
+ playlists: YouTubeMusicPlaylist[];
53
+ }
54
+ export type YouTubeMusicSearchType = 'songs' | 'albums' | 'artists' | 'playlists';
55
+ export interface YouTubeMusicListOptions {
56
+ limit?: number;
57
+ }
58
+ export interface YouTubeMusicClient {
59
+ isConnected(): boolean;
60
+ search(query: string, types?: YouTubeMusicSearchType[], options?: YouTubeMusicListOptions): Promise<YouTubeMusicSearchResult>;
61
+ getSong(videoId: string): Promise<YouTubeMusicSong | null>;
62
+ getLibrarySongs(options?: YouTubeMusicListOptions): Promise<YouTubeMusicSong[]>;
63
+ getPlaylists(options?: YouTubeMusicListOptions): Promise<YouTubeMusicPlaylist[]>;
64
+ getPlaylistTracks(playlistId: string, options?: YouTubeMusicListOptions): Promise<YouTubeMusicSong[]>;
65
+ getRecommendations(options?: YouTubeMusicListOptions): Promise<YouTubeMusicSong[]>;
66
+ getWatchPlaylist(videoId: string, options?: YouTubeMusicListOptions): Promise<YouTubeMusicSong[]>;
67
+ }
68
+ /** Convert YouTube Music song to provider-agnostic TrackInfo */
69
+ export declare function mapSongToTrackInfo(song: YouTubeMusicSong): TrackInfo;
70
+ /**
71
+ * Adapts YouTubeMusicClient to the MusicProvider interface.
72
+ */
73
+ export declare class YouTubeMusicProvider implements MusicProvider {
74
+ readonly name: "youtube";
75
+ private client;
76
+ constructor(client: YouTubeMusicClient);
77
+ isConnected(): boolean;
78
+ search(query: string, limit?: number): Promise<TrackInfo[]>;
79
+ getLibraryTracks(options?: {
80
+ limit?: number;
81
+ }): Promise<TrackInfo[]>;
82
+ /**
83
+ * I reject synthetic top-track semantics so the YouTube provider stays
84
+ * aligned with what the official API actually exposes.
85
+ */
86
+ getTopTracks(options?: {
87
+ limit?: number;
88
+ }): Promise<TrackInfo[]>;
89
+ getRecentlyPlayed(options?: {
90
+ limit?: number;
91
+ }): Promise<TrackInfo[]>;
92
+ getPlaylistTracks(playlistId: string, options?: {
93
+ limit?: number;
94
+ }): Promise<TrackInfo[]>;
95
+ getRecommendations(options: {
96
+ seedTrackIds?: string[];
97
+ limit?: number;
98
+ }): Promise<TrackInfo[]>;
99
+ getTrackFeatures(trackIds: string[]): Promise<(AudioFeatures | null)[]>;
100
+ }
101
+ export declare function createYouTubeMusicProvider(client: YouTubeMusicClient): MusicProvider;
102
+ /**
103
+ * I create a YouTube Music client for the currently supported Data API
104
+ * surface: song search and individual song lookup.
105
+ */
106
+ export declare function createYouTubeMusicClient(config: YouTubeMusicConfig): YouTubeMusicClient;
107
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}