├── .editorconfig
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .npmignore
├── .prettierrc
├── LICENSE
├── README.md
├── lib
└── index.ts
├── package-lock.json
├── package.json
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | insert_final_newline = false
8 | trim_trailing_whitespace = true
9 |
10 | [*.{js,json,ts}]
11 | indent_style = space
12 | indent_size = 2
13 |
14 | [*.{md,markdown}]
15 | trim_trailing_whitespace = false
16 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "parser": "@typescript-eslint/parser",
7 | "parserOptions": {
8 | "ecmaVersion": 2018,
9 | "sourceType": "module",
10 | "project": "./tsconfig.json",
11 | "tsconfigRootDir": "./",
12 | "ecmaFeatures": {
13 | "jsx": false
14 | }
15 | },
16 | "extends": ["standard", "prettier", "prettier/@typescript-eslint"],
17 | "plugins": ["prettier", "@typescript-eslint"],
18 | "globals": {
19 | "Atomics": "readonly",
20 | "SharedArrayBuffer": "readonly"
21 | },
22 | "settings": {
23 | "import/resolver": {
24 | "typescript": {}
25 | }
26 | },
27 | "rules": {
28 | "prettier/prettier": "error",
29 | "@typescript-eslint/no-unused-vars": "error"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=lf
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS
2 | .DS_Store
3 | .DS_Store?
4 | ._*
5 | .Spotlight-V100
6 | .Trashes
7 | ehthumbs.db
8 | Thumbs.db
9 |
10 | # IDE
11 | nbproject
12 | .~lock.*
13 | .buildpath
14 | .idea
15 | .project
16 | .settings
17 | .vscode
18 | composer.lock
19 | *.sublime-workspace
20 | *.swp
21 | *.swo
22 |
23 | # Project
24 | *.log
25 | *.cache
26 | /.eslintcache
27 | /.nyc_output
28 | /.stryker-tmp
29 | /node_modules
30 | /dist
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | .editorconfig
4 | .eslintrc.json
5 | .gitattributes
6 | .gitignore
7 | .prettierrc
8 | tsconfig.json
9 | *.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ludwig DUBOS
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![Stargazers][stars-shield]][stars-url] [![Downloads][downloads-shield]][downloads-url] [![Issues][issues-shield]][issues-url] [![Vulnerabilities][vulnerabilities-shield]][vulnerabilities-url] [![MIT License][license-shield]][license-url]
2 |
3 |
4 |
5 |
node-vlc-http
6 |
7 |
8 | ## Install:
9 |
10 | npm install -s node-vlc-http
11 |
12 | or
13 |
14 | yarn add node-vlc-http
15 |
16 | ## Usage:
17 |
18 | ### Basics
19 |
20 | ```js
21 | const { VLC } = require('node-vlc-http');
22 |
23 | const vlc = new VLC({
24 | host,
25 | port,
26 | username,
27 | password,
28 | // update automatically status and playlist of VLC, default true.
29 | autoUpdate,
30 | // how many times per seconds (in ms) node-vlc-http will update the status of VLC, default 1000/30 ~ 33ms (30fps)
31 | tickLengthMs,
32 | // checks that browse, status and playlist have changed since the last update of one of its elements,
33 | // if it the case fire browsechange, statuschange or playlistchange event. default true.
34 | changeEvents,
35 | // max tries at the first connection before throwing an error set it to -1 for infinite try, default 3
36 | maxTries,
37 | // interval between each try in ms, default 1000
38 | triesInterval
39 | });
40 |
41 | // update status and playlist at the same time
42 | vlc.updateAll()
43 | .then(results => {
44 | const [status, playlist] = results
45 | })
46 | .catch(console.error)
47 |
48 | vlc.browse(path)
49 | .then(browse => {
50 | // do stuff
51 | })
52 | .catch(console.error)
53 |
54 | // update status
55 | vlc.updateStatus()
56 | .then(status => {
57 | // do stuff
58 | })
59 | .catch(console.error)
60 |
61 | // update playlist
62 | vlc.updatePlaylist()
63 | .then(playlist => {
64 | // do stuff
65 | })
66 | .catch(console.error)
67 | ```
68 |
69 | ### Events
70 |
71 | ```js
72 | vlc.on('tick', (delta) => {
73 | // do stuff
74 | });
75 |
76 | vlc.on('update', (status, playlist) => {
77 | // do stuff
78 | });
79 |
80 | vlc.on(
81 | 'statuschange',
82 | (prev, next) => {
83 | // do stuff
84 | }
85 | );
86 |
87 | vlc.on(
88 | 'playlistchange',
89 | (prev, next) => {
90 | // do stuff
91 | }
92 | );
93 |
94 | vlc.on('error', (err: Error) => {
95 | // do stuff
96 | });
97 |
98 | vlc.on('connect', () => {
99 | // do stuff
100 | })
101 | ```
102 |
103 | ### Actions
104 |
105 | ```js
106 | // Add media to playlist
107 | vlc.addToQueue(uri)
108 |
109 | // Add media to playlist and play
110 | vlc.addToQueueAndPlay(uri)
111 |
112 | // Add media to playlist and play without audio
113 | vlc.addToQueueAndPlay(uri, 'noaudio')
114 |
115 | // Add media to playlist and play without video
116 | vlc.addToQueueAndPlay(uri, 'novideo')
117 |
118 | // Add subtitle to currently playing file
119 | vlc.addSubtitle(uri)
120 |
121 | // Play video (id)
122 | vlc.play(id)
123 |
124 | // Pause video (id)
125 | vlc.pause(id)
126 |
127 | // Stop playback
128 | vlc.stop()
129 |
130 | // Resume playback
131 | vlc.resume()
132 |
133 | // Pause playback, do nothing if state was 'paused'
134 | vlc.forcePause()
135 |
136 | // Jump to next item in playlist
137 | vlc.playlistNext()
138 |
139 | // Jump to previous item in playlist
140 | vlc.playlistPrevious()
141 |
142 | // Delete item (id) from playlist
143 | vlc.playlistDelete(id)
144 |
145 | // Empty playlist
146 | vlc.playlistEmpty()
147 |
148 | // Sort playlist using sort mode and order
149 | // order 0 sort by asc and 1 sort by dsc
150 | // mode 0 sort by id, 1 sort by name, 3 sort by author, 5 sort by random and
151 | // 7 by track number
152 | vlc.sortPlaylist(order, mode)
153 |
154 | // Toggle random playback
155 | vlc.toggleRandom()
156 |
157 | // Toggle loop
158 | vlc.toggleLoop()
159 |
160 | // Toggle repeat
161 | vlc.toggleRepeat()
162 |
163 | // Toggle fullscreen
164 | vlc.toggleFullscreen()
165 |
166 | // Seek to time
167 | vlc.seek(time)
168 |
169 | // Seek to chapter
170 | vlc.seekToChapter(chapter)
171 | ```
172 |
173 | ## License:
174 |
175 | [MIT](./LICENSE)
176 |
177 | [downloads-shield]: https://img.shields.io/npm/dt/node-vlc-http.svg?style=flat-square
178 | [downloads-url]: https://www.npmjs.com/package/node-vlc-http
179 | [vulnerabilities-shield]: https://snyk.io/test/github/ldubos/node-vlc-http/badge.svg?targetFile=package.json&style=flat-square
180 | [vulnerabilities-url]: https://snyk.io/test/github/ldubos/node-vlc-http?targetFile=package.json
181 | [stars-shield]: https://img.shields.io/github/stars/ldubos/node-vlc-http.svg?style=flat-square
182 | [stars-url]: https://github.com/ldubos/node-vlc-http/stargazers
183 | [issues-shield]: https://img.shields.io/github/issues/ldubos/node-vlc-http.svg?style=flat-square
184 | [issues-url]: https://github.com/ldubos/node-vlc-http/issues
185 | [license-shield]: https://img.shields.io/github/license/ldubos/node-vlc-http.svg?style=flat-square
186 | [license-url]: https://github.com/ldubos/node-vlc-http/blob/master/LICENSE
--------------------------------------------------------------------------------
/lib/index.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 | import { URL } from 'url';
3 | import fs from 'fs';
4 | import http from 'http';
5 | import querystring from 'querystring';
6 |
7 | export interface Information {
8 | chapter: number;
9 | chapters: number[];
10 | title: number;
11 | category: Category;
12 | titles: number[];
13 | }
14 |
15 | export interface Category {
16 | meta: Meta;
17 | [flux: string]: { [key: string]: string } | Meta;
18 | }
19 |
20 | export interface Flux {
21 | [flux: string]: string;
22 | }
23 |
24 | export interface Audiofilters {
25 | [filter: string]: string;
26 | }
27 |
28 | export interface Meta {
29 | // eslint-disable-next-line camelcase
30 | encoded_by: string;
31 | filename: string;
32 | }
33 |
34 | export interface Videoeffects {
35 | hue: number;
36 | saturation: number;
37 | contrast: number;
38 | brightness: number;
39 | gamma: number;
40 | }
41 |
42 | type Stats = { [key: string]: number };
43 |
44 | type AspectRatio =
45 | | '1:1'
46 | | '4:3'
47 | | '5:4'
48 | | '16:9'
49 | | '16:10'
50 | | '221:100'
51 | | '235:100'
52 | | '239:100';
53 |
54 | type State = 'paused' | 'playing' | 'stopped';
55 |
56 | type StatusBase = {
57 | fullscreen: boolean;
58 | stats: Stats | null;
59 | aspectratio: AspectRatio | null;
60 | audiodelay: number;
61 | apiversion: number;
62 | currentplid: number;
63 | time: number;
64 | volume: number;
65 | length: number;
66 | random: boolean;
67 | audiofilters: Audiofilters;
68 | rate: number;
69 | videoeffects: Videoeffects;
70 | state: State;
71 | loop: boolean;
72 | version: string;
73 | position: number;
74 | information: Information;
75 | repeat: boolean;
76 | subtitledelay: number;
77 | equalizer: any[];
78 | };
79 |
80 | type StatusPaused = StatusBase & {
81 | stats: Stats;
82 | aspectratio: AspectRatio;
83 | state: 'paused';
84 | };
85 |
86 | type StatusPlaying = StatusBase & {
87 | stats: Stats;
88 | aspectratio: AspectRatio;
89 | state: 'playing';
90 | };
91 |
92 | type StatusStopped = StatusBase & {
93 | stats: null;
94 | aspectratio: null;
95 | state: 'stopped';
96 | };
97 |
98 | type Status = StatusPaused | StatusPlaying | StatusStopped;
99 |
100 | enum CommandScope {
101 | BROWSE = '/requests/browse.json',
102 | STATUS = '/requests/status.json',
103 | PLAYLIST = '/requests/playlist.json'
104 | }
105 |
106 | type BrowseElement = {
107 | type: 'dir' | 'file';
108 | path: string;
109 | name: string;
110 | uid: number;
111 | // eslint-disable-next-line camelcase
112 | creation_time: number;
113 | gid: number;
114 | // eslint-disable-next-line camelcase
115 | modification_time: number;
116 | mode: number;
117 | uri: string;
118 | size: number;
119 | };
120 |
121 | type Browse = {
122 | elements: BrowseElement[];
123 | };
124 |
125 | type PlaylistBase = {
126 | ro: 'rw' | 'ro';
127 | type: 'node' | 'leaf';
128 | name: string;
129 | id: string;
130 | children?: any[];
131 | duration?: number;
132 | uri?: string;
133 | };
134 |
135 | type PlaylistLeaf = PlaylistBase & {
136 | duration: number;
137 | uri: string;
138 | };
139 |
140 | type PlaylistNode = PlaylistBase & {
141 | children: (PlaylistNode | PlaylistLeaf)[];
142 | };
143 |
144 | type Playlist = PlaylistNode | PlaylistLeaf;
145 |
146 | const waitFor = (ms: number): Promise =>
147 | new Promise(resolve => setTimeout(resolve, ms));
148 |
149 | const pathlikeToString = (path: fs.PathLike) => {
150 | if (Buffer.isBuffer(path)) {
151 | return path.toString('utf8');
152 | } else if (path instanceof URL) {
153 | return path.href;
154 | }
155 |
156 | return path;
157 | };
158 |
159 | export type VLCOptions = {
160 | host?: string;
161 | port?: number;
162 | username: string;
163 | password: string;
164 | /** update automatically status and playlist of VLC, default true. */
165 | autoUpdate?: boolean;
166 | /** how many times per seconds (in ms) node-vlc-http will update the status of VLC, default 1000/30 ~ 33ms (30fps). */
167 | tickLengthMs?: number;
168 | /**
169 | * checks that browse, status and playlist have changed since the last update of one of its elements,
170 | * if it the case fire browsechange, statuschange or playlistchange event. default true.
171 | */
172 | changeEvents?: boolean;
173 | /** max tries at the first connection before throwing an error set it to -1 for infinite try, default 3 */
174 | maxTries?: number;
175 | /** interval between two try in ms, default 1000 */
176 | triesInterval?: number;
177 | };
178 |
179 | export declare interface VLC {
180 | on(event: 'tick', listener: (delta: number) => void): this;
181 | on(event: 'update', listener: (status: Status, playlist: any) => void): this;
182 | on(
183 | event: 'statuschange',
184 | listener: (prev: Status, next: Status) => void
185 | ): this;
186 | on(event: 'playlistchange', listener: (prev: any, next: any) => void): this;
187 | on(event: 'error', listener: (err: Error) => void): this;
188 | /** fired when connected */
189 | on(event: 'connect', listener: () => void): this;
190 | on(event: string | symbol, listener: (...args: any[]) => void): this;
191 | }
192 |
193 | const s2nano = 1e9;
194 | const ms2nano = 1e6;
195 | const nano2s = 1 / s2nano;
196 |
197 | function getNano(): number {
198 | const hrtime = process.hrtime();
199 | return +hrtime[0] * s2nano + +hrtime[1];
200 | }
201 |
202 | function get(options: http.RequestOptions): Promise {
203 | return new Promise((resolve, reject) => {
204 | const req = http.get(options, response => {
205 | let data = '';
206 |
207 | response.on('error', reject);
208 | response.on('data', chunk => (data += chunk));
209 | response.on('end', () => {
210 | if (response.statusCode !== 200) {
211 | return reject(
212 | new Error(
213 | `${response.statusCode} ${response.statusMessage || 'HTTPError'}`
214 | )
215 | );
216 | }
217 |
218 | try {
219 | resolve(JSON.parse(data));
220 | } catch (err) {
221 | reject(err);
222 | }
223 | });
224 | });
225 |
226 | req.on('error', reject);
227 | });
228 | }
229 |
230 | function isObject(obj: any) {
231 | if (typeof obj === 'object' && obj != null) {
232 | return true;
233 | } else {
234 | return false;
235 | }
236 | }
237 |
238 | function equal(a: any, b: any) {
239 | if (a === b) {
240 | return true;
241 | } else if (isObject(a) && isObject(b)) {
242 | if (Object.keys(a).length !== Object.keys(b).length) {
243 | return false;
244 | }
245 |
246 | for (var prop in a) {
247 | if (!equal(a[prop], b[prop])) {
248 | return false;
249 | }
250 | }
251 |
252 | return true;
253 | }
254 |
255 | return false;
256 | }
257 |
258 | export class VLC extends EventEmitter {
259 | private _host: string;
260 | private _port: number;
261 | private _autoUpdate = true;
262 | private _changeEvents = true;
263 | private _authorization: string;
264 | private _tickLengthMs: number;
265 | private _tickLengthNano: number;
266 | private _longWaitMs: number;
267 | private _longWaitNano: number;
268 | private _prev: number;
269 | private _target: number;
270 | private _status: Status = null as any;
271 | private _playlist: Playlist = null as any;
272 | private _maxTries: number = -1;
273 | private _triesInterval: number = 1000;
274 |
275 | constructor(options: VLCOptions) {
276 | super();
277 |
278 | this._host = options.host || '127.0.0.1';
279 | this._port = options.port || 8080;
280 |
281 | if (typeof options.autoUpdate === 'boolean') {
282 | this._autoUpdate = options.autoUpdate;
283 | }
284 |
285 | if (typeof options.changeEvents === 'boolean') {
286 | this._changeEvents = options.changeEvents;
287 | }
288 |
289 | this._tickLengthMs = options.tickLengthMs || 1000 / 30;
290 |
291 | // node leaks memory if setTimeout is called with a value less than 16
292 | if (this._tickLengthMs < 16) {
293 | this._tickLengthMs = 16;
294 | }
295 |
296 | this._maxTries = options.maxTries || 3;
297 | this._triesInterval = options.triesInterval || 1000;
298 |
299 | this._tickLengthNano = this._tickLengthMs * ms2nano;
300 | this._longWaitMs = Math.floor(this._tickLengthMs - 1);
301 | this._longWaitNano = this._longWaitMs * ms2nano;
302 | this._prev = getNano();
303 | this._target = this._prev;
304 |
305 | // generate authorization string
306 | this._authorization = `Basic ${Buffer.from(
307 | `${options.username}:${options.password}`
308 | ).toString('base64')}`;
309 |
310 | // check if VLC is up
311 | this._connect()
312 | .then(() => {
313 | this.emit('connect');
314 | if (this._autoUpdate) this._doTick();
315 | })
316 | .catch(this.emit.bind(this, 'error'));
317 | }
318 |
319 | private async _connect(): Promise {
320 | if (this._maxTries === 0) await this._sendCommand(CommandScope.STATUS);
321 | else if (this._maxTries === -1) {
322 | while (true) {
323 | try {
324 | await this._sendCommand(CommandScope.STATUS);
325 | } catch (_) {
326 | await waitFor(this._triesInterval);
327 | continue;
328 | }
329 |
330 | break;
331 | }
332 | } else {
333 | for (let i = 1; i < this._maxTries; i++) {
334 | try {
335 | await this._sendCommand(CommandScope.STATUS);
336 | } catch (_) {
337 | await waitFor(this._triesInterval);
338 | continue;
339 | }
340 |
341 | return;
342 | }
343 |
344 | await this._sendCommand(CommandScope.STATUS);
345 | }
346 | }
347 |
348 | private _doTick(): void {
349 | const now = getNano();
350 |
351 | if (now >= this._target) {
352 | const delta = (now - this._prev) * nano2s;
353 | this._prev = now;
354 | this._target = now + this._tickLengthNano;
355 |
356 | this.emit('tick', delta);
357 | this.updateAll().catch(err => this.emit('error', err));
358 | }
359 |
360 | const remainingInTick = this._target - getNano();
361 |
362 | if (remainingInTick > this._longWaitNano) {
363 | setTimeout(
364 | this._doTick.bind(this),
365 | Math.max(this._longWaitMs, this._tickLengthMs)
366 | );
367 | } else {
368 | setImmediate(this._doTick.bind(this));
369 | }
370 | }
371 |
372 | private async _sendCommand(
373 | scope: CommandScope,
374 | command?: string | null,
375 | options?: { [key: string]: any }
376 | ): Promise {
377 | let query = null;
378 |
379 | if (command) {
380 | query = querystring.stringify({ command, ...options });
381 | } else if (!command && query) {
382 | query = querystring.stringify(options);
383 | }
384 |
385 | return get({
386 | host: this._host,
387 | port: this._port,
388 | path: `${scope}${query ? `?${query}` : ''}`,
389 | headers: {
390 | Authorization: this._authorization
391 | }
392 | });
393 | }
394 |
395 | public async browse(path: string): Promise {
396 | const browse = this._sendCommand(CommandScope.BROWSE, null, {
397 | dir: path
398 | });
399 |
400 | return browse;
401 | }
402 |
403 | public async updateStatus(): Promise {
404 | const status = await this._sendCommand(CommandScope.STATUS);
405 |
406 | if (this._changeEvents && !equal(status, this._status)) {
407 | try {
408 | this.emit('statuschange', this._status || status, status);
409 | } catch (err) {
410 | this.emit('error', err);
411 | }
412 | this._status = status;
413 | }
414 |
415 | return status;
416 | }
417 |
418 | public async updatePlaylist(): Promise {
419 | const playlist = await this._sendCommand(CommandScope.PLAYLIST);
420 |
421 | if (this._changeEvents && !equal(playlist, this._playlist)) {
422 | try {
423 | this.emit('playlistchange', this._playlist || playlist, playlist);
424 | } catch (err) {
425 | this.emit('error', err);
426 | }
427 | this._playlist = playlist;
428 | }
429 |
430 | return playlist;
431 | }
432 |
433 | public async updateAll(): Promise<[Status, Playlist]> {
434 | const [status, playlist] = await Promise.all([
435 | this.updateStatus(),
436 | this.updatePlaylist()
437 | ]);
438 |
439 | try {
440 | this.emit('update', status, playlist);
441 | } catch (err) {
442 | this.emit('error', err);
443 | }
444 |
445 | return [status, playlist];
446 | }
447 |
448 | /**
449 | * Add `uri` to playlist and start playback.
450 | */
451 | public addToQueueAndPlay(
452 | uri: fs.PathLike,
453 | option?: 'noaudio' | 'novideo'
454 | ): Promise {
455 | return this._sendCommand(CommandScope.STATUS, 'in_play', {
456 | input: pathlikeToString(uri),
457 | option
458 | });
459 | }
460 |
461 | /**
462 | * Add `uri` to playlist.
463 | */
464 | public addToQueue(uri: fs.PathLike): Promise {
465 | return this._sendCommand(CommandScope.STATUS, 'in_enqueue', {
466 | input: pathlikeToString(uri)
467 | });
468 | }
469 |
470 | /**
471 | * Add subtitle to currently playing file.
472 | */
473 | public addSubtitle(uri: fs.PathLike): Promise {
474 | return this._sendCommand(CommandScope.STATUS, 'addsubtitle', {
475 | input: pathlikeToString(uri)
476 | });
477 | }
478 |
479 | /**
480 | * Play playlist item `id`. If `id` is omitted, play last active item.
481 | */
482 | public play(id: number): Promise {
483 | return this._sendCommand(CommandScope.STATUS, 'pl_play', { id });
484 | }
485 |
486 | /**
487 | * Toggle pause. If current state was 'stop', play item `id`, if `id` is omitted, play current item.
488 | * If no current item, play 1st item in the playlist.
489 | */
490 | public pause(id: number): Promise {
491 | return this._sendCommand(CommandScope.STATUS, 'pl_pause', { id });
492 | }
493 |
494 | /**
495 | * Stop playback.
496 | */
497 | public stop(): Promise {
498 | return this._sendCommand(CommandScope.STATUS, 'pl_forcepause');
499 | }
500 |
501 | /**
502 | * Resume playback if state was 'paused', else do nothing.
503 | */
504 | public resume(): Promise {
505 | return this._sendCommand(CommandScope.STATUS, 'pl_forceresume');
506 | }
507 |
508 | /**
509 | * Pause playback, do nothing if state was 'paused'.
510 | */
511 | public forcePause(): Promise {
512 | return this._sendCommand(CommandScope.STATUS, 'pl_forcepause');
513 | }
514 |
515 | /**
516 | * Jump to next item in playlist.
517 | */
518 | public playlistNext(): Promise {
519 | return this._sendCommand(CommandScope.STATUS, 'pl_next');
520 | }
521 |
522 | /**
523 | * Jump to previous item in playlist.
524 | */
525 | public playlistPrevious(): Promise {
526 | return this._sendCommand(CommandScope.STATUS, 'pl_previous');
527 | }
528 |
529 | /**
530 | * Delete item `id` from playlist.
531 | */
532 | public playlistDelete(id: number): Promise {
533 | return this._sendCommand(CommandScope.STATUS, 'pl_delete', { id });
534 | }
535 |
536 | /**
537 | * Empty playlist.
538 | */
539 | public playlistEmpty(): Promise {
540 | return this._sendCommand(CommandScope.STATUS, 'pl_empty');
541 | }
542 |
543 | /**
544 | * Sort playlist using sort mode `mode` and order `order`.
545 | * If `order` = 0 then items will be sorted in normal order, if `order` = 1 ` they will be sorted in reverse order.
546 | * A non exhaustive list of sort modes:
547 | * 0 Id
548 | * 1 Name
549 | * 3 Author
550 | * 5 Random
551 | * 7 Track number
552 | */
553 | public sortPlaylist(order: 0 | 1, mode: 0 | 1 | 3 | 5 | 7): Promise {
554 | return this._sendCommand(CommandScope.STATUS, 'pl_sort', {
555 | id: mode,
556 | val: order
557 | });
558 | }
559 |
560 | /**
561 | * Set audio delay.
562 | */
563 | public setAudioDelay(delay: number): Promise {
564 | return this._sendCommand(CommandScope.STATUS, 'audiodelay', { val: delay });
565 | }
566 |
567 | /**
568 | * Set subtitle delay.
569 | */
570 | public setSubtitleDelay(delay: number): Promise {
571 | return this._sendCommand(CommandScope.STATUS, 'subdelay', { val: delay });
572 | }
573 |
574 | /**
575 | * Set playback rate.
576 | */
577 | public setPlaybackRate(rate: number): Promise {
578 | return this._sendCommand(CommandScope.STATUS, 'rate', { val: rate });
579 | }
580 |
581 | /**
582 | * Set aspect ratio.
583 | */
584 | public setAspectRatio(ratio: AspectRatio): Promise {
585 | return this._sendCommand(CommandScope.STATUS, 'aspectratio', {
586 | val: ratio
587 | });
588 | }
589 |
590 | /**
591 | * Set volume level to `volume`.
592 | */
593 | public setVolume(volume: number | string): Promise {
594 | return this._sendCommand(CommandScope.STATUS, 'volume', { val: volume });
595 | }
596 |
597 | /**
598 | * Set the preamp value.
599 | */
600 | public setPreamp(value: number): Promise {
601 | return this._sendCommand(CommandScope.STATUS, 'preamp', { val: value });
602 | }
603 |
604 | /**
605 | * Set the gain for a specific band.
606 | */
607 | public setEqualizer(band: number, gain: number): Promise {
608 | return this._sendCommand(CommandScope.STATUS, 'equalizer', {
609 | band: band,
610 | val: gain
611 | });
612 | }
613 |
614 | /**
615 | * Set the equalizer preset as per the `id` specified.
616 | */
617 | public setEqualizerPreset(id: number): Promise {
618 | return this._sendCommand(CommandScope.STATUS, 'equalizer', { val: id });
619 | }
620 |
621 | /**
622 | * Toggle random playback.
623 | */
624 | public toggleRandom(): Promise {
625 | return this._sendCommand(CommandScope.STATUS, 'pl_random');
626 | }
627 |
628 | /**
629 | * Toggle loop.
630 | */
631 | public toggleLoop(): Promise {
632 | return this._sendCommand(CommandScope.STATUS, 'pl_loop');
633 | }
634 |
635 | /**
636 | * Toggle repeat.
637 | */
638 | public toggleRepeat(): Promise {
639 | return this._sendCommand(CommandScope.STATUS, 'pl_repeat');
640 | }
641 |
642 | /**
643 | * Toggle fullscreen.
644 | */
645 | public toggleFullscreen(): Promise {
646 | return this._sendCommand(CommandScope.STATUS, 'fullscreen');
647 | }
648 |
649 | /**
650 | * Seek to `time`.
651 | * @return {Promise