iobroker.foobar2000 2.2.0 → 2.3.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/.github/copilot-instructions.md +809 -0
- package/.github/dependabot.yml +11 -9
- package/.github/workflows/automerge-dependabot.yml +35 -0
- package/.github/workflows/check-copilot-template.yml +195 -0
- package/.github/workflows/test-and-release.yml +6 -4
- package/CHANGELOG_OLD.md +26 -0
- package/LICENSE +1 -1
- package/README.md +7 -27
- package/foobar2000.js +291 -261
- package/io-package.json +22 -7
- package/package.json +17 -41
- package/.eslintignore +0 -2
- package/.prettierrc.js +0 -9
- package/lib/tools.js +0 -99
- /package/.github/workflows/{dependabot-auto-merge.yml → dependabot-auto-merge.yml.OLD} +0 -0
package/foobar2000.js
CHANGED
|
@@ -10,187 +10,208 @@ let muteVol = 100;
|
|
|
10
10
|
let request;
|
|
11
11
|
let old_states;
|
|
12
12
|
const states = {
|
|
13
|
-
playlist: []
|
|
13
|
+
playlist: [],
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
const Commands = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
play: 'Start',
|
|
18
|
+
stop: 'Stop',
|
|
19
|
+
next: 'StartNext',
|
|
20
|
+
pause: 'PlayOrPause',
|
|
21
|
+
prev: 'StartPrevious',
|
|
22
|
+
nextrandom: 'StartRandom',
|
|
23
|
+
repeat: 'PlaybackOrder',
|
|
24
|
+
shuffle: 'PlaybackOrder',
|
|
25
|
+
random: 'PlaybackOrder',
|
|
26
|
+
seek: 'Seek',
|
|
27
|
+
volume: 'Volume',
|
|
28
|
+
volumedb: 'VolumeDB',
|
|
29
|
+
rating: 'PlayingCommand',
|
|
30
|
+
sac: 'SAC',
|
|
31
|
+
saq: 'SAQ',
|
|
32
|
+
switch_playlist: 'SwitchPlaylist',
|
|
33
|
+
search: 'SearchMediaLibrary',
|
|
34
|
+
browser: 'Browse',
|
|
35
|
+
playid: 'Start',
|
|
36
|
+
itemplaying: 'Start',
|
|
37
|
+
clear: 'EmptyPlaylist',
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
function startAdapter(options){
|
|
41
|
-
return adapter = utils.adapter(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
stateChange: (id, state) => {
|
|
56
|
-
if (id && state && !state.ack){
|
|
57
|
-
adapter.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
|
|
58
|
-
let param;
|
|
59
|
-
id = id.split('.')[2].toString().toLowerCase();
|
|
60
|
-
if (id === 'start' || id === 'exit'){
|
|
61
|
-
launch(id);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
if (id === 'repeat'){
|
|
65
|
-
param = state.val;
|
|
66
|
-
}
|
|
67
|
-
if (id === 'switch_playlist'){
|
|
68
|
-
param = state.val;
|
|
69
|
-
}
|
|
70
|
-
if (id === 'rating'){
|
|
71
|
-
if (state.val > 5) state.val = 5;
|
|
72
|
-
if (state.val < 0) state.val = 0;
|
|
73
|
-
param = encodeURIComponent('Playback Statistics/Rating/' + state.val);
|
|
74
|
-
}
|
|
75
|
-
if (id === 'seek'){
|
|
76
|
-
if (state.val > 100) state.val = 100;
|
|
77
|
-
if (state.val < 0) state.val = 0;
|
|
78
|
-
param = state.val;
|
|
79
|
-
}
|
|
80
|
-
if (id === 'volume'){
|
|
81
|
-
if (state.val > 100) state.val = 100;
|
|
82
|
-
if (state.val < 0) state.val = 0;
|
|
83
|
-
param = state.val;
|
|
84
|
-
}
|
|
85
|
-
if (id === 'volumedb'){ //volume level, 0...665 (0...-66.5 db), or 1000 to mute
|
|
86
|
-
if (state.val > 665) state.val = 665;
|
|
87
|
-
if (state.val < 0) state.val = 0;
|
|
88
|
-
param = state.val;
|
|
89
|
-
}
|
|
90
|
-
if (id === 'playid'){
|
|
91
|
-
param = state.val - 1;
|
|
40
|
+
function startAdapter(options) {
|
|
41
|
+
return (adapter = utils.adapter(
|
|
42
|
+
Object.assign({}, options, {
|
|
43
|
+
systemConfig: true,
|
|
44
|
+
name: 'foobar2000',
|
|
45
|
+
ready: main,
|
|
46
|
+
unload: callback => {
|
|
47
|
+
timerPoll && clearInterval(timerPoll);
|
|
48
|
+
timeout && clearTimeout(timeout);
|
|
49
|
+
try {
|
|
50
|
+
adapter.log.debug('cleaned everything up...');
|
|
51
|
+
callback();
|
|
52
|
+
} catch {
|
|
53
|
+
callback();
|
|
92
54
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
55
|
+
},
|
|
56
|
+
stateChange: (id, state) => {
|
|
57
|
+
if (id && state && !state.ack) {
|
|
58
|
+
adapter.log.debug(`state ${id} changed: ${state.val} (ack = ${state.ack})`);
|
|
59
|
+
let param;
|
|
60
|
+
id = id.split('.')[2].toString().toLowerCase();
|
|
61
|
+
if (id === 'start' || id === 'exit') {
|
|
62
|
+
launch(id);
|
|
63
|
+
return;
|
|
98
64
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (state.val){
|
|
102
|
-
param = '1';
|
|
103
|
-
} else {
|
|
104
|
-
param = '0';
|
|
65
|
+
if (id === 'repeat') {
|
|
66
|
+
param = state.val;
|
|
105
67
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (state.val){
|
|
109
|
-
param = '3';
|
|
110
|
-
} else {
|
|
111
|
-
param = '0';
|
|
68
|
+
if (id === 'switch_playlist') {
|
|
69
|
+
param = state.val;
|
|
112
70
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
param = muteVol;
|
|
71
|
+
if (id === 'rating') {
|
|
72
|
+
if (state.val > 5) {
|
|
73
|
+
state.val = 5;
|
|
74
|
+
}
|
|
75
|
+
if (state.val < 0) {
|
|
76
|
+
state.val = 0;
|
|
77
|
+
}
|
|
78
|
+
param = encodeURIComponent(`Playback Statistics/Rating/${state.val}`);
|
|
122
79
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
param = state.val;
|
|
132
|
-
if (param === '/'){
|
|
133
|
-
param = ' ';
|
|
80
|
+
if (id === 'seek') {
|
|
81
|
+
if (state.val > 100) {
|
|
82
|
+
state.val = 100;
|
|
83
|
+
}
|
|
84
|
+
if (state.val < 0) {
|
|
85
|
+
state.val = 0;
|
|
86
|
+
}
|
|
87
|
+
param = state.val;
|
|
134
88
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
89
|
+
if (id === 'volume') {
|
|
90
|
+
if (state.val > 100) {
|
|
91
|
+
state.val = 100;
|
|
92
|
+
}
|
|
93
|
+
if (state.val < 0) {
|
|
94
|
+
state.val = 0;
|
|
95
|
+
}
|
|
96
|
+
param = state.val;
|
|
97
|
+
}
|
|
98
|
+
if (id === 'volumedb') {
|
|
99
|
+
//volume level, 0...665 (0...-66.5 db), or 1000 to mute
|
|
100
|
+
if (state.val > 665) {
|
|
101
|
+
state.val = 665;
|
|
102
|
+
}
|
|
103
|
+
if (state.val < 0) {
|
|
104
|
+
state.val = 0;
|
|
105
|
+
}
|
|
106
|
+
param = state.val;
|
|
107
|
+
}
|
|
108
|
+
if (id === 'playid') {
|
|
109
|
+
param = state.val - 1;
|
|
110
|
+
}
|
|
111
|
+
if (id === 'shuffle') {
|
|
112
|
+
if (state.val) {
|
|
113
|
+
param = '4';
|
|
114
|
+
} else {
|
|
115
|
+
param = '0';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (id === 'sac') {
|
|
119
|
+
if (state.val) {
|
|
120
|
+
param = '1';
|
|
121
|
+
} else {
|
|
122
|
+
param = '0';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (id === 'random') {
|
|
126
|
+
if (state.val) {
|
|
127
|
+
param = '3';
|
|
128
|
+
} else {
|
|
129
|
+
param = '0';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (id === 'mute') {
|
|
133
|
+
if (state.val) {
|
|
134
|
+
id = 'volume';
|
|
135
|
+
muteVol = states.volume || 100;
|
|
136
|
+
param = '0';
|
|
137
|
+
} else {
|
|
138
|
+
id = 'volume';
|
|
139
|
+
param = muteVol;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (id === 'search') {
|
|
143
|
+
param = encodeURIComponent(param);
|
|
144
|
+
}
|
|
145
|
+
if (id === 'clear') {
|
|
146
|
+
getPlaylist();
|
|
147
|
+
}
|
|
148
|
+
if (id === 'browser') {
|
|
149
|
+
param = state.val;
|
|
150
|
+
if (param === '/') {
|
|
151
|
+
param = ' ';
|
|
152
|
+
}
|
|
153
|
+
browser(Commands[id], param);
|
|
154
|
+
}
|
|
155
|
+
if (id === 'add') {
|
|
156
|
+
if (id === 'add') {
|
|
157
|
+
param = encodeURIComponent(state.val);
|
|
158
|
+
/*let options = {
|
|
141
159
|
host: adapter.config.ip,
|
|
142
160
|
port: adapter.config.port,
|
|
143
161
|
path: '/foobar2000controller/?cmd=Browse¶m1=' + param + '¶m2=EnqueueDirSubdirs' //¶m3=browser.json
|
|
144
162
|
};*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
timeout && clearTimeout(timeout);
|
|
164
|
+
httpGet('Browse', [param, 'EnqueueDirSubdirs'], _data => {
|
|
165
|
+
timeout = setTimeout(() => {
|
|
166
|
+
getPlaylist();
|
|
167
|
+
}, 1000);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
const cmd = Commands[id];
|
|
172
|
+
if (cmd) {
|
|
173
|
+
if (param !== undefined) {
|
|
174
|
+
httpGet(cmd, [param]);
|
|
175
|
+
} else {
|
|
176
|
+
httpGet(cmd, '');
|
|
177
|
+
}
|
|
159
178
|
}
|
|
160
179
|
}
|
|
161
180
|
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
181
|
+
},
|
|
182
|
+
}),
|
|
183
|
+
));
|
|
165
184
|
}
|
|
166
185
|
|
|
167
|
-
function httpGet(cmd, param, cb){
|
|
186
|
+
function httpGet(cmd, param, cb) {
|
|
168
187
|
let params = '';
|
|
169
|
-
if (Array.isArray(param)){
|
|
188
|
+
if (Array.isArray(param)) {
|
|
170
189
|
param.forEach((key, i) => {
|
|
171
|
-
params +=
|
|
190
|
+
params += `¶m${i + 1}=${key}`;
|
|
172
191
|
});
|
|
173
192
|
}
|
|
174
193
|
const options = {
|
|
175
194
|
host: adapter.config.ip || '127.0.0.1',
|
|
176
195
|
port: adapter.config.port || 8888,
|
|
177
|
-
path:
|
|
196
|
+
path: `/foobar2000controller/?cmd=${cmd}${params}`,
|
|
178
197
|
};
|
|
179
|
-
if (adapter.config.login && adapter.config.password){
|
|
180
|
-
options.headers = {
|
|
198
|
+
if (adapter.config.login && adapter.config.password) {
|
|
199
|
+
options.headers = {
|
|
200
|
+
Authorization: `Basic ${new Buffer(`${adapter.config.login}:${adapter.config.password}`).toString('base64')}`,
|
|
201
|
+
};
|
|
181
202
|
}
|
|
182
|
-
adapter.log.debug(
|
|
183
|
-
request = http.get(options,
|
|
203
|
+
adapter.log.debug(`foobar2000 httpGet("${cmd} - ${JSON.stringify(options)}")`);
|
|
204
|
+
request = http.get(options, res => {
|
|
184
205
|
let jsondata = '';
|
|
185
206
|
res.setEncoding('utf8');
|
|
186
|
-
res.on('error',
|
|
207
|
+
res.on('error', e => {
|
|
187
208
|
adapter.log.debug(e.toString());
|
|
188
209
|
});
|
|
189
|
-
res.on('data', chunk => jsondata += chunk);
|
|
210
|
+
res.on('data', chunk => (jsondata += chunk));
|
|
190
211
|
res.on('end', () => {
|
|
191
|
-
if (res.statusCode === 200){
|
|
212
|
+
if (res.statusCode === 200) {
|
|
192
213
|
setInfoConnection(true);
|
|
193
|
-
if (!~jsondata.indexOf('Invalid request')){
|
|
214
|
+
if (!~jsondata.indexOf('Invalid request')) {
|
|
194
215
|
try {
|
|
195
216
|
jsondata = JSON.parse(jsondata);
|
|
196
217
|
cb && cb(jsondata, false);
|
|
@@ -203,8 +224,8 @@ function httpGet(cmd, param, cb){
|
|
|
203
224
|
}
|
|
204
225
|
} else {
|
|
205
226
|
setInfoConnection(false);
|
|
206
|
-
adapter.log.debug(
|
|
207
|
-
if (res.statusCode === 401){
|
|
227
|
+
adapter.log.debug(`STATUS: ${res.statusCode}`);
|
|
228
|
+
if (res.statusCode === 401) {
|
|
208
229
|
adapter.log.error('Foobar2000 Connection Authorization Error has ocurred. Check login or pass!');
|
|
209
230
|
}
|
|
210
231
|
jsondata = null;
|
|
@@ -212,22 +233,22 @@ function httpGet(cmd, param, cb){
|
|
|
212
233
|
});
|
|
213
234
|
});
|
|
214
235
|
request.shouldKeepAlive = false;
|
|
215
|
-
request.on('error',
|
|
236
|
+
request.on('error', e => {
|
|
216
237
|
setInfoConnection(false);
|
|
217
|
-
cb && cb(false,
|
|
238
|
+
cb && cb(false, ` Error: ${e}`);
|
|
218
239
|
});
|
|
219
240
|
}
|
|
220
241
|
|
|
221
|
-
function getCurrentTrackInfo(cb){
|
|
222
|
-
httpGet('PlaylistItemsPerPage', [1],
|
|
223
|
-
if (res){
|
|
242
|
+
function getCurrentTrackInfo(cb) {
|
|
243
|
+
httpGet('PlaylistItemsPerPage', [1], res => {
|
|
244
|
+
if (res) {
|
|
224
245
|
states.volume = res.volume;
|
|
225
246
|
states.state = statePlaying(res.isPlaying, res.playingItem);
|
|
226
|
-
states.album = res.playlist[0].album === '?' ? '' :res.playlist[0].album;
|
|
227
|
-
states.artist = res.playlist[0].artist === '?' ? res.playlist[0].track :res.playlist[0].artist;
|
|
247
|
+
states.album = res.playlist[0].album === '?' ? '' : res.playlist[0].album;
|
|
248
|
+
states.artist = res.playlist[0].artist === '?' ? res.playlist[0].track : res.playlist[0].artist;
|
|
228
249
|
states.title = res.playlist[0].track;
|
|
229
250
|
states.current_duration = res.playlist[0].len;
|
|
230
|
-
states.rating = res.playlist[0].rating !== '?' ? res.playlist[0].rating :'';
|
|
251
|
+
states.rating = res.playlist[0].rating !== '?' ? res.playlist[0].rating : '';
|
|
231
252
|
states.current_elapsed = secToText(res.trackPosition);
|
|
232
253
|
states.playlistId = parseInt(res.currentPlaylist, 10);
|
|
233
254
|
states.itemPlaying = parseInt(res.playingItem, 10) + 1;
|
|
@@ -236,19 +257,21 @@ function getCurrentTrackInfo(cb){
|
|
|
236
257
|
states.bitrate = res.codec && parseInt(res.codec.split('|')[1], 10);
|
|
237
258
|
states.sampleRate = res.codec && parseInt(res.codec.split('|')[2], 10);
|
|
238
259
|
states.bits = res.codec && parseInt(res.codec.split('|')[3], 10);
|
|
239
|
-
states.channels = res.codec && res.codec.split('|')[4] || res.codec.split('|')[3];
|
|
240
|
-
states.albumArt = adapter.config.ip
|
|
260
|
+
states.channels = (res.codec && res.codec.split('|')[4]) || res.codec.split('|')[3];
|
|
261
|
+
states.albumArt = `${adapter.config.ip}:${adapter.config.port}${res.albumArt}`;
|
|
241
262
|
states.volumeDB = parseInt(res.volumeDB, 10);
|
|
242
263
|
states.trackLength = parseInt(res.trackLength, 10);
|
|
243
264
|
states.elapsedTime = parseInt(res.trackPosition, 10);
|
|
244
|
-
states.seek = isNaN(parseFloat((res.trackPosition / res.trackLength) * 100).toFixed(4))
|
|
265
|
+
states.seek = isNaN(parseFloat((res.trackPosition / res.trackLength) * 100).toFixed(4))
|
|
266
|
+
? 0
|
|
267
|
+
: parseFloat((res.trackPosition / res.trackLength) * 100).toFixed(4);
|
|
245
268
|
states.mute = res.volume === 0;
|
|
246
269
|
cb && cb();
|
|
247
270
|
}
|
|
248
271
|
});
|
|
249
272
|
}
|
|
250
273
|
|
|
251
|
-
function clearStatePlay(){
|
|
274
|
+
function clearStatePlay() {
|
|
252
275
|
states.album = '';
|
|
253
276
|
states.artist = '';
|
|
254
277
|
states.title = '';
|
|
@@ -257,55 +280,59 @@ function clearStatePlay(){
|
|
|
257
280
|
states.rating = '';
|
|
258
281
|
}
|
|
259
282
|
|
|
260
|
-
function getInfo(cb){
|
|
261
|
-
httpGet(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
283
|
+
function getInfo(cb) {
|
|
284
|
+
httpGet(
|
|
285
|
+
'',
|
|
286
|
+
['', '', '{"order":"[PLAYBACK_ORDER]","sac":"[SAC]","saq":"[SAQ]","active_playlist":"[PLAYLIST_ACTIVE]"}.json'],
|
|
287
|
+
res => {
|
|
288
|
+
if (res) {
|
|
289
|
+
let obj = res.match(/<p>.*({.*}).json/)[1];
|
|
290
|
+
if (obj) {
|
|
291
|
+
try {
|
|
292
|
+
obj = JSON.parse(obj);
|
|
293
|
+
const order = parseInt(obj.order, 10);
|
|
294
|
+
states.sac = !!obj.sac;
|
|
295
|
+
states.switch_playlist = obj.active_playlist;
|
|
296
|
+
switch (order) {
|
|
297
|
+
case 0:
|
|
298
|
+
states.repeat = 'Off';
|
|
299
|
+
states.shuffle = false;
|
|
300
|
+
break;
|
|
301
|
+
case 1:
|
|
302
|
+
states.repeat = 'All';
|
|
303
|
+
states.shuffle = false;
|
|
304
|
+
break;
|
|
305
|
+
case 2:
|
|
306
|
+
states.repeat = 'One';
|
|
307
|
+
states.shuffle = false;
|
|
308
|
+
break;
|
|
309
|
+
default:
|
|
310
|
+
}
|
|
311
|
+
if (order === 3) {
|
|
312
|
+
states.random = true;
|
|
313
|
+
} else {
|
|
314
|
+
states.random = false;
|
|
315
|
+
}
|
|
316
|
+
if (order === 3 || order === 4 || order === 5 || order === 6) {
|
|
272
317
|
states.repeat = 'Off';
|
|
273
|
-
states.shuffle =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
states.shuffle = false;
|
|
278
|
-
break;
|
|
279
|
-
case 2:
|
|
280
|
-
states.repeat = 'One';
|
|
281
|
-
states.shuffle = false;
|
|
282
|
-
break;
|
|
283
|
-
default:
|
|
284
|
-
}
|
|
285
|
-
if (order === 3){
|
|
286
|
-
states.random = true;
|
|
287
|
-
} else {
|
|
288
|
-
states.random = false;
|
|
289
|
-
}
|
|
290
|
-
if (order === 3 || order === 4 || order === 5 || order === 6){
|
|
291
|
-
states.repeat = 'Off';
|
|
292
|
-
states.shuffle = true;
|
|
318
|
+
states.shuffle = true;
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
adapter.log.debug('Error parse obj');
|
|
293
322
|
}
|
|
294
|
-
} catch (e) {
|
|
295
|
-
adapter.log.debug('Error parse obj');
|
|
296
323
|
}
|
|
324
|
+
cb && cb();
|
|
297
325
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
});
|
|
326
|
+
},
|
|
327
|
+
);
|
|
301
328
|
}
|
|
302
329
|
|
|
303
|
-
function statePlaying(play, item){
|
|
330
|
+
function statePlaying(play, item) {
|
|
304
331
|
let state;
|
|
305
|
-
if (play === '1'){
|
|
332
|
+
if (play === '1') {
|
|
306
333
|
state = 'play';
|
|
307
334
|
} else {
|
|
308
|
-
if (item !== '?'){
|
|
335
|
+
if (item !== '?') {
|
|
309
336
|
state = 'pause';
|
|
310
337
|
} else {
|
|
311
338
|
state = 'stop';
|
|
@@ -315,10 +342,10 @@ function statePlaying(play, item){
|
|
|
315
342
|
return state;
|
|
316
343
|
}
|
|
317
344
|
|
|
318
|
-
function setStates(){
|
|
319
|
-
Object.keys(states).forEach(
|
|
320
|
-
if (states[key] !== old_states[key]){
|
|
321
|
-
if (key === 'playid'){
|
|
345
|
+
function setStates() {
|
|
346
|
+
Object.keys(states).forEach(key => {
|
|
347
|
+
if (states[key] !== old_states[key]) {
|
|
348
|
+
if (key === 'playid') {
|
|
322
349
|
getPlaylist();
|
|
323
350
|
}
|
|
324
351
|
old_states[key] = states[key];
|
|
@@ -327,7 +354,7 @@ function setStates(){
|
|
|
327
354
|
});
|
|
328
355
|
}
|
|
329
356
|
|
|
330
|
-
function poll(){
|
|
357
|
+
function poll() {
|
|
331
358
|
timerPoll && clearInterval(timerPoll);
|
|
332
359
|
timerPoll = setInterval(() => {
|
|
333
360
|
getCurrentTrackInfo(() => {
|
|
@@ -338,10 +365,10 @@ function poll(){
|
|
|
338
365
|
}, 2000);
|
|
339
366
|
}
|
|
340
367
|
|
|
341
|
-
function main(){
|
|
368
|
+
function main() {
|
|
342
369
|
adapter.setState('info.connection', false, true);
|
|
343
370
|
old_states = JSON.parse(JSON.stringify(states));
|
|
344
|
-
if (adapter.config.path){
|
|
371
|
+
if (adapter.config.path) {
|
|
345
372
|
foobarPath = adapter.config.path;
|
|
346
373
|
}
|
|
347
374
|
adapter.subscribeStates('*');
|
|
@@ -349,52 +376,54 @@ function main(){
|
|
|
349
376
|
//getPlaylist(); // test
|
|
350
377
|
}
|
|
351
378
|
|
|
352
|
-
function setInfoConnection(val){
|
|
379
|
+
function setInfoConnection(val) {
|
|
353
380
|
adapter.getState('info.connection', (err, state) => {
|
|
354
|
-
if (!err && state.val !== val){
|
|
381
|
+
if (!err && state.val !== val) {
|
|
355
382
|
adapter.setState('info.connection', val, true);
|
|
356
|
-
if (val)
|
|
383
|
+
if (val) {
|
|
384
|
+
adapter.log.info(`Foobar2000 ${adapter.config.ip}:${adapter.config.port} connected`);
|
|
385
|
+
}
|
|
357
386
|
}
|
|
358
387
|
});
|
|
359
388
|
}
|
|
360
389
|
|
|
361
|
-
function secToText(sec){
|
|
390
|
+
function secToText(sec) {
|
|
362
391
|
let res;
|
|
363
392
|
let m = Math.floor(sec / 60);
|
|
364
393
|
const s = sec % 60;
|
|
365
394
|
const h = Math.floor(m / 60);
|
|
366
395
|
m = m % 60;
|
|
367
|
-
if (h > 0){
|
|
368
|
-
res = pad2(h)
|
|
396
|
+
if (h > 0) {
|
|
397
|
+
res = `${pad2(h)}:${pad2(m)}:${pad2(s)}`;
|
|
369
398
|
} else {
|
|
370
|
-
res = pad2(m)
|
|
399
|
+
res = `${pad2(m)}:${pad2(s)}`;
|
|
371
400
|
}
|
|
372
401
|
return res;
|
|
373
402
|
}
|
|
374
403
|
|
|
375
|
-
function pad2(num){
|
|
404
|
+
function pad2(num) {
|
|
376
405
|
const s = num.toString();
|
|
377
|
-
return
|
|
406
|
+
return s.length < 2 ? `0${s}` : s;
|
|
378
407
|
}
|
|
379
408
|
|
|
380
|
-
function getPlaylist(){
|
|
381
|
-
httpGet('PlaylistItemsPerPage', [16384],
|
|
382
|
-
if (res && res.playlist){
|
|
409
|
+
function getPlaylist() {
|
|
410
|
+
httpGet('PlaylistItemsPerPage', [16384], res => {
|
|
411
|
+
if (res && res.playlist) {
|
|
383
412
|
const playlist = [];
|
|
384
413
|
//const arr = res.songs;
|
|
385
414
|
res.playlist.forEach((key, i) => {
|
|
386
415
|
playlist[i] = {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
416
|
+
id: i + 1,
|
|
417
|
+
artist: key.artist,
|
|
418
|
+
album: key.album,
|
|
419
|
+
bitrate: 0,
|
|
420
|
+
title: key.track,
|
|
421
|
+
file: '',
|
|
422
|
+
genre: '',
|
|
423
|
+
year: 0,
|
|
424
|
+
len: key.len,
|
|
425
|
+
rating: key.rating,
|
|
426
|
+
cover: '',
|
|
398
427
|
};
|
|
399
428
|
});
|
|
400
429
|
states.playlist = JSON.stringify(playlist);
|
|
@@ -403,30 +432,30 @@ function getPlaylist(){
|
|
|
403
432
|
});
|
|
404
433
|
}
|
|
405
434
|
|
|
406
|
-
function launch(cmd){
|
|
407
|
-
adapter.log.debug(
|
|
408
|
-
if (adapter.config.remote !== 'on'){
|
|
409
|
-
if (cmd === 'start'){
|
|
435
|
+
function launch(cmd) {
|
|
436
|
+
adapter.log.debug(`launch ${JSON.stringify(cmd)}`);
|
|
437
|
+
if (adapter.config.remote !== 'on') {
|
|
438
|
+
if (cmd === 'start') {
|
|
410
439
|
launchFoobar();
|
|
411
|
-
} else if (cmd === 'exit'){
|
|
440
|
+
} else if (cmd === 'exit') {
|
|
412
441
|
sendShellCommand('exit');
|
|
413
442
|
}
|
|
414
|
-
} else if (adapter.config.cmdstart){
|
|
443
|
+
} else if (adapter.config.cmdstart) {
|
|
415
444
|
const parts = adapter.config.path.split(':');
|
|
416
445
|
const options = {
|
|
417
446
|
host: parts[0],
|
|
418
447
|
port: parts[1],
|
|
419
|
-
path: ''
|
|
448
|
+
path: '',
|
|
420
449
|
};
|
|
421
|
-
if (cmd === 'start'){
|
|
422
|
-
options.path =
|
|
450
|
+
if (cmd === 'start') {
|
|
451
|
+
options.path = `/?cmd=${adapter.config.cmdstart}`;
|
|
423
452
|
} else {
|
|
424
|
-
options.path =
|
|
453
|
+
options.path = `/?cmd=${adapter.config.cmdexit}`;
|
|
425
454
|
}
|
|
426
|
-
adapter.log.info(
|
|
427
|
-
request = http.get(options,
|
|
455
|
+
adapter.log.info(`launch ${JSON.stringify(options)}`);
|
|
456
|
+
request = http.get(options, res => {
|
|
428
457
|
res.on('end', () => {
|
|
429
|
-
if (res.statusCode === 200){
|
|
458
|
+
if (res.statusCode === 200) {
|
|
430
459
|
adapter.log.debug('foobar2000 send start ok');
|
|
431
460
|
} else {
|
|
432
461
|
adapter.log.error('foobar2000 send start false');
|
|
@@ -439,20 +468,20 @@ function launch(cmd){
|
|
|
439
468
|
}
|
|
440
469
|
}
|
|
441
470
|
|
|
442
|
-
function sendShellCommand(command){
|
|
443
|
-
exec(
|
|
471
|
+
function sendShellCommand(command) {
|
|
472
|
+
exec(`foobar2000.exe /${command}`, { cwd: foobarPath });
|
|
444
473
|
}
|
|
445
474
|
|
|
446
|
-
function launchFoobar(){
|
|
447
|
-
exec('foobar2000.exe', {cwd: foobarPath});
|
|
475
|
+
function launchFoobar() {
|
|
476
|
+
exec('foobar2000.exe', { cwd: foobarPath });
|
|
448
477
|
}
|
|
449
478
|
|
|
450
|
-
function browser(cmd, param){
|
|
479
|
+
function browser(cmd, param) {
|
|
451
480
|
param = encodeURIComponent(param); //'¶m3=browser.json'
|
|
452
481
|
// let data = 'cmd=' + cmd + '¶m1=' + param;
|
|
453
|
-
if (cmd){
|
|
454
|
-
httpGet(cmd, [param],
|
|
455
|
-
if (data){
|
|
482
|
+
if (cmd) {
|
|
483
|
+
httpGet(cmd, [param], data => {
|
|
484
|
+
if (data) {
|
|
456
485
|
data = data.browser;
|
|
457
486
|
filemanager('', data);
|
|
458
487
|
}
|
|
@@ -460,27 +489,28 @@ function browser(cmd, param){
|
|
|
460
489
|
}
|
|
461
490
|
}
|
|
462
491
|
|
|
463
|
-
function filemanager(val, arr){
|
|
464
|
-
const browser = {},
|
|
492
|
+
function filemanager(val, arr) {
|
|
493
|
+
const browser = {},
|
|
494
|
+
files = [];
|
|
465
495
|
arr.forEach((item, i, arr) => {
|
|
466
496
|
const obj = {};
|
|
467
497
|
const size = parseFloat(arr[i].fs) * 1024;
|
|
468
|
-
if (!isNaN(size)){
|
|
498
|
+
if (!isNaN(size)) {
|
|
469
499
|
obj.size = (parseFloat(arr[i].fs.replace(',', '.')) * 1024).toFixed(0);
|
|
470
500
|
} else {
|
|
471
501
|
obj.size = '';
|
|
472
502
|
}
|
|
473
|
-
if (arr[i].ft && ~arr[i].ft.indexOf(':')){
|
|
503
|
+
if (arr[i].ft && ~arr[i].ft.indexOf(':')) {
|
|
474
504
|
const mod = arr[i].ft.split(' '); //"27.05.2004 01:50" 2016-02-27 16:05:46
|
|
475
505
|
const d = mod[0].split('.').reverse().join('-');
|
|
476
|
-
arr[i].ft = d
|
|
506
|
+
arr[i].ft = `${d} ${mod[1]}`;
|
|
477
507
|
}
|
|
478
|
-
if (arr[i].fs){
|
|
508
|
+
if (arr[i].fs) {
|
|
479
509
|
obj.filetype = 'file';
|
|
480
510
|
} else {
|
|
481
511
|
obj.filetype = 'directory';
|
|
482
512
|
}
|
|
483
|
-
if (arr[i].fs === 'NTFS' || arr[i].fs === 'FAT32'){
|
|
513
|
+
if (arr[i].fs === 'NTFS' || arr[i].fs === 'FAT32') {
|
|
484
514
|
obj.filetype = 'directory';
|
|
485
515
|
}
|
|
486
516
|
obj.file = decodeURIComponent(arr[i].pu);
|
|
@@ -488,15 +518,15 @@ function filemanager(val, arr){
|
|
|
488
518
|
obj.label = arr[i].p;
|
|
489
519
|
|
|
490
520
|
files.push(obj);
|
|
491
|
-
if (i === arr.length - 1){
|
|
521
|
+
if (i === arr.length - 1) {
|
|
492
522
|
browser.files = files;
|
|
493
523
|
adapter.setState('browser', JSON.stringify(browser), true);
|
|
494
524
|
}
|
|
495
525
|
});
|
|
496
526
|
}
|
|
497
527
|
|
|
498
|
-
if (module.parent){
|
|
528
|
+
if (module.parent) {
|
|
499
529
|
module.exports = startAdapter;
|
|
500
530
|
} else {
|
|
501
531
|
startAdapter();
|
|
502
|
-
}
|
|
532
|
+
}
|