liquidsoap-prettier 1.8.2 → 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 (67) hide show
  1. package/.github/workflows/check-formatting.yml +32 -0
  2. package/README.md +31 -5
  3. package/package.json +1 -1
  4. package/src/cli.js +104 -9
  5. package/tests/liq/audio.liq +460 -0
  6. package/tests/liq/autocue.liq +1081 -0
  7. package/tests/liq/clock.liq +14 -0
  8. package/tests/liq/cron.liq +74 -0
  9. package/tests/liq/error.liq +48 -0
  10. package/tests/liq/extra/audio.liq +677 -0
  11. package/tests/liq/extra/audioscrobbler.liq +482 -0
  12. package/tests/liq/extra/deprecations.liq +976 -0
  13. package/tests/liq/extra/externals.liq +196 -0
  14. package/tests/liq/extra/fades.liq +260 -0
  15. package/tests/liq/extra/file.liq +66 -0
  16. package/tests/liq/extra/http.liq +160 -0
  17. package/tests/liq/extra/interactive.liq +917 -0
  18. package/tests/liq/extra/metadata.liq +75 -0
  19. package/tests/liq/extra/native.liq +201 -0
  20. package/tests/liq/extra/openai.liq +150 -0
  21. package/tests/liq/extra/server.liq +177 -0
  22. package/tests/liq/extra/source.liq +476 -0
  23. package/tests/liq/extra/spinitron.liq +272 -0
  24. package/tests/liq/extra/telnet.liq +266 -0
  25. package/tests/liq/extra/video.liq +59 -0
  26. package/tests/liq/extra/visualization.liq +68 -0
  27. package/tests/liq/fades.liq +941 -0
  28. package/tests/liq/ffmpeg.liq +605 -0
  29. package/tests/liq/file.liq +387 -0
  30. package/tests/liq/getter.liq +74 -0
  31. package/tests/liq/hls.liq +329 -0
  32. package/tests/liq/http.liq +1048 -0
  33. package/tests/liq/http_codes.liq +447 -0
  34. package/tests/liq/icecast.liq +58 -0
  35. package/tests/liq/io.liq +106 -0
  36. package/tests/liq/liquidsoap.liq +31 -0
  37. package/tests/liq/list.liq +440 -0
  38. package/tests/liq/log.liq +47 -0
  39. package/tests/liq/lufs.liq +295 -0
  40. package/tests/liq/math.liq +23 -0
  41. package/tests/liq/medialib.liq +752 -0
  42. package/tests/liq/metadata.liq +253 -0
  43. package/tests/liq/nfo.liq +258 -0
  44. package/tests/liq/null.liq +71 -0
  45. package/tests/liq/playlist.liq +1347 -0
  46. package/tests/liq/predicate.liq +106 -0
  47. package/tests/liq/process.liq +93 -0
  48. package/tests/liq/profiler.liq +5 -0
  49. package/tests/liq/protocols.liq +1139 -0
  50. package/tests/liq/ref.liq +28 -0
  51. package/tests/liq/replaygain.liq +135 -0
  52. package/tests/liq/request.liq +467 -0
  53. package/tests/liq/resolvers.liq +33 -0
  54. package/tests/liq/runtime.liq +70 -0
  55. package/tests/liq/server.liq +99 -0
  56. package/tests/liq/settings.liq +41 -0
  57. package/tests/liq/socket.liq +33 -0
  58. package/tests/liq/source.liq +362 -0
  59. package/tests/liq/sqlite.liq +161 -0
  60. package/tests/liq/stdlib.liq +172 -0
  61. package/tests/liq/string.liq +476 -0
  62. package/tests/liq/switches.liq +197 -0
  63. package/tests/liq/testing.liq +37 -0
  64. package/tests/liq/thread.liq +161 -0
  65. package/tests/liq/tracks.liq +100 -0
  66. package/tests/liq/utils.liq +81 -0
  67. package/tests/liq/video.liq +918 -0
@@ -0,0 +1,161 @@
1
+ # Run a function in a separate thread.
2
+ # @category Programming
3
+ # @param ~fast Whether the thread is supposed to return quickly or not. Typically, blocking tasks (e.g. fetching data over the internet) should not be considered to be fast. When set to `false` its priority will be lowered below that of request resolutions and fast timeouts. This is only effective if you set a dedicated queue for fast tasks, see the "scheduler" settings for more details.
4
+ # @param ~delay Delay (in seconds) after which the thread should be launched.
5
+ # @param ~every How often (in seconds) the thread should be run. If negative or `null`, run once.
6
+ # @param ~on_error Error callback executed when an error occurred while running the given function. When passed, \
7
+ # all raised errors are silenced unless re-raised by the callback.
8
+ # @param f Function to execute.
9
+ def replaces thread.run(~fast=true, ~delay=0., ~on_error=null, ~every=null, f) =
10
+ every = every ?? getter(-1.)
11
+
12
+ def f() =
13
+ ignore(f() == ())
14
+ getter.get(every)
15
+ end
16
+
17
+ on_error =
18
+ null.map(
19
+ fun (fn) ->
20
+ fun (err) ->
21
+ begin
22
+ (fn(err) : unit)
23
+ (-1.)
24
+ end,
25
+ on_error
26
+ )
27
+
28
+ thread.run.recurrent(fast=fast, delay=delay, on_error=on_error, f)
29
+ end
30
+
31
+ # Unless told not to, we want to batch thread.when calls when the getter is a fixed
32
+ # value. We batch them by getter value. All callbacks are expected to be fast so we
33
+ # do a single global callback for all batched calls. After tasks_count_warning is
34
+ # reached that they might need to refactor their code.
35
+ let thread.when =
36
+ {tasks_count = ref(0), tasks_count_warning = 70, batches = ref([])}
37
+
38
+ # @flag hidden
39
+ def thread.when.add_to_batch(~every, check) =
40
+ current_batches = thread.when.batches()
41
+
42
+ if
43
+ list.assoc.mem(every, current_batches)
44
+ then
45
+ tasks = list.assoc(every, current_batches)
46
+ tasks := [check, ...tasks()]
47
+ else
48
+ tasks = ref([check])
49
+ errors = ref([])
50
+ def exec_task(fn) =
51
+ try
52
+ fn()
53
+ catch err do
54
+ errors := [err, ...errors()]
55
+ end
56
+ end
57
+ def check_tasks() =
58
+ current_tasks = tasks()
59
+ list.iter(exec_task, current_tasks)
60
+ current_errors = errors()
61
+
62
+ errors := []
63
+
64
+ if
65
+ current_errors != []
66
+ then
67
+ errors_list =
68
+ string.concat(
69
+ separator=", ",
70
+ list.map(string, current_errors)
71
+ )
72
+ log.critical(
73
+ label="thread.when",
74
+ "Error(s) while executing batched `thread.when`: #{errors_list}"
75
+ )
76
+ end
77
+
78
+ every
79
+ end
80
+ thread.run.recurrent(fast=true, delay=0., on_error=null, check_tasks)
81
+ thread.when.batches := [(every, tasks), ...current_batches]
82
+ end
83
+ end
84
+
85
+ # Execute a callback when a predicate is `true`. The predicate
86
+ # is checked `every` seconds and the callback is
87
+ # called when the predicate returns `true` after having been
88
+ # `false`, following the same semantics as `predicate.activates`.
89
+ # @category Programming
90
+ # @param ~fast Whether the callback is supposed to return quickly or not.
91
+ # @param ~init Detect at beginning.
92
+ # @param ~every How often (in sec.) to check for the predicate.
93
+ # @param ~once Execute the function only once.
94
+ # @param ~changed Execute the function only if the predicate was false when last checked.
95
+ # @param ~batched When `fast` is `true`, `every` is a fixed value, `on_error` is `null` and `once` is `false`, \
96
+ # we batch callbacks to make it more efficient. Set this argument to `false` to disable that.
97
+ # @param ~on_error Error callback executed when an error occurred while running the given function. When passed, \
98
+ # all raised errors are silenced unless re-raised by the callback.
99
+ # @param p Predicate indicating when to execute the function, typically a time interval such as `{10h-10h30}`.
100
+ # @param f Function to execute when the predicate is true.
101
+ def thread.when(
102
+ ~fast=true,
103
+ ~init=true,
104
+ ~every=getter(0.5),
105
+ ~once=false,
106
+ ~changed=true,
107
+ ~on_error=null,
108
+ ~batched=true,
109
+ p,
110
+ f
111
+ ) =
112
+ ref.incr(thread.when.tasks_count)
113
+ if
114
+ thread.when.tasks_count() == thread.when.tasks_count_warning + 1
115
+ then
116
+ log.critical(
117
+ label="thread.when",
118
+ "There are over #{thread.when.tasks_count_warning} `thread.when` tasks \
119
+ running! You might want to consider refactoring your script to use a \
120
+ single callback!"
121
+ )
122
+ end
123
+
124
+ p = once or not changed ? p : (predicate.activates(init=init, p))
125
+
126
+ def check() =
127
+ if
128
+ p()
129
+ then
130
+ f()
131
+ once ? (-1.) : (getter.get(every))
132
+ else
133
+ getter.get(every)
134
+ end
135
+ end
136
+
137
+ if
138
+ (
139
+ batched
140
+ and getter.is_constant(every)
141
+ and not null.defined(on_error)
142
+ and fast
143
+ and not once
144
+ )
145
+ then
146
+ thread.when.add_to_batch(every=getter.get(every), fun () -> ignore(check()))
147
+ else
148
+ thread.run.recurrent(fast=fast, delay=0., on_error=on_error, check)
149
+ end
150
+ end
151
+
152
+ # @flag hidden
153
+ def default_error_handler(~backtrace, err) =
154
+ log(
155
+ label="runtime",
156
+ level=1,
157
+ "Uncaught error #{err}!\n#{backtrace}"
158
+ )
159
+ end
160
+
161
+ thread.on_error(null, default_error_handler)
@@ -0,0 +1,100 @@
1
+ let source.mux = ()
2
+ let source.mux.track = ()
3
+
4
+ # Replace the audio track of a source.
5
+ # @category Source / Track processing
6
+ def source.mux.track.audio(~id=null, ~audio, s) =
7
+ source(id=id, source.tracks(s).{audio = audio})
8
+ end
9
+
10
+ # Replace the video track of a source.
11
+ # @category Source / Track processing
12
+ def source.mux.track.video(~id=null, ~video, s) =
13
+ source(id=id, source.tracks(s).{video = video})
14
+ end
15
+
16
+ # Replace the audio track of a source by the one of another source.
17
+ # @category Source / Track processing
18
+ # @param ~audio Source whose audio track is to be taken.
19
+ def source.mux.audio(~id=null, ~(audio:source), s) =
20
+ source.mux.track.audio(id=id, audio=source.tracks(audio).audio, s)
21
+ end
22
+
23
+ # Replace the video track of a source by the one of another source.
24
+ # @category Source / Track processing
25
+ # @param ~audio Source whose video track is to be taken.
26
+ def source.mux.video(~id=null, ~(video:source), s) =
27
+ source.mux.track.video(id=id, video=source.tracks(video).video, s)
28
+ end
29
+
30
+ # Replace the midi track of a source by the one of another source.
31
+ # @category Source / Track processing
32
+ # @param ~midi Source whose midi track is to be taken.
33
+ def source.mux.midi(~id=null, ~(midi:source), s) =
34
+ source(id=id, source.tracks(s).{midi = source.tracks(midi).midi})
35
+ end
36
+
37
+ let source.drop = ()
38
+
39
+ # Remove the audio track of a source.
40
+ # @category Source / Track processing
41
+ def source.drop.audio(~id=null, s) =
42
+ let {audio = _, ...tracks} = source.tracks(s)
43
+ source(id=id, tracks)
44
+ end
45
+
46
+ # Remove the video track of a source.
47
+ # @category Source / Track processing
48
+ def source.drop.video(~id=null, s) =
49
+ let {video = _, ...tracks} = source.tracks(s)
50
+ source(id=id, tracks)
51
+ end
52
+
53
+ # Remove the midi track of a source.
54
+ # @category Source / Track processing
55
+ def source.drop.midi(~id=null, s) =
56
+ let {midi = _, ...tracks} = source.tracks(s)
57
+ source(id=id, tracks)
58
+ end
59
+
60
+ # Remove the metadata track of a source.
61
+ # @category Source / Track processing
62
+ def source.drop.metadata(~id=null, s) =
63
+ let {metadata = _, ...tracks} = source.tracks(s)
64
+ source(id=id, tracks)
65
+ end
66
+
67
+ # Remove the track marks of a source.
68
+ # @category Source / Track processing
69
+ def source.drop.track_marks(~id=null, s) =
70
+ let {track_marks = _, ...tracks} = source.tracks(s)
71
+ source(id=id, tracks)
72
+ end
73
+
74
+ # Remove the metadata and track marks of a source.
75
+ # @category Source / Track processing
76
+ def source.drop.metadata_track_marks(~id=null, s) =
77
+ let {metadata = _, track_marks = _, ...tracks} = source.tracks(s)
78
+ source(id=id, tracks)
79
+ end
80
+
81
+ let settings.amplify =
82
+ settings.make.void(
83
+ "Settings for the amplify operator"
84
+ )
85
+
86
+ let settings.amplify.override =
87
+ settings.make(
88
+ description="Default metadata used to override amplification.",
89
+ "liq_amplify"
90
+ )
91
+
92
+ # @docof track.audio.amplify
93
+ def track.audio.amplify(
94
+ %argsof(track.audio.amplify[!override]),
95
+ ~override=getter({(settings.amplify.override() : string?)}),
96
+ v,
97
+ t
98
+ ) =
99
+ track.audio.amplify(%argsof(track.audio.amplify), v, t)
100
+ end
@@ -0,0 +1,81 @@
1
+ %ifdef environment
2
+ # Get the value of an environment variable.
3
+ # Returns `default` if the variable is not set.
4
+ # @category System
5
+ def environment.get(~default="", s) =
6
+ env = environment()
7
+ if
8
+ not list.assoc.mem(s, env)
9
+ then
10
+ default
11
+ else
12
+ list.assoc(default=default, s, env)
13
+ end
14
+ end
15
+ %endif
16
+
17
+ # Split the arguments of an url of the form `arg=bar&arg2=bar2` into
18
+ # `[("arg","bar"),("arg2","bar2")]`. The returned strings are decoded (see
19
+ # `url.decode`).
20
+ # @category String
21
+ # @param args Argument string to split.
22
+ def url.split_args(args) =
23
+ def f(args, x) =
24
+ if
25
+ x == ""
26
+ then
27
+ args
28
+ else
29
+ ret = r/=/.split(x)
30
+ arg = url.decode(list.nth(default="", ret, 0))
31
+ val = url.decode(list.nth(default="", ret, 1))
32
+ [...args, (arg, val)]
33
+ end
34
+ end
35
+
36
+ l = r/&/.split(args)
37
+ list.fold(f, [], l)
38
+ end
39
+
40
+ # Split an url of the form `foo?arg=bar&arg2=bar2` into
41
+ # `("foo",[("arg","bar"),("arg2","bar2")])`. The returned strings are decoded
42
+ # (see `url.decode`).
43
+ # @category String
44
+ # @param uri Url to split.
45
+ def url.split(uri) =
46
+ ret = r/(?<uri>[^\?]*)\?(?<args>.*)/.exec(uri).groups
47
+ args = ret["args"]
48
+ if
49
+ args != ""
50
+ then
51
+ (url.decode(ret["uri"]), url.split_args(args))
52
+ else
53
+ (url.decode(uri), [])
54
+ end
55
+ end
56
+
57
+ # Memoize the result of a function, making sure it is only executed once.
58
+ # @category Programming
59
+ def memoize(fn) =
60
+ cached_result = ref([])
61
+ fun () ->
62
+ begin
63
+ if
64
+ cached_result() != []
65
+ then
66
+ list.hd(cached_result())
67
+ else
68
+ result = fn()
69
+ cached_result := [result]
70
+ result
71
+ end
72
+ end
73
+ end
74
+
75
+ let duration = ()
76
+
77
+ # Convert a duration in seconds to hour/minutes/seconds
78
+ # @category Time
79
+ def duration.split(d) =
80
+ {hours = d / 3600, minutes = (d / 60) mod 60, seconds = (d mod 60)}
81
+ end