liquidsoap-prettier 1.8.1 → 1.8.3
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/workflows/check-formatting.yml +32 -0
- package/README.md +31 -5
- package/dist/liquidsoap.cjs +4155 -3090
- package/package.json +1 -1
- package/src/cli.js +104 -9
- package/tests/liq/audio.liq +460 -0
- package/tests/liq/autocue.liq +1081 -0
- package/tests/liq/clock.liq +14 -0
- package/tests/liq/cron.liq +74 -0
- package/tests/liq/error.liq +48 -0
- package/tests/liq/extra/audio.liq +677 -0
- package/tests/liq/extra/audioscrobbler.liq +482 -0
- package/tests/liq/extra/deprecations.liq +976 -0
- package/tests/liq/extra/externals.liq +196 -0
- package/tests/liq/extra/fades.liq +260 -0
- package/tests/liq/extra/file.liq +66 -0
- package/tests/liq/extra/http.liq +160 -0
- package/tests/liq/extra/interactive.liq +917 -0
- package/tests/liq/extra/metadata.liq +75 -0
- package/tests/liq/extra/native.liq +201 -0
- package/tests/liq/extra/openai.liq +150 -0
- package/tests/liq/extra/server.liq +177 -0
- package/tests/liq/extra/source.liq +476 -0
- package/tests/liq/extra/spinitron.liq +272 -0
- package/tests/liq/extra/telnet.liq +266 -0
- package/tests/liq/extra/video.liq +59 -0
- package/tests/liq/extra/visualization.liq +68 -0
- package/tests/liq/fades.liq +941 -0
- package/tests/liq/ffmpeg.liq +605 -0
- package/tests/liq/file.liq +387 -0
- package/tests/liq/getter.liq +74 -0
- package/tests/liq/hls.liq +329 -0
- package/tests/liq/http.liq +1048 -0
- package/tests/liq/http_codes.liq +447 -0
- package/tests/liq/icecast.liq +58 -0
- package/tests/liq/io.liq +106 -0
- package/tests/liq/liquidsoap.liq +31 -0
- package/tests/liq/list.liq +440 -0
- package/tests/liq/log.liq +47 -0
- package/tests/liq/lufs.liq +295 -0
- package/tests/liq/math.liq +23 -0
- package/tests/liq/medialib.liq +752 -0
- package/tests/liq/metadata.liq +253 -0
- package/tests/liq/nfo.liq +258 -0
- package/tests/liq/null.liq +71 -0
- package/tests/liq/playlist.liq +1347 -0
- package/tests/liq/predicate.liq +106 -0
- package/tests/liq/process.liq +93 -0
- package/tests/liq/profiler.liq +5 -0
- package/tests/liq/protocols.liq +1139 -0
- package/tests/liq/ref.liq +28 -0
- package/tests/liq/replaygain.liq +135 -0
- package/tests/liq/request.liq +467 -0
- package/tests/liq/resolvers.liq +33 -0
- package/tests/liq/runtime.liq +70 -0
- package/tests/liq/server.liq +99 -0
- package/tests/liq/settings.liq +41 -0
- package/tests/liq/socket.liq +33 -0
- package/tests/liq/source.liq +362 -0
- package/tests/liq/sqlite.liq +161 -0
- package/tests/liq/stdlib.liq +172 -0
- package/tests/liq/string.liq +476 -0
- package/tests/liq/switches.liq +197 -0
- package/tests/liq/testing.liq +37 -0
- package/tests/liq/thread.liq +161 -0
- package/tests/liq/tracks.liq +100 -0
- package/tests/liq/utils.liq +81 -0
- package/tests/liq/video.liq +918 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
let error.audioscrobbler = error.register("audioscrobbler")
|
|
2
|
+
|
|
3
|
+
let settings.audioscrobbler =
|
|
4
|
+
settings.make.void(
|
|
5
|
+
"Audioscrobbler settings"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
let settings.audioscrobbler.api_key =
|
|
9
|
+
settings.make(
|
|
10
|
+
description="Default API key for audioscrobbler",
|
|
11
|
+
""
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
let settings.audioscrobbler.api_secret =
|
|
15
|
+
settings.make(
|
|
16
|
+
description="Default API secret for audioscrobbler",
|
|
17
|
+
""
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
audioscrobbler = ()
|
|
21
|
+
|
|
22
|
+
def audioscrobbler.request(
|
|
23
|
+
~base_url="http://ws.audioscrobbler.com/2.0",
|
|
24
|
+
~api_key=null,
|
|
25
|
+
~api_secret=null,
|
|
26
|
+
params
|
|
27
|
+
) =
|
|
28
|
+
api_key = api_key ?? settings.audioscrobbler.api_key()
|
|
29
|
+
api_secret = api_secret ?? settings.audioscrobbler.api_secret()
|
|
30
|
+
|
|
31
|
+
if
|
|
32
|
+
api_key == "" or api_secret == ""
|
|
33
|
+
then
|
|
34
|
+
error.raise(
|
|
35
|
+
error.audioscrobbler,
|
|
36
|
+
"`api_key` or `api_secret` missing!"
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
params = [("api_key", api_key), ...params]
|
|
41
|
+
|
|
42
|
+
sig_params = list.sort(fun (v, v') -> string.compare(fst(v), fst(v')), params)
|
|
43
|
+
sig_params = list.map(fun (v) -> "#{fst(v)}#{(snd(v) : string)}", sig_params)
|
|
44
|
+
sig_params = string.concat(separator="", sig_params)
|
|
45
|
+
|
|
46
|
+
api_sig = string.digest("#{sig_params}#{api_secret}")
|
|
47
|
+
|
|
48
|
+
http.post(
|
|
49
|
+
base_url,
|
|
50
|
+
headers=[("Content-Type", "application/x-www-form-urlencoded")],
|
|
51
|
+
data=http.www_form_urlencoded([...params, ("api_sig", api_sig)])
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def audioscrobbler.check_response(resp) =
|
|
56
|
+
let xml.parse ({lfm = {xml_params = {status}}} :
|
|
57
|
+
{lfm: {xml_params: {status: string}}}
|
|
58
|
+
) = resp
|
|
59
|
+
if
|
|
60
|
+
(status == "failed")
|
|
61
|
+
then
|
|
62
|
+
error_ref = error
|
|
63
|
+
let xml.parse ({lfm = {error = {xml_params = {code}}}} :
|
|
64
|
+
{lfm: {error: string.{ xml_params: {code: int} }}}
|
|
65
|
+
) = resp
|
|
66
|
+
error_ref.raise(
|
|
67
|
+
error_ref.audioscrobbler,
|
|
68
|
+
"Error #{code}: #{error}"
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def audioscrobbler.auth(~username, ~password, ~api_key=null, ~api_secret=null) =
|
|
74
|
+
resp =
|
|
75
|
+
audioscrobbler.request(
|
|
76
|
+
api_key=api_key,
|
|
77
|
+
api_secret=api_secret,
|
|
78
|
+
[
|
|
79
|
+
("method", "auth.getMobileSession"),
|
|
80
|
+
("username", username),
|
|
81
|
+
("password", password)
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
audioscrobbler.check_response(resp)
|
|
86
|
+
|
|
87
|
+
try
|
|
88
|
+
let xml.parse ({lfm = {session = {key}}} :
|
|
89
|
+
{lfm: {session: {name: string, key: string}}}
|
|
90
|
+
) = resp
|
|
91
|
+
key
|
|
92
|
+
catch err do
|
|
93
|
+
error.raise(
|
|
94
|
+
error.invalid,
|
|
95
|
+
"Invalid response: #{resp}, error: #{err}"
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
let audioscrobbler.api = {track = ()}
|
|
101
|
+
|
|
102
|
+
# Submit a track to the audioscrobbler
|
|
103
|
+
# `track.updateNowPlaying` API.
|
|
104
|
+
# @category Interaction
|
|
105
|
+
def audioscrobbler.api.track.updateNowPlaying(
|
|
106
|
+
~username,
|
|
107
|
+
~password,
|
|
108
|
+
~session_key=null,
|
|
109
|
+
~api_key=null,
|
|
110
|
+
~api_secret=null,
|
|
111
|
+
~artist,
|
|
112
|
+
~track,
|
|
113
|
+
~album=null,
|
|
114
|
+
~context=null,
|
|
115
|
+
~trackNumber=null,
|
|
116
|
+
~mbid=null,
|
|
117
|
+
~albumArtist=null,
|
|
118
|
+
~duration=null
|
|
119
|
+
) =
|
|
120
|
+
session_key =
|
|
121
|
+
session_key
|
|
122
|
+
?? audioscrobbler.auth(
|
|
123
|
+
username=username,
|
|
124
|
+
password=password,
|
|
125
|
+
api_key=api_key,
|
|
126
|
+
api_secret=api_secret
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
params =
|
|
130
|
+
[
|
|
131
|
+
("track", track),
|
|
132
|
+
("artist", artist),
|
|
133
|
+
...(null.defined(album) ? [("album", null.get(album))] : []),
|
|
134
|
+
...(null.defined(context) ? [("context", null.get(context))] : []),
|
|
135
|
+
...(
|
|
136
|
+
null.defined(trackNumber)
|
|
137
|
+
? [("trackNumber", string((null.get(trackNumber) : int)))]
|
|
138
|
+
: []
|
|
139
|
+
),
|
|
140
|
+
...(null.defined(mbid) ? [("mbid", null.get(mbid))] : []),
|
|
141
|
+
...(
|
|
142
|
+
null.defined(albumArtist)
|
|
143
|
+
? [("albumArtist", null.get(albumArtist))]
|
|
144
|
+
: []
|
|
145
|
+
),
|
|
146
|
+
...(
|
|
147
|
+
null.defined(duration)
|
|
148
|
+
? [("duration", string((null.get(duration) : int)))]
|
|
149
|
+
: []
|
|
150
|
+
)
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
log.info(
|
|
154
|
+
label="audioscrobbler.api.track.updateNowPlaying",
|
|
155
|
+
"Submitting updateNowPlaying with: #{params}"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
resp =
|
|
159
|
+
audioscrobbler.request(
|
|
160
|
+
api_key=api_key,
|
|
161
|
+
api_secret=api_secret,
|
|
162
|
+
[...params, ("method", "track.updateNowPlaying"), ("sk", session_key)]
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
audioscrobbler.check_response(resp)
|
|
166
|
+
|
|
167
|
+
try
|
|
168
|
+
let xml.parse (v :
|
|
169
|
+
{
|
|
170
|
+
lfm: {
|
|
171
|
+
nowplaying: {
|
|
172
|
+
track: string.{ xml_params: {corrected: int} },
|
|
173
|
+
artist: string.{ xml_params: {corrected: int} },
|
|
174
|
+
album: string?.{ xml_params: {corrected: int} },
|
|
175
|
+
albumArtist: string?.{ xml_params: {corrected: int} },
|
|
176
|
+
ignoredMessage: {xml_params: {code: int}}
|
|
177
|
+
},
|
|
178
|
+
xml_params: {status: string}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
) = resp
|
|
182
|
+
|
|
183
|
+
log.info(
|
|
184
|
+
label="audioscrobbler.api.track.updateNowPlaying",
|
|
185
|
+
"Done submitting updateNowPlaying with: #{params}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
v
|
|
189
|
+
catch err do
|
|
190
|
+
error.raise(
|
|
191
|
+
error.invalid,
|
|
192
|
+
"Invalid response: #{resp}, error: #{err}"
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# @flag hidden
|
|
198
|
+
def audioscrobbler.api.apply_meta(
|
|
199
|
+
~name,
|
|
200
|
+
~username,
|
|
201
|
+
~password,
|
|
202
|
+
~api_key,
|
|
203
|
+
~api_secret,
|
|
204
|
+
~session_key,
|
|
205
|
+
fn,
|
|
206
|
+
m
|
|
207
|
+
) =
|
|
208
|
+
def c(v) =
|
|
209
|
+
v == "" ? null : v
|
|
210
|
+
end
|
|
211
|
+
track = m["title"]
|
|
212
|
+
artist = m["artist"]
|
|
213
|
+
|
|
214
|
+
if
|
|
215
|
+
track == "" or artist == ""
|
|
216
|
+
then
|
|
217
|
+
log.info(
|
|
218
|
+
label=name,
|
|
219
|
+
"No artist or track present: metadata submission disabled!"
|
|
220
|
+
)
|
|
221
|
+
else
|
|
222
|
+
album = c(m["album"])
|
|
223
|
+
trackNumber =
|
|
224
|
+
try
|
|
225
|
+
null.map(int_of_string, c(m["tracknumber"]))
|
|
226
|
+
catch _ do
|
|
227
|
+
null
|
|
228
|
+
end
|
|
229
|
+
albumArtist = c(m["albumartist"])
|
|
230
|
+
ignore(
|
|
231
|
+
fn(
|
|
232
|
+
username=username,
|
|
233
|
+
password=password,
|
|
234
|
+
api_key=api_key,
|
|
235
|
+
api_secret=api_secret,
|
|
236
|
+
session_key=session_key,
|
|
237
|
+
track=track,
|
|
238
|
+
artist=artist,
|
|
239
|
+
album=album,
|
|
240
|
+
trackNumber=trackNumber,
|
|
241
|
+
albumArtist=albumArtist
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Submit a track using its metadata to the audioscrobbler
|
|
248
|
+
# `track.updateNowPlaying` API.
|
|
249
|
+
# @category Interaction
|
|
250
|
+
def audioscrobbler.api.track.updateNowPlaying.metadata(
|
|
251
|
+
~username,
|
|
252
|
+
~password,
|
|
253
|
+
~session_key=null,
|
|
254
|
+
~api_key=null,
|
|
255
|
+
~api_secret=null,
|
|
256
|
+
m
|
|
257
|
+
) =
|
|
258
|
+
audioscrobbler.api.apply_meta(
|
|
259
|
+
username=username,
|
|
260
|
+
password=password,
|
|
261
|
+
session_key=session_key,
|
|
262
|
+
api_key=api_key,
|
|
263
|
+
api_secret=api_secret,
|
|
264
|
+
name="audioscrobbler.api.track.updateNowPlaying",
|
|
265
|
+
audioscrobbler.api.track.updateNowPlaying,
|
|
266
|
+
m
|
|
267
|
+
)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Submit a track to the audioscrobbler
|
|
271
|
+
# `track.scrobble` API.
|
|
272
|
+
# @category Interaction
|
|
273
|
+
def audioscrobbler.api.track.scrobble(
|
|
274
|
+
~username,
|
|
275
|
+
~password,
|
|
276
|
+
~session_key=null,
|
|
277
|
+
~api_key=null,
|
|
278
|
+
~api_secret=null,
|
|
279
|
+
~artist,
|
|
280
|
+
~track,
|
|
281
|
+
~timestamp=null,
|
|
282
|
+
~album=null,
|
|
283
|
+
~context=null,
|
|
284
|
+
~streamId=null,
|
|
285
|
+
~chosenByUser=true,
|
|
286
|
+
~trackNumber=null,
|
|
287
|
+
~mbid=null,
|
|
288
|
+
~albumArtist=null,
|
|
289
|
+
~duration=null
|
|
290
|
+
) =
|
|
291
|
+
session_key =
|
|
292
|
+
session_key
|
|
293
|
+
?? audioscrobbler.auth(
|
|
294
|
+
username=username,
|
|
295
|
+
password=password,
|
|
296
|
+
api_key=api_key,
|
|
297
|
+
api_secret=api_secret
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
params =
|
|
301
|
+
[
|
|
302
|
+
("track", track),
|
|
303
|
+
("artist", artist),
|
|
304
|
+
("timestamp", string(timestamp ?? time())),
|
|
305
|
+
...(null.defined(album) ? [("album", null.get(album))] : []),
|
|
306
|
+
...(null.defined(context) ? [("context", null.get(context))] : []),
|
|
307
|
+
...(null.defined(streamId) ? [("streamId", null.get(streamId))] : []),
|
|
308
|
+
("chosenByUser", chosenByUser ? "1" : "0"),
|
|
309
|
+
...(
|
|
310
|
+
null.defined(trackNumber)
|
|
311
|
+
? [("trackNumber", string((null.get(trackNumber) : int)))]
|
|
312
|
+
: []
|
|
313
|
+
),
|
|
314
|
+
...(null.defined(mbid) ? [("mbid", null.get(mbid))] : []),
|
|
315
|
+
...(
|
|
316
|
+
null.defined(albumArtist)
|
|
317
|
+
? [("albumArtist", null.get(albumArtist))]
|
|
318
|
+
: []
|
|
319
|
+
),
|
|
320
|
+
...(
|
|
321
|
+
null.defined(duration)
|
|
322
|
+
? [("duration", string((null.get(duration) : int)))]
|
|
323
|
+
: []
|
|
324
|
+
)
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
log.info(
|
|
328
|
+
label="audioscrobbler.api.track.scrobble",
|
|
329
|
+
"Submitting updateNowPlaying with: #{params}"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
resp =
|
|
333
|
+
audioscrobbler.request(
|
|
334
|
+
api_key=api_key,
|
|
335
|
+
api_secret=api_secret,
|
|
336
|
+
[...params, ("method", "track.scrobble"), ("sk", session_key)]
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
audioscrobbler.check_response(resp)
|
|
340
|
+
|
|
341
|
+
try
|
|
342
|
+
let xml.parse (v :
|
|
343
|
+
{
|
|
344
|
+
lfm: {
|
|
345
|
+
scrobbles: {
|
|
346
|
+
scrobble: {
|
|
347
|
+
track: string.{ xml_params: {corrected: int} },
|
|
348
|
+
artist: string.{ xml_params: {corrected: int} },
|
|
349
|
+
album: string?.{ xml_params: {corrected: int} },
|
|
350
|
+
albumArtist: string?.{ xml_params: {corrected: int} },
|
|
351
|
+
timestamp: float,
|
|
352
|
+
ignoredMessage: {xml_params: {code: int}}
|
|
353
|
+
},
|
|
354
|
+
xml_params: {ignored: int, accepted: int}
|
|
355
|
+
},
|
|
356
|
+
xml_params: {status: string}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
) = resp
|
|
360
|
+
|
|
361
|
+
log.info(
|
|
362
|
+
label="audioscrobbler.api.track.scrobble",
|
|
363
|
+
"Done submitting scrobble with: #{params}"
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
v
|
|
367
|
+
catch err do
|
|
368
|
+
error.raise(
|
|
369
|
+
error.invalid,
|
|
370
|
+
"Invalid response: #{resp}, error: #{err}"
|
|
371
|
+
)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Submit a track to the audioscrobbler
|
|
376
|
+
# `track.scrobble` API using its metadata.
|
|
377
|
+
# @category Interaction
|
|
378
|
+
def audioscrobbler.api.track.scrobble.metadata(
|
|
379
|
+
~username,
|
|
380
|
+
~password,
|
|
381
|
+
~session_key=null,
|
|
382
|
+
~api_key=null,
|
|
383
|
+
~api_secret=null,
|
|
384
|
+
m
|
|
385
|
+
) =
|
|
386
|
+
audioscrobbler.api.apply_meta(
|
|
387
|
+
username=username,
|
|
388
|
+
password=password,
|
|
389
|
+
session_key=session_key,
|
|
390
|
+
api_key=api_key,
|
|
391
|
+
api_secret=api_secret,
|
|
392
|
+
name="audioscrobbler.api.track.scrobble",
|
|
393
|
+
audioscrobbler.api.track.scrobble,
|
|
394
|
+
m
|
|
395
|
+
)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Submit songs using audioscrobbler, respecting the full protocol:
|
|
399
|
+
# First signal song as now playing when starting, and
|
|
400
|
+
# then submit song when it ends.
|
|
401
|
+
# @category Interaction
|
|
402
|
+
# @flag extra
|
|
403
|
+
# @param ~source Source for tracks. Should be one of: "broadcast", "user", "recommendation" or "unknown". Since liquidsoap is intended for radio broadcasting, this is the default. Sources other than user don't need duration to be set.
|
|
404
|
+
# @param ~delay Submit song when there is only this delay left, in seconds.
|
|
405
|
+
# @param ~force If remaining time is null, the song will be assumed to be skipped or cut, and not submitted. Set this to `true` to prevent this behavior
|
|
406
|
+
# @param ~metadata_preprocessor Metadata pre-processor callback. Can be used to change metadata on-the-fly before sending to nowPlaying/scrobble. If returning an empty metadata, nothing is sent at all.
|
|
407
|
+
def audioscrobbler.submit(
|
|
408
|
+
~username,
|
|
409
|
+
~password,
|
|
410
|
+
~api_key=null,
|
|
411
|
+
~api_secret=null,
|
|
412
|
+
~delay=10.,
|
|
413
|
+
~force=false,
|
|
414
|
+
~metadata_preprocessor=fun (m) -> m,
|
|
415
|
+
s
|
|
416
|
+
) =
|
|
417
|
+
session_key =
|
|
418
|
+
audioscrobbler.auth(
|
|
419
|
+
username=username,
|
|
420
|
+
password=password,
|
|
421
|
+
api_key=api_key,
|
|
422
|
+
api_secret=api_secret
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
def now_playing(m) =
|
|
426
|
+
try
|
|
427
|
+
audioscrobbler.api.track.updateNowPlaying.metadata(
|
|
428
|
+
username=username,
|
|
429
|
+
password=password,
|
|
430
|
+
api_key=api_key,
|
|
431
|
+
api_secret=api_secret,
|
|
432
|
+
session_key=session_key,
|
|
433
|
+
metadata_preprocessor(m)
|
|
434
|
+
)
|
|
435
|
+
catch err do
|
|
436
|
+
log.important(
|
|
437
|
+
"Error while submitting nowplaying info for #{source.id(s)}: #{err}"
|
|
438
|
+
)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
s = source.methods(s)
|
|
443
|
+
s.on_metadata(synchronous=false, now_playing)
|
|
444
|
+
|
|
445
|
+
f =
|
|
446
|
+
fun (rem, m) ->
|
|
447
|
+
# Avoid skipped songs
|
|
448
|
+
if
|
|
449
|
+
rem > 0. or force
|
|
450
|
+
then
|
|
451
|
+
thread.run(
|
|
452
|
+
delay=0.,
|
|
453
|
+
{
|
|
454
|
+
try
|
|
455
|
+
audioscrobbler.api.track.scrobble.metadata(
|
|
456
|
+
username=username,
|
|
457
|
+
password=password,
|
|
458
|
+
api_key=api_key,
|
|
459
|
+
api_secret=api_secret,
|
|
460
|
+
session_key=session_key,
|
|
461
|
+
metadata_preprocessor(m)
|
|
462
|
+
)
|
|
463
|
+
catch err do
|
|
464
|
+
log.important(
|
|
465
|
+
"Error while submitting scrobble info for #{source.id(s)}: #{
|
|
466
|
+
err
|
|
467
|
+
}"
|
|
468
|
+
)
|
|
469
|
+
end
|
|
470
|
+
}
|
|
471
|
+
)
|
|
472
|
+
else
|
|
473
|
+
log(
|
|
474
|
+
label="audioscrobbler.submit",
|
|
475
|
+
level=4,
|
|
476
|
+
"Remaining time null: will not submit song (song skipped ?)"
|
|
477
|
+
)
|
|
478
|
+
end
|
|
479
|
+
s.on_position(synchronous=true, remaining=true, position=delay, f)
|
|
480
|
+
|
|
481
|
+
(s : source)
|
|
482
|
+
end
|