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.
Files changed (68) hide show
  1. package/.github/workflows/check-formatting.yml +32 -0
  2. package/README.md +31 -5
  3. package/dist/liquidsoap.cjs +4155 -3090
  4. package/package.json +1 -1
  5. package/src/cli.js +104 -9
  6. package/tests/liq/audio.liq +460 -0
  7. package/tests/liq/autocue.liq +1081 -0
  8. package/tests/liq/clock.liq +14 -0
  9. package/tests/liq/cron.liq +74 -0
  10. package/tests/liq/error.liq +48 -0
  11. package/tests/liq/extra/audio.liq +677 -0
  12. package/tests/liq/extra/audioscrobbler.liq +482 -0
  13. package/tests/liq/extra/deprecations.liq +976 -0
  14. package/tests/liq/extra/externals.liq +196 -0
  15. package/tests/liq/extra/fades.liq +260 -0
  16. package/tests/liq/extra/file.liq +66 -0
  17. package/tests/liq/extra/http.liq +160 -0
  18. package/tests/liq/extra/interactive.liq +917 -0
  19. package/tests/liq/extra/metadata.liq +75 -0
  20. package/tests/liq/extra/native.liq +201 -0
  21. package/tests/liq/extra/openai.liq +150 -0
  22. package/tests/liq/extra/server.liq +177 -0
  23. package/tests/liq/extra/source.liq +476 -0
  24. package/tests/liq/extra/spinitron.liq +272 -0
  25. package/tests/liq/extra/telnet.liq +266 -0
  26. package/tests/liq/extra/video.liq +59 -0
  27. package/tests/liq/extra/visualization.liq +68 -0
  28. package/tests/liq/fades.liq +941 -0
  29. package/tests/liq/ffmpeg.liq +605 -0
  30. package/tests/liq/file.liq +387 -0
  31. package/tests/liq/getter.liq +74 -0
  32. package/tests/liq/hls.liq +329 -0
  33. package/tests/liq/http.liq +1048 -0
  34. package/tests/liq/http_codes.liq +447 -0
  35. package/tests/liq/icecast.liq +58 -0
  36. package/tests/liq/io.liq +106 -0
  37. package/tests/liq/liquidsoap.liq +31 -0
  38. package/tests/liq/list.liq +440 -0
  39. package/tests/liq/log.liq +47 -0
  40. package/tests/liq/lufs.liq +295 -0
  41. package/tests/liq/math.liq +23 -0
  42. package/tests/liq/medialib.liq +752 -0
  43. package/tests/liq/metadata.liq +253 -0
  44. package/tests/liq/nfo.liq +258 -0
  45. package/tests/liq/null.liq +71 -0
  46. package/tests/liq/playlist.liq +1347 -0
  47. package/tests/liq/predicate.liq +106 -0
  48. package/tests/liq/process.liq +93 -0
  49. package/tests/liq/profiler.liq +5 -0
  50. package/tests/liq/protocols.liq +1139 -0
  51. package/tests/liq/ref.liq +28 -0
  52. package/tests/liq/replaygain.liq +135 -0
  53. package/tests/liq/request.liq +467 -0
  54. package/tests/liq/resolvers.liq +33 -0
  55. package/tests/liq/runtime.liq +70 -0
  56. package/tests/liq/server.liq +99 -0
  57. package/tests/liq/settings.liq +41 -0
  58. package/tests/liq/socket.liq +33 -0
  59. package/tests/liq/source.liq +362 -0
  60. package/tests/liq/sqlite.liq +161 -0
  61. package/tests/liq/stdlib.liq +172 -0
  62. package/tests/liq/string.liq +476 -0
  63. package/tests/liq/switches.liq +197 -0
  64. package/tests/liq/testing.liq +37 -0
  65. package/tests/liq/thread.liq +161 -0
  66. package/tests/liq/tracks.liq +100 -0
  67. package/tests/liq/utils.liq +81 -0
  68. package/tests/liq/video.liq +918 -0
@@ -0,0 +1,329 @@
1
+ let hls = {playlist = ()}
2
+
3
+ # Generate a main HLS playlist
4
+ # @category String
5
+ def hls.playlist.main(~extra_tags=[], ~prefix="", ~version=7, streams) =
6
+ prefix = prefix == "" or r/\/$/.test(prefix) ? prefix : "#{prefix}/"
7
+
8
+ streams =
9
+ list.fold(
10
+ fun (streams, s) ->
11
+ begin
12
+ let ({bandwidth, codecs, video_size?, ...s} :
13
+ string.{ bandwidth: int, codecs: string, video_size?: (int * int) }
14
+ ) = s
15
+ resolution =
16
+ if
17
+ null.defined(video_size)
18
+ then
19
+ let (w, h) = null.get(video_size)
20
+ ",RESOLUTION=#{w}x#{h}"
21
+ else
22
+ ""
23
+ end
24
+
25
+ [
26
+ ...streams,
27
+ "#EXT-X-STREAM-INF:BANDWIDTH=#{bandwidth},CODECS=#{
28
+ string.quote(codecs)
29
+ }#{resolution}",
30
+ "#{prefix}#{s}.m3u8"
31
+ ]
32
+ end,
33
+ [],
34
+ streams
35
+ )
36
+
37
+ string.concat(
38
+ separator="\r\n",
39
+ ["#EXTM3U", "#EXT-X-VERSION:#{version}", ...extra_tags, ...streams, ""]
40
+ )
41
+ end
42
+
43
+ # @docof output.file.hls
44
+ def replaces output.file.hls(
45
+ %argsof(output.file.hls[!main_playlist_writer]),
46
+ ~main_playlist_writer=null(
47
+ fun (~extra_tags, ~prefix, ~version, streams) ->
48
+ null(
49
+ hls.playlist.main(
50
+ extra_tags=extra_tags,
51
+ prefix=prefix,
52
+ version=version,
53
+ streams
54
+ )
55
+ )
56
+ ),
57
+ dir,
58
+ streams,
59
+ s
60
+ ) =
61
+ output.file.hls(
62
+ %argsof(output.file.hls[!main_playlist_writer]),
63
+ main_playlist_writer=main_playlist_writer,
64
+ dir,
65
+ streams,
66
+ s
67
+ )
68
+ end
69
+
70
+ let input.hls = ()
71
+
72
+ # Play an HLS stream.
73
+ # @category Source / Input / Active
74
+ # @param ~id Force the value of the source ID.
75
+ # @param ~reload How often (in seconds) the playlist should be reloaded.
76
+ # @param uri Playlist URI.
77
+ # @flag experimental
78
+ def input.hls.native(~id=null, ~reload=10., uri) =
79
+ playlistr = ref([])
80
+ sequence = ref(0)
81
+ playlist_uri = ref(uri)
82
+ id = string.id.default(default="input.hls.native", id)
83
+
84
+ def load_playlist() =
85
+ pl = request.create(playlist_uri())
86
+ if
87
+ request.resolve(pl)
88
+ then
89
+ pl = request.filename(pl)
90
+ m = r/#EXT-X-MEDIA-SEQUENCE:(\d+)/.exec(file.contents(pl))
91
+ pl_sequence = m[1]
92
+ log.info(
93
+ label=id,
94
+ "Sequence: " ^
95
+ pl_sequence
96
+ )
97
+ pl_sequence = int_of_string(default=0, pl_sequence)
98
+ files = playlist.parse(path=path.dirname(playlist_uri()) ^ "/", pl)
99
+
100
+ def file_request(idx, el) =
101
+ let (meta, file) = el
102
+
103
+ def escape(s) =
104
+ string.escape(encoding="ascii", s)
105
+ end
106
+
107
+ s =
108
+ list.fold(
109
+ fun (cur, el) -> "#{cur},#{fst(el)}=#{escape(snd(el))}",
110
+ "",
111
+ meta
112
+ )
113
+
114
+ s = if s == "" then file else "annotate:#{s}:#{file}" end
115
+ (pl_sequence + idx, s)
116
+ end
117
+
118
+ files = list.mapi(file_request, files)
119
+ let (first_idx, _) = list.hd(default=(-1, ""), playlistr())
120
+
121
+ def add_file(playlist, file) =
122
+ let (idx, _) = file
123
+ if
124
+ first_idx < idx and not list.assoc.mem(idx, playlist)
125
+ then
126
+ list.append(playlist, [file])
127
+ else
128
+ playlist
129
+ end
130
+ end
131
+
132
+ playlistr := list.fold(add_file, playlistr(), files)
133
+ else
134
+ log.severe(
135
+ label=id,
136
+ "Couldn't read playlist: request resolution failed."
137
+ )
138
+ playlistr := []
139
+ end
140
+
141
+ request.destroy(pl)
142
+ end
143
+
144
+ def rec next() =
145
+ if
146
+ list.length(playlistr()) > 0
147
+ then
148
+ let (_, ret) = list.hd(default=(1, ""), playlistr())
149
+ playlistr := list.tl(playlistr())
150
+ sequence := sequence() + 1
151
+ request.create(ret)
152
+ else
153
+ null
154
+ end
155
+ end
156
+
157
+ def find_stream() =
158
+ pl = request.create(playlist_uri())
159
+ if
160
+ request.resolve(pl)
161
+ then
162
+ plfile = request.filename(pl)
163
+ m =
164
+ r/#EXT-X-STREAM-INF[^\n]*\n([^\r\n]*)\r?\n/.exec(file.contents(plfile))
165
+
166
+ playlist_uri := list.assoc(default=playlist_uri(), 1, m)
167
+ if
168
+ not (string.contains(substring="/", playlist_uri()))
169
+ then
170
+ playlist_uri := path.dirname(request.uri(pl)) ^ "/" ^ playlist_uri()
171
+ end
172
+
173
+ log(
174
+ label=id,
175
+ "Playlist: " ^
176
+ playlist_uri()
177
+ )
178
+ end
179
+ end
180
+
181
+ find_stream()
182
+ s = request.dynamic(id=id, prefetch=10, next)
183
+ let {track_marks = _, ...tracks} = source.tracks(s)
184
+ s = source(tracks)
185
+ thread.run(every=reload, load_playlist)
186
+ s
187
+ end
188
+
189
+ let replaces input.hls = input.hls.native
190
+ %ifdef input.ffmpeg
191
+ let replaces input.hls = input.ffmpeg
192
+ %endif
193
+
194
+ # Play an HLS stream.
195
+ # @category Source / Input / Active
196
+ # @param ~id Force the value of the source ID.
197
+ # @param uri Playlist URI.
198
+ def input.hls(~id=null, uri) =
199
+ input.hls(id=id, uri)
200
+ end
201
+
202
+ let output.harbor.hls = ()
203
+
204
+ # @flag hidden
205
+ def output.harbor.hls.base(
206
+ %argsof(output.file.hls[!segment_name]),
207
+ ~segment_name,
208
+ ~tmpdir,
209
+ ~port,
210
+ ~path,
211
+ serve,
212
+ formats,
213
+ s
214
+ ) =
215
+ tmpdir = tmpdir ?? file.temp_dir("hls", "")
216
+
217
+ def content_type(fname) =
218
+ ext = file.extension(fname)
219
+ if
220
+ ext == ".m3u8"
221
+ then
222
+ "application/x-mpegURL"
223
+ else
224
+ def f(cur, el) =
225
+ format = snd(el)
226
+ if
227
+ ext == ".#{encoder.extension(format)}"
228
+ then
229
+ encoder.content_type(format)
230
+ else
231
+ cur
232
+ end
233
+ end
234
+
235
+ list.fold(f, "", formats)
236
+ end
237
+ end
238
+
239
+ serve(port=port, path=path, content_type=content_type, tmpdir)
240
+ output.file.hls(%argsof(output.file.hls), tmpdir, formats, s)
241
+ end
242
+
243
+ # Output the source stream to an HTTP live stream served from the harbor HTTP server.
244
+ # @category Source / Output
245
+ # @argsof output.file.hls
246
+ # @param ~headers Default response headers.
247
+ # @param ~port Port for incoming harbor (http) connections.
248
+ # @param ~path Base path for hls URIs.
249
+ # @param ~transport Http transport. Use `http.transport.ssl` or `http.transport.secure_transport`, when available, to enable HTTPS output
250
+ # @param ~tmpdir Directory for generated files.
251
+ # @param formats List of specifications for each stream: (name, format).
252
+ def replaces output.harbor.hls(
253
+ %argsof(output.file.hls[!segment_name]),
254
+ ~segment_name=(
255
+ fun (metadata) ->
256
+ "#{metadata.stream_name}_#{metadata.position}.#{metadata.extname}"
257
+ ),
258
+ ~headers=[("Access-Control-Allow-Origin", "*")],
259
+ ~port=8000,
260
+ ~path="/",
261
+ ~tmpdir=null,
262
+ ~transport=http.transport.unix,
263
+ formats,
264
+ s
265
+ ) =
266
+ def serve(~port, ~path, ~content_type, dir) =
267
+ harbor.http.static(
268
+ port=port,
269
+ path=path,
270
+ content_type=content_type,
271
+ headers=headers,
272
+ transport=transport,
273
+ dir
274
+ )
275
+ end
276
+
277
+ output.harbor.hls.base(
278
+ %argsof(output.file.hls),
279
+ path=path,
280
+ port=port,
281
+ tmpdir=tmpdir,
282
+ serve,
283
+ formats,
284
+ s
285
+ )
286
+ end
287
+
288
+ %ifdef harbor.https.static
289
+ # Output the source stream to an HTTP live stream served from the harbor HTTPS server.
290
+ # @category Source / Output
291
+ # @param ~headers Default response headers.
292
+ # @param ~port Port for incoming harbor (http) connections.
293
+ # @param ~path Base path for hls URIs.
294
+ # @param ~tmpdir Directory for generated files.
295
+ # @param formats List of specifications for each stream: (name, format).
296
+ def output.harbor.hls.https(
297
+ %argsof(output.file.hls[!segment_name]),
298
+ ~segment_name=(
299
+ fun (metadata) ->
300
+ "#{metadata.stream_name}_#{metadata.position}.#{metadata.extname}"
301
+ ),
302
+ ~headers=[("Access-Control-Allow-Origin", "*")],
303
+ ~port=8000,
304
+ ~path="/",
305
+ ~tmpdir=null,
306
+ formats,
307
+ s
308
+ ) =
309
+ def serve(~port, ~path, ~content_type, dir) =
310
+ harbor.https.static(
311
+ port=port,
312
+ path=path,
313
+ content_type=content_type,
314
+ headers=headers,
315
+ dir
316
+ )
317
+ end
318
+
319
+ output.harbor.hls.base(
320
+ %argsof(output.file.hls),
321
+ path=path,
322
+ port=port,
323
+ tmpdir=tmpdir,
324
+ serve,
325
+ formats,
326
+ s
327
+ )
328
+ end
329
+ %endif