yandex-music-desktop-library 1.0.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/LICENSE +21 -0
- package/README.md +287 -0
- package/bin/win-x64/YandexMusicController.exe +0 -0
- package/bin/win-x64/YandexMusicController.pdb +0 -0
- package/bin/win-x64/libSkiaSharp.dll +0 -0
- package/dist/controller.d.ts +96 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +270 -0
- package/dist/controller.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 WebAFilippov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Yandex Music Desktop Library
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/yandex-music-desktop-library)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
TypeScript library for controlling **Yandex Music desktop application** on Windows. Provides event-based and promise-based APIs for media control, volume management, and real-time track information.
|
|
7
|
+
|
|
8
|
+
## â ī¸ Requirements
|
|
9
|
+
|
|
10
|
+
- **Windows 10/11** (version 1809 or later)
|
|
11
|
+
- **Node.js** 14.0.0 or later
|
|
12
|
+
- **Yandex Music** desktop application installed and running
|
|
13
|
+
|
|
14
|
+
## đ Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install yandex-music-desktop-library
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## đ Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { YandexMusicController } from 'yandex-music-desktop-library';
|
|
24
|
+
|
|
25
|
+
// Create controller with custom options
|
|
26
|
+
const controller = new YandexMusicController({
|
|
27
|
+
thumbnailSize: 200, // Thumbnail size in pixels (default: 150)
|
|
28
|
+
thumbnailQuality: 90, // JPEG quality 1-100 (default: 85)
|
|
29
|
+
autoRestart: true, // Auto-restart on crash (default: true)
|
|
30
|
+
restartDelay: 1000 // Restart delay in ms (default: 1000)
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Event-based API - listen for track changes
|
|
34
|
+
controller.on('track', (track) => {
|
|
35
|
+
if (track) {
|
|
36
|
+
console.log(`đĩ Now playing: ${track.title} by ${track.artist}`);
|
|
37
|
+
console.log(`đ Album: ${track.album}`);
|
|
38
|
+
console.log(`đ Volume: ${track.volume}% (Muted: ${track.isMuted})`);
|
|
39
|
+
console.log(`âļī¸ Status: ${track.playbackStatus}`);
|
|
40
|
+
} else {
|
|
41
|
+
console.log('âšī¸ Yandex Music is not running');
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Listen for volume changes
|
|
46
|
+
controller.on('volume', ({ volume, isMuted }) => {
|
|
47
|
+
console.log(`đ Volume changed: ${volume}% (Muted: ${isMuted})`);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Handle errors
|
|
51
|
+
controller.on('error', (error) => {
|
|
52
|
+
console.error('Controller error:', error.message);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Start the controller
|
|
56
|
+
await controller.start();
|
|
57
|
+
|
|
58
|
+
// Promise-based API - control playback
|
|
59
|
+
await controller.playPause();
|
|
60
|
+
await controller.next();
|
|
61
|
+
await controller.previous();
|
|
62
|
+
|
|
63
|
+
// Control volume
|
|
64
|
+
await controller.setVolume(75);
|
|
65
|
+
await controller.volumeUp(5); // Increase by 5%
|
|
66
|
+
await controller.volumeDown(3); // Decrease by 3%
|
|
67
|
+
await controller.toggleMute();
|
|
68
|
+
|
|
69
|
+
// Stop the controller
|
|
70
|
+
await controller.stop();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## đ Electron Integration
|
|
74
|
+
|
|
75
|
+
### Basic Usage
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { YandexMusicController } from 'yandex-music-desktop-library';
|
|
79
|
+
import { ipcMain } from 'electron';
|
|
80
|
+
|
|
81
|
+
const controller = new YandexMusicController({
|
|
82
|
+
thumbnailSize: 150,
|
|
83
|
+
thumbnailQuality: 85,
|
|
84
|
+
autoRestart: true
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Start controller when app is ready
|
|
88
|
+
app.whenReady().then(async () => {
|
|
89
|
+
await controller.start();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// IPC handlers for renderer process
|
|
93
|
+
ipcMain.handle('play-pause', () => controller.playPause());
|
|
94
|
+
ipcMain.handle('next-track', () => controller.next());
|
|
95
|
+
ipcMain.handle('previous-track', () => controller.previous());
|
|
96
|
+
ipcMain.handle('set-volume', (_, value) => controller.setVolume(value));
|
|
97
|
+
ipcMain.handle('volume-up', (_, step) => controller.volumeUp(step));
|
|
98
|
+
ipcMain.handle('volume-down', (_, step) => controller.volumeDown(step));
|
|
99
|
+
ipcMain.handle('toggle-mute', () => controller.toggleMute());
|
|
100
|
+
|
|
101
|
+
// Send track updates to renderer
|
|
102
|
+
controller.on('track', (track) => {
|
|
103
|
+
mainWindow.webContents.send('track-update', track);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
controller.on('volume', (volume) => {
|
|
107
|
+
mainWindow.webContents.send('volume-update', volume);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Cleanup on quit
|
|
111
|
+
app.on('before-quit', async () => {
|
|
112
|
+
await controller.stop();
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Production Build with electron-builder
|
|
117
|
+
|
|
118
|
+
Add to your `package.json`:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"build": {
|
|
123
|
+
"asarUnpack": [
|
|
124
|
+
"node_modules/yandex-music-desktop-library/bin/**/*"
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This ensures the C# executable is unpacked from the ASAR archive so it can be spawned.
|
|
131
|
+
|
|
132
|
+
## đĄ MQTT / ESP32 Integration
|
|
133
|
+
|
|
134
|
+
Perfect for DIY hardware controllers:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { YandexMusicController } from 'yandex-music-desktop-library';
|
|
138
|
+
import mqtt from 'mqtt';
|
|
139
|
+
|
|
140
|
+
const controller = new YandexMusicController();
|
|
141
|
+
const client = mqtt.connect('mqtt://your-esp32-ip');
|
|
142
|
+
|
|
143
|
+
// Start controller
|
|
144
|
+
await controller.start();
|
|
145
|
+
|
|
146
|
+
// Send track data to ESP32
|
|
147
|
+
controller.on('track', (track) => {
|
|
148
|
+
if (track) {
|
|
149
|
+
client.publish('media/current', JSON.stringify({
|
|
150
|
+
title: track.title,
|
|
151
|
+
artist: track.artist,
|
|
152
|
+
album: track.album,
|
|
153
|
+
volume: track.volume,
|
|
154
|
+
isMuted: track.isMuted,
|
|
155
|
+
status: track.playbackStatus,
|
|
156
|
+
hasThumbnail: !!track.thumbnailBase64
|
|
157
|
+
}));
|
|
158
|
+
|
|
159
|
+
// Send thumbnail separately (it's large)
|
|
160
|
+
if (track.thumbnailBase64) {
|
|
161
|
+
client.publish('media/thumbnail', track.thumbnailBase64);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Receive commands from ESP32
|
|
167
|
+
client.subscribe('esp32/commands');
|
|
168
|
+
client.on('message', (topic, message) => {
|
|
169
|
+
const cmd = message.toString();
|
|
170
|
+
|
|
171
|
+
switch (cmd) {
|
|
172
|
+
case 'playpause':
|
|
173
|
+
controller.playPause();
|
|
174
|
+
break;
|
|
175
|
+
case 'next':
|
|
176
|
+
controller.next();
|
|
177
|
+
break;
|
|
178
|
+
case 'prev':
|
|
179
|
+
controller.previous();
|
|
180
|
+
break;
|
|
181
|
+
case 'vol_up':
|
|
182
|
+
controller.volumeUp(5);
|
|
183
|
+
break;
|
|
184
|
+
case 'vol_down':
|
|
185
|
+
controller.volumeDown(5);
|
|
186
|
+
break;
|
|
187
|
+
case 'mute':
|
|
188
|
+
controller.toggleMute();
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## đ API Reference
|
|
195
|
+
|
|
196
|
+
### ControllerOptions
|
|
197
|
+
|
|
198
|
+
| Option | Type | Default | Description |
|
|
199
|
+
|--------|------|---------|-------------|
|
|
200
|
+
| `thumbnailSize` | `number` | `150` | Thumbnail size in pixels (1-1000) |
|
|
201
|
+
| `thumbnailQuality` | `number` | `85` | JPEG quality (1-100) |
|
|
202
|
+
| `autoRestart` | `boolean` | `true` | Auto-restart on crash |
|
|
203
|
+
| `restartDelay` | `number` | `1000` | Restart delay in milliseconds |
|
|
204
|
+
|
|
205
|
+
### TrackData
|
|
206
|
+
|
|
207
|
+
| Property | Type | Description |
|
|
208
|
+
|----------|------|-------------|
|
|
209
|
+
| `id` | `string` | Session unique identifier |
|
|
210
|
+
| `appId` | `string` | Application identifier |
|
|
211
|
+
| `appName` | `string` | Human-readable app name |
|
|
212
|
+
| `title` | `string` | Track title |
|
|
213
|
+
| `artist` | `string` | Track artist |
|
|
214
|
+
| `album` | `string` | Album name |
|
|
215
|
+
| `playbackStatus` | `'Playing' \| 'Paused' \| 'Stopped' \| 'Unknown'` | Current playback status |
|
|
216
|
+
| `thumbnailBase64` | `string \| null` | Base64-encoded JPEG thumbnail |
|
|
217
|
+
| `isFocused` | `boolean` | Whether this is the focused session |
|
|
218
|
+
| `volume` | `number` | System volume level (0-100) |
|
|
219
|
+
| `isMuted` | `boolean` | Whether volume is muted |
|
|
220
|
+
|
|
221
|
+
### YandexMusicController
|
|
222
|
+
|
|
223
|
+
#### Methods
|
|
224
|
+
|
|
225
|
+
| Method | Returns | Description |
|
|
226
|
+
|--------|---------|-------------|
|
|
227
|
+
| `start()` | `Promise<void>` | Start the controller |
|
|
228
|
+
| `stop()` | `Promise<void>` | Stop the controller |
|
|
229
|
+
| `isRunning()` | `boolean` | Check if controller is active |
|
|
230
|
+
| `play()` | `Promise<void>` | Start playback |
|
|
231
|
+
| `pause()` | `Promise<void>` | Pause playback |
|
|
232
|
+
| `playPause()` | `Promise<void>` | Toggle play/pause |
|
|
233
|
+
| `next()` | `Promise<void>` | Skip to next track |
|
|
234
|
+
| `previous()` | `Promise<void>` | Skip to previous track |
|
|
235
|
+
| `volumeUp(stepPercent?)` | `Promise<void>` | Increase volume (default step: 3) |
|
|
236
|
+
| `volumeDown(stepPercent?)` | `Promise<void>` | Decrease volume (default step: 3) |
|
|
237
|
+
| `setVolume(value)` | `Promise<void>` | Set volume to specific value (0-100) |
|
|
238
|
+
| `toggleMute()` | `Promise<void>` | Toggle mute state |
|
|
239
|
+
|
|
240
|
+
#### Events
|
|
241
|
+
|
|
242
|
+
| Event | Payload | Description |
|
|
243
|
+
|-------|---------|-------------|
|
|
244
|
+
| `track` | `TrackData \| null` | Emitted when track changes or Yandex Music state changes |
|
|
245
|
+
| `volume` | `{ volume: number, isMuted: boolean }` | Emitted when system volume changes |
|
|
246
|
+
| `error` | `Error` | Emitted when an error occurs |
|
|
247
|
+
| `exit` | `number \| null` | Emitted when the controller process exits |
|
|
248
|
+
|
|
249
|
+
## đ ī¸ Development
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Clone repository
|
|
253
|
+
git clone https://github.com/WebAFilippov/yandexMusic-desktop-library.git
|
|
254
|
+
cd yandexMusic-desktop-library
|
|
255
|
+
|
|
256
|
+
# Install dependencies
|
|
257
|
+
npm install
|
|
258
|
+
|
|
259
|
+
# Build TypeScript
|
|
260
|
+
npm run build
|
|
261
|
+
|
|
262
|
+
# The C# executable will be automatically built and included in bin/win-x64/
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## đ License
|
|
266
|
+
|
|
267
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
268
|
+
|
|
269
|
+
## đ¤ Contributing
|
|
270
|
+
|
|
271
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
272
|
+
|
|
273
|
+
## đ Issues
|
|
274
|
+
|
|
275
|
+
If you find a bug, please [create an issue](https://github.com/WebAFilippov/yandexMusic-desktop-library/issues) with:
|
|
276
|
+
- Windows version
|
|
277
|
+
- Node.js version
|
|
278
|
+
- Steps to reproduce
|
|
279
|
+
- Error messages (if any)
|
|
280
|
+
|
|
281
|
+
## đĄ Related Projects
|
|
282
|
+
|
|
283
|
+
- [YandexMusicController](https://github.com/WebAFilippov/af-csharp-yandexMusic) - The C# backend service
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
**Note**: This package is for Windows only and requires the Yandex Music desktop application to be installed.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { ControllerOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Controller for Yandex Music desktop application on Windows.
|
|
5
|
+
* Provides both event-based and promise-based APIs for controlling playback and volume.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { YandexMusicController } from 'yandex-music-desktop-library';
|
|
10
|
+
*
|
|
11
|
+
* const controller = new YandexMusicController({
|
|
12
|
+
* thumbnailSize: 200,
|
|
13
|
+
* thumbnailQuality: 90,
|
|
14
|
+
* autoRestart: true
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Event-based API
|
|
18
|
+
* controller.on('track', (track) => {
|
|
19
|
+
* console.log(`Now playing: ${track?.title} by ${track?.artist}`);
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* await controller.start();
|
|
23
|
+
* await controller.playPause();
|
|
24
|
+
* await controller.setVolume(75);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class YandexMusicController extends EventEmitter {
|
|
28
|
+
private process?;
|
|
29
|
+
private options;
|
|
30
|
+
private restartTimer?;
|
|
31
|
+
private isStarted;
|
|
32
|
+
private executablePath;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new YandexMusicController instance
|
|
35
|
+
* @param options - Configuration options
|
|
36
|
+
*/
|
|
37
|
+
constructor(options?: ControllerOptions);
|
|
38
|
+
private clamp;
|
|
39
|
+
/**
|
|
40
|
+
* Starts the controller and spawns the C# process
|
|
41
|
+
* @returns Promise that resolves when controller is ready
|
|
42
|
+
* @throws Error if controller is already running or fails to start
|
|
43
|
+
*/
|
|
44
|
+
start(): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Stops the controller and kills the C# process
|
|
47
|
+
* @returns Promise that resolves when process is terminated
|
|
48
|
+
*/
|
|
49
|
+
stop(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Checks if the controller is currently running
|
|
52
|
+
* @returns true if process is active
|
|
53
|
+
*/
|
|
54
|
+
isRunning(): boolean;
|
|
55
|
+
private sendCommand;
|
|
56
|
+
/**
|
|
57
|
+
* Start playback
|
|
58
|
+
*/
|
|
59
|
+
play(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Pause playback
|
|
62
|
+
*/
|
|
63
|
+
pause(): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Toggle between play and pause
|
|
66
|
+
*/
|
|
67
|
+
playPause(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Skip to next track
|
|
70
|
+
*/
|
|
71
|
+
next(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Skip to previous track
|
|
74
|
+
*/
|
|
75
|
+
previous(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Increase volume by specified step
|
|
78
|
+
* @param stepPercent - Percentage to increase (default: 3)
|
|
79
|
+
*/
|
|
80
|
+
volumeUp(stepPercent?: number): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Decrease volume by specified step
|
|
83
|
+
* @param stepPercent - Percentage to decrease (default: 3)
|
|
84
|
+
*/
|
|
85
|
+
volumeDown(stepPercent?: number): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Set volume to specific value
|
|
88
|
+
* @param value - Volume level 0-100
|
|
89
|
+
*/
|
|
90
|
+
setVolume(value: number): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Toggle mute state
|
|
93
|
+
*/
|
|
94
|
+
toggleMute(): Promise<void>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,OAAO,EAAE,iBAAiB,EAAyB,MAAM,YAAY,CAAC;AAgBtE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,qBAAsB,SAAQ,YAAY;IACrD,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAS;IAE/B;;;OAGG;gBACS,OAAO,GAAE,iBAAsB;IAY3C,OAAO,CAAC,KAAK;IAIb;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+F5B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC3B;;;OAGG;IACH,SAAS,IAAI,OAAO;YAIN,WAAW;IAmBzB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;;OAGG;IACG,QAAQ,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAOtD;;;OAGG;IACG,UAAU,CAAC,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD;;;OAGG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAGlC"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { createInterface } from 'readline';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
/**
|
|
10
|
+
* Controller for Yandex Music desktop application on Windows.
|
|
11
|
+
* Provides both event-based and promise-based APIs for controlling playback and volume.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { YandexMusicController } from 'yandex-music-desktop-library';
|
|
16
|
+
*
|
|
17
|
+
* const controller = new YandexMusicController({
|
|
18
|
+
* thumbnailSize: 200,
|
|
19
|
+
* thumbnailQuality: 90,
|
|
20
|
+
* autoRestart: true
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Event-based API
|
|
24
|
+
* controller.on('track', (track) => {
|
|
25
|
+
* console.log(`Now playing: ${track?.title} by ${track?.artist}`);
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* await controller.start();
|
|
29
|
+
* await controller.playPause();
|
|
30
|
+
* await controller.setVolume(75);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class YandexMusicController extends EventEmitter {
|
|
34
|
+
process;
|
|
35
|
+
options;
|
|
36
|
+
restartTimer;
|
|
37
|
+
isStarted = false;
|
|
38
|
+
executablePath;
|
|
39
|
+
/**
|
|
40
|
+
* Creates a new YandexMusicController instance
|
|
41
|
+
* @param options - Configuration options
|
|
42
|
+
*/
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
super();
|
|
45
|
+
this.options = {
|
|
46
|
+
thumbnailSize: this.clamp(options.thumbnailSize ?? 150, 1, 1000),
|
|
47
|
+
thumbnailQuality: this.clamp(options.thumbnailQuality ?? 85, 1, 100),
|
|
48
|
+
autoRestart: options.autoRestart ?? true,
|
|
49
|
+
restartDelay: Math.max(options.restartDelay ?? 1000, 0)
|
|
50
|
+
};
|
|
51
|
+
this.executablePath = join(__dirname, '..', 'bin', 'win-x64', 'YandexMusicController.exe');
|
|
52
|
+
}
|
|
53
|
+
clamp(value, min, max) {
|
|
54
|
+
return Math.min(Math.max(value, min), max);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Starts the controller and spawns the C# process
|
|
58
|
+
* @returns Promise that resolves when controller is ready
|
|
59
|
+
* @throws Error if controller is already running or fails to start
|
|
60
|
+
*/
|
|
61
|
+
async start() {
|
|
62
|
+
if (this.isRunning()) {
|
|
63
|
+
throw new Error('Controller is already running');
|
|
64
|
+
}
|
|
65
|
+
// Verify executable exists
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(this.executablePath);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new Error(`Executable not found at: ${this.executablePath}`);
|
|
71
|
+
}
|
|
72
|
+
const args = [
|
|
73
|
+
`--thumbnail-size=${this.options.thumbnailSize}`,
|
|
74
|
+
`--thumbnail-quality=${this.options.thumbnailQuality}`
|
|
75
|
+
];
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
try {
|
|
78
|
+
this.process = spawn(this.executablePath, args, {
|
|
79
|
+
windowsHide: true,
|
|
80
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
81
|
+
});
|
|
82
|
+
this.process.on('error', (err) => {
|
|
83
|
+
this.emit('error', err);
|
|
84
|
+
reject(err);
|
|
85
|
+
});
|
|
86
|
+
this.process.on('exit', (code) => {
|
|
87
|
+
this.isStarted = false;
|
|
88
|
+
this.emit('exit', code);
|
|
89
|
+
if (this.options.autoRestart && code !== 0 && !this.restartTimer) {
|
|
90
|
+
this.restartTimer = setTimeout(() => {
|
|
91
|
+
this.restartTimer = undefined;
|
|
92
|
+
this.start().catch(() => {
|
|
93
|
+
// Auto-restart failed, emit error
|
|
94
|
+
this.emit('error', new Error('Auto-restart failed'));
|
|
95
|
+
});
|
|
96
|
+
}, this.options.restartDelay);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
// Handle stdout for JSON messages
|
|
100
|
+
if (this.process.stdout) {
|
|
101
|
+
const rl = createInterface({
|
|
102
|
+
input: this.process.stdout,
|
|
103
|
+
crlfDelay: Infinity
|
|
104
|
+
});
|
|
105
|
+
rl.on('line', (line) => {
|
|
106
|
+
try {
|
|
107
|
+
const msg = JSON.parse(line);
|
|
108
|
+
if (msg.type === 'session') {
|
|
109
|
+
this.emit('track', msg.data);
|
|
110
|
+
// Also emit volume event when track data changes
|
|
111
|
+
if (msg.data) {
|
|
112
|
+
this.emit('volume', {
|
|
113
|
+
volume: msg.data.volume,
|
|
114
|
+
isMuted: msg.data.isMuted
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore non-JSON lines (debug output)
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// Handle stderr for errors
|
|
125
|
+
if (this.process.stderr) {
|
|
126
|
+
const rl = createInterface({
|
|
127
|
+
input: this.process.stderr,
|
|
128
|
+
crlfDelay: Infinity
|
|
129
|
+
});
|
|
130
|
+
rl.on('line', (line) => {
|
|
131
|
+
this.emit('error', new Error(line));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Wait a moment for process to initialize
|
|
135
|
+
setTimeout(() => {
|
|
136
|
+
this.isStarted = true;
|
|
137
|
+
resolve();
|
|
138
|
+
}, 500);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Stops the controller and kills the C# process
|
|
147
|
+
* @returns Promise that resolves when process is terminated
|
|
148
|
+
*/
|
|
149
|
+
async stop() {
|
|
150
|
+
if (!this.isRunning()) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Clear restart timer
|
|
154
|
+
if (this.restartTimer) {
|
|
155
|
+
clearTimeout(this.restartTimer);
|
|
156
|
+
this.restartTimer = undefined;
|
|
157
|
+
}
|
|
158
|
+
return new Promise((resolve) => {
|
|
159
|
+
if (!this.process) {
|
|
160
|
+
resolve();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Send close command
|
|
164
|
+
this.sendCommand({ command: 'close' });
|
|
165
|
+
// Wait for graceful exit
|
|
166
|
+
const timeout = setTimeout(() => {
|
|
167
|
+
this.process?.kill();
|
|
168
|
+
this.isStarted = false;
|
|
169
|
+
resolve();
|
|
170
|
+
}, 1000);
|
|
171
|
+
this.process.on('exit', () => {
|
|
172
|
+
clearTimeout(timeout);
|
|
173
|
+
this.isStarted = false;
|
|
174
|
+
resolve();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Checks if the controller is currently running
|
|
180
|
+
* @returns true if process is active
|
|
181
|
+
*/
|
|
182
|
+
isRunning() {
|
|
183
|
+
return this.isStarted && this.process !== undefined && !this.process.killed;
|
|
184
|
+
}
|
|
185
|
+
async sendCommand(cmd) {
|
|
186
|
+
if (!this.isRunning()) {
|
|
187
|
+
throw new Error('Controller is not running. Call start() first.');
|
|
188
|
+
}
|
|
189
|
+
return new Promise((resolve, reject) => {
|
|
190
|
+
const message = JSON.stringify(cmd) + '\n';
|
|
191
|
+
this.process.stdin.write(message, (err) => {
|
|
192
|
+
if (err) {
|
|
193
|
+
reject(err);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
resolve();
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// Playback Controls
|
|
202
|
+
/**
|
|
203
|
+
* Start playback
|
|
204
|
+
*/
|
|
205
|
+
async play() {
|
|
206
|
+
return this.sendCommand({ command: 'play' });
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Pause playback
|
|
210
|
+
*/
|
|
211
|
+
async pause() {
|
|
212
|
+
return this.sendCommand({ command: 'pause' });
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Toggle between play and pause
|
|
216
|
+
*/
|
|
217
|
+
async playPause() {
|
|
218
|
+
return this.sendCommand({ command: 'playpause' });
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Skip to next track
|
|
222
|
+
*/
|
|
223
|
+
async next() {
|
|
224
|
+
return this.sendCommand({ command: 'next' });
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Skip to previous track
|
|
228
|
+
*/
|
|
229
|
+
async previous() {
|
|
230
|
+
return this.sendCommand({ command: 'previous' });
|
|
231
|
+
}
|
|
232
|
+
// Volume Controls
|
|
233
|
+
/**
|
|
234
|
+
* Increase volume by specified step
|
|
235
|
+
* @param stepPercent - Percentage to increase (default: 3)
|
|
236
|
+
*/
|
|
237
|
+
async volumeUp(stepPercent = 3) {
|
|
238
|
+
return this.sendCommand({
|
|
239
|
+
command: 'volume_up',
|
|
240
|
+
stepPercent: this.clamp(stepPercent, 1, 100)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Decrease volume by specified step
|
|
245
|
+
* @param stepPercent - Percentage to decrease (default: 3)
|
|
246
|
+
*/
|
|
247
|
+
async volumeDown(stepPercent = 3) {
|
|
248
|
+
return this.sendCommand({
|
|
249
|
+
command: 'volume_down',
|
|
250
|
+
stepPercent: this.clamp(stepPercent, 1, 100)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Set volume to specific value
|
|
255
|
+
* @param value - Volume level 0-100
|
|
256
|
+
*/
|
|
257
|
+
async setVolume(value) {
|
|
258
|
+
return this.sendCommand({
|
|
259
|
+
command: 'set_volume',
|
|
260
|
+
value: this.clamp(value, 0, 100)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Toggle mute state
|
|
265
|
+
*/
|
|
266
|
+
async toggleMute() {
|
|
267
|
+
return this.sendCommand({ command: 'toggle_mute' });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yandex Music Desktop Library
|
|
3
|
+
*
|
|
4
|
+
* TypeScript library for controlling Yandex Music desktop application on Windows.
|
|
5
|
+
* Provides event-based and promise-based APIs for media control and volume management.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { YandexMusicController } from './controller.js';
|
|
10
|
+
export type { ControllerOptions, TrackData, VolumeData, ControllerEvents } from './types.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,gBAAgB,EACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yandex Music Desktop Library
|
|
3
|
+
*
|
|
4
|
+
* TypeScript library for controlling Yandex Music desktop application on Windows.
|
|
5
|
+
* Provides event-based and promise-based APIs for media control and volume management.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { YandexMusicController } from './controller.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for configuring the YandexMusicController
|
|
3
|
+
*/
|
|
4
|
+
export interface ControllerOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Thumbnail size in pixels. Must be between 1 and 1000.
|
|
7
|
+
* @default 150
|
|
8
|
+
*/
|
|
9
|
+
thumbnailSize?: number;
|
|
10
|
+
/**
|
|
11
|
+
* JPEG quality for thumbnails. Must be between 1 and 100.
|
|
12
|
+
* @default 85
|
|
13
|
+
*/
|
|
14
|
+
thumbnailQuality?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to automatically restart the controller if it crashes.
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
autoRestart?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Delay in milliseconds before auto-restart.
|
|
22
|
+
* @default 1000
|
|
23
|
+
*/
|
|
24
|
+
restartDelay?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Data about the currently playing track
|
|
28
|
+
*/
|
|
29
|
+
export interface TrackData {
|
|
30
|
+
/**
|
|
31
|
+
* Unique identifier for the session
|
|
32
|
+
*/
|
|
33
|
+
id: string;
|
|
34
|
+
/**
|
|
35
|
+
* Application identifier
|
|
36
|
+
*/
|
|
37
|
+
appId: string;
|
|
38
|
+
/**
|
|
39
|
+
* Human-readable application name
|
|
40
|
+
*/
|
|
41
|
+
appName: string;
|
|
42
|
+
/**
|
|
43
|
+
* Track title
|
|
44
|
+
*/
|
|
45
|
+
title: string;
|
|
46
|
+
/**
|
|
47
|
+
* Track artist
|
|
48
|
+
*/
|
|
49
|
+
artist: string;
|
|
50
|
+
/**
|
|
51
|
+
* Album name
|
|
52
|
+
*/
|
|
53
|
+
album: string;
|
|
54
|
+
/**
|
|
55
|
+
* Current playback status
|
|
56
|
+
*/
|
|
57
|
+
playbackStatus: 'Playing' | 'Paused' | 'Stopped' | 'Unknown';
|
|
58
|
+
/**
|
|
59
|
+
* Base64-encoded thumbnail image (JPEG), or null if not available
|
|
60
|
+
*/
|
|
61
|
+
thumbnailBase64: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Whether this is the currently focused media session
|
|
64
|
+
*/
|
|
65
|
+
isFocused: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Current system volume level (0-100)
|
|
68
|
+
*/
|
|
69
|
+
volume: number;
|
|
70
|
+
/**
|
|
71
|
+
* Whether the system volume is muted
|
|
72
|
+
*/
|
|
73
|
+
isMuted: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Volume change event data
|
|
77
|
+
*/
|
|
78
|
+
export interface VolumeData {
|
|
79
|
+
/**
|
|
80
|
+
* Current volume level (0-100)
|
|
81
|
+
*/
|
|
82
|
+
volume: number;
|
|
83
|
+
/**
|
|
84
|
+
* Whether volume is muted
|
|
85
|
+
*/
|
|
86
|
+
isMuted: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Events emitted by YandexMusicController
|
|
90
|
+
*/
|
|
91
|
+
export interface ControllerEvents {
|
|
92
|
+
/**
|
|
93
|
+
* Emitted when track information changes or when Yandex Music state changes
|
|
94
|
+
*/
|
|
95
|
+
track: [data: TrackData | null];
|
|
96
|
+
/**
|
|
97
|
+
* Emitted when system volume changes
|
|
98
|
+
*/
|
|
99
|
+
volume: [data: VolumeData];
|
|
100
|
+
/**
|
|
101
|
+
* Emitted when an error occurs
|
|
102
|
+
*/
|
|
103
|
+
error: [error: Error];
|
|
104
|
+
/**
|
|
105
|
+
* Emitted when the controller process exits
|
|
106
|
+
*/
|
|
107
|
+
exit: [code: number | null];
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,cAAc,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAE7D;;OAEG;IACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;IAEhC;;OAEG;IACH,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAE3B;;OAEG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtB;;OAEG;IACH,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CAC7B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "yandex-music-desktop-library",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript library for controlling Yandex Music desktop application on Windows",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=14.0.0"
|
|
10
|
+
},
|
|
11
|
+
"os": [
|
|
12
|
+
"win32"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
17
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"yandex",
|
|
21
|
+
"music",
|
|
22
|
+
"desktop",
|
|
23
|
+
"windows",
|
|
24
|
+
"media",
|
|
25
|
+
"controller",
|
|
26
|
+
"typescript"
|
|
27
|
+
],
|
|
28
|
+
"author": "WebAFilippov",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/WebAFilippov/yandexMusic-desktop-library.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/WebAFilippov/yandexMusic-desktop-library/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/WebAFilippov/yandexMusic-desktop-library#readme",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^20.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist/**/*",
|
|
44
|
+
"bin/**/*",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
]
|
|
48
|
+
}
|