diff --git a/CHANGES.md b/CHANGES.md index cc939d7349..26ac685fca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -85,6 +85,9 @@ Changed: `nan != x` is always `true`. Use `float.is_nan` to test if a float is `nan`. - BREAKING: `replaygain` no longer takes `ebu_r128` parameter (#3438). - BREAKING: assume `replaygain_track_gain` always stores volume in _dB_ (#3438). +- BREAKING: protocols can now check for nested static uri. Typically, this means + that requests for an uri of the form: `annotate:key="value",...:/path/to/file.mp3` + is now considered infallible if `/path/to/file.mp3` can be decoded. - Added `parents` option of `file.mkdir` (#3600, #3601). - Added `forced_major_collections` record field to the result of `runtime.gc.stat()` and `runtime.gc.quick_stat()` (#3783). diff --git a/doc/content/migrating.md b/doc/content/migrating.md index 6f6aca2361..3a99da33b5 100644 --- a/doc/content/migrating.md +++ b/doc/content/migrating.md @@ -120,6 +120,22 @@ for more details. However, EBU R128 data is now extracted directly from metadata when available. So `replaygain` cannot control the gain type via this parameter anymore. +### Static requests + +Static requests detection can now work with nested requests. + +Typically, a request for this URI: `annotate:key="value",...:/path/to/file.mp3` will be +considered static if `/path/to/file.mp3` can be decoded. + +Practically, this means that more source will now be considered infallible, for instance +a `single` using the above uri. + +In most cases, this should improve the user experience when building new scripts and streaming +systems. + +In rare cases where you actually wanted a fallible source, you can still pass `fallible=true` to e.g. +the `single` operator or use the `fallible:` protocol. + ### String functions Some string functions have been updated to account for string encoding. In particular, `string.length` and `string.sub` now assume that their diff --git a/src/core/builtins/builtins_resolvers.ml b/src/core/builtins/builtins_resolvers.ml index c1af3259e0..5499a047a5 100644 --- a/src/core/builtins/builtins_resolvers.ml +++ b/src/core/builtins/builtins_resolvers.ml @@ -133,6 +133,9 @@ let _ = { Playlist_parser.strict; Playlist_parser.parser = fn }; Lang.unit) +let default_static = + Lang.eval ~cache:false ~typecheck:false ~stdlib:`Disabled "fun (_) -> false" + let _ = let log_p = [("", "", None)] in let log_t = Lang.fun_t [(false, "", Lang.string_t)] Lang.unit_t in @@ -153,11 +156,12 @@ let _ = Some (Lang.bool false), Some "if true, file is removed when it is finished." ); ( "static", - Lang.bool_t, - Some (Lang.bool false), + Lang.fun_t [(false, "", Lang.string_t)] Lang.bool_t, + Some default_static, Some - "if true, then requests can be resolved once and for all. Typically, \ - static protocols can be used to create infallible sources." ); + "When given an uri for the protocol, if it returns `true`, then \ + requests can be resolved once and for all. Typically, static \ + protocols can be used to create infallible sources." ); ( "syntax", Lang.string_t, Some (Lang.string "Undocumented"), @@ -185,7 +189,8 @@ let _ = let name = Lang.to_string (Lang.assoc "" 1 p) in let f = Lang.assoc "" 2 p in let temporary = Lang.to_bool (List.assoc "temporary" p) in - let static = Lang.to_bool (List.assoc "static" p) in + let static = List.assoc "static" p in + let static s = Lang.to_bool (Lang.apply static [("", Lang.string s)]) in let doc = Lang.to_string (List.assoc "doc" p) in let syntax = Lang.to_string (List.assoc "syntax" p) in Lang.add_protocol ~syntax ~doc ~static name (fun arg ~log timeout -> diff --git a/src/core/doc.ml b/src/core/doc.ml index 9d9afa045d..7604a93890 100644 --- a/src/core/doc.ml +++ b/src/core/doc.ml @@ -1 +1,22 @@ include Liquidsoap_lang.Doc + +(** Documentation for protocols. *) +module Protocol = struct + type t = { name : string; description : string; syntax : string } + + let db = ref [] + + let add ~name ~doc ~syntax = + let p = { name; description = doc; syntax } in + db := p :: !db + + let db () = List.sort compare !db + let count () = db () |> List.length + + let print_md print = + List.iter + (fun p -> + Printf.ksprintf print "### %s\n\n%s\n\nThe syntax is `%s`.\n\n" p.name + p.description p.syntax) + (db ()) +end diff --git a/src/core/lang.ml b/src/core/lang.ml index a5904d4a60..cca49f0597 100644 --- a/src/core/lang.ml +++ b/src/core/lang.ml @@ -1,7 +1,6 @@ include Liquidsoap_lang.Lang include Lang_source include Lang_encoder.L -module Doc = Liquidsoap_lang.Doc module Flags = Liquidsoap_lang.Flags module Http = Liq_http @@ -11,7 +10,7 @@ let () = Hooks_implementations.register () (** Helpers for defining protocols. *) let add_protocol ~syntax ~doc ~static name resolver = - Doc.Protocol.add ~name ~doc ~syntax ~static; + Doc.Protocol.add ~name ~doc ~syntax; let spec = { Request.static; resolve = resolver } in Plug.register Request.protocols ~doc name spec diff --git a/src/core/lang.mli b/src/core/lang.mli index dfcf39091e..b933b25e1f 100644 --- a/src/core/lang.mli +++ b/src/core/lang.mli @@ -62,7 +62,7 @@ val apply : ?pos:Liquidsoap_lang.Pos.t list -> value -> env -> value val add_protocol : syntax:string -> doc:string -> - static:bool -> + static:(string -> bool) -> string -> Request.resolver -> unit diff --git a/src/core/protocols/annotate.ml b/src/core/protocols/annotate.ml index d5aafc5aca..0630dacd11 100644 --- a/src/core/protocols/annotate.ml +++ b/src/core/protocols/annotate.ml @@ -66,5 +66,10 @@ let annotate s ~log _ = let () = Lang.add_protocol ~doc:"Add metadata to a request" - ~syntax:"annotate:key=\"val\",key2=\"val2\",...:uri" ~static:false + ~syntax:"annotate:key=\"val\",key2=\"val2\",...:uri" + ~static:(fun uri -> + try + let _, uri = parse uri in + Request.is_static uri + with _ -> false) "annotate" annotate diff --git a/src/core/protocols/mpd.ml b/src/core/protocols/mpd.ml index c4d6ea7444..fae9a6e60e 100644 --- a/src/core/protocols/mpd.ml +++ b/src/core/protocols/mpd.ml @@ -146,4 +146,6 @@ let mpd s ~log _ = let () = Lang.add_protocol ~doc:"Finds all files with a tag equal to a given value using mpd." - ~syntax:"mpd:tag=value" ~static:false "mpd" mpd + ~syntax:"mpd:tag=value" + ~static:(fun _ -> false) + "mpd" mpd diff --git a/src/core/request.ml b/src/core/request.ml index 11407289be..e6c6c5cd31 100644 --- a/src/core/request.ml +++ b/src/core/request.ml @@ -610,7 +610,7 @@ let get_decoder ~ctype r = (** Plugins registration. *) type resolver = string -> log:(string -> unit) -> float -> indicator option -type protocol = { resolve : resolver; static : bool } +type protocol = { resolve : resolver; static : string -> bool } let protocols_doc = "Methods to get a file. They are the first part of URIs: 'protocol:args'." @@ -621,9 +621,9 @@ let is_static s = if file_exists (home_unrelate s) then true else ( match parse_uri s with - | Some (proto, _) -> ( + | Some (proto, uri) -> ( match Plug.get protocols proto with - | Some handler -> handler.static + | Some handler -> handler.static uri | None -> false) | None -> false) diff --git a/src/core/request.mli b/src/core/request.mli index f5052c6558..80a8bd68b5 100644 --- a/src/core/request.mli +++ b/src/core/request.mli @@ -100,7 +100,7 @@ val from_id : int -> t option type resolver = string -> log:(string -> unit) -> float -> indicator option (** A protocol, which can resolve associated URIs. *) -type protocol = { resolve : resolver; static : bool } +type protocol = { resolve : resolver; static : string -> bool } (** A static request [r] is such that every resolving leads to the same file. Sometimes, it allows removing useless destroy/create/resolve. *) diff --git a/src/lang/doc.ml b/src/lang/doc.ml index 90d23ded51..5e3588cbd5 100644 --- a/src/lang/doc.ml +++ b/src/lang/doc.ml @@ -62,33 +62,6 @@ module Plug = struct let print_string = print_md end -(** Documentation for protocols. *) -module Protocol = struct - type t = { - name : string; - description : string; - syntax : string; - static : bool; - } - - let db = ref [] - - let add ~name ~doc ~syntax ~static = - let p = { name; description = doc; syntax; static } in - db := p :: !db - - let db () = List.sort compare !db - let count () = db () |> List.length - - let print_md print = - List.iter - (fun p -> - let static = if p.static then " This protocol is static." else "" in - Printf.ksprintf print "### %s\n\n%s\n\nThe syntax is `%s`.%s\n\n" p.name - p.description p.syntax static) - (db ()) -end - (** Documenentation for values. *) module Value = struct (** Documentation flags. *) diff --git a/src/libs/protocols.liq b/src/libs/protocols.liq index 71d51c4990..afa8d7b1c4 100644 --- a/src/libs/protocols.liq +++ b/src/libs/protocols.liq @@ -388,6 +388,23 @@ protocol.add( syntax="tmp:uri" ) +# Register fallible +# @flag hidden +def protocol.fallible(~rlog=_, ~maxtime=_, arg) = + arg +end + +protocol.add( + "fallible", + protocol.fallible, + doc= + "Mark the given uri as being fallible. This can be used to prevent a request \ + or source from being resolved once and for all and considered infallible \ + for the duration of the script, typically when debugging.", + static=fun (_) -> false, + syntax="fallible:uri" +) + let settings.protocol.ffmpeg = settings.make.protocol("FFmpeg") let settings.protocol.ffmpeg.path = settings.make( @@ -651,7 +668,7 @@ def protocol.stereo(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, temporary=true, "stereo", protocol.stereo, @@ -671,7 +688,7 @@ def protocol.copy(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "copy", protocol.copy, doc= @@ -699,7 +716,7 @@ def protocol.text2wave(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "text2wave", protocol.text2wave, doc= @@ -736,7 +753,7 @@ def protocol.pico2wave(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "pico2wave", protocol.pico2wave, doc= @@ -781,7 +798,7 @@ def protocol.gtts(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "gtts", protocol.gtts, doc= @@ -819,7 +836,7 @@ def protocol.macos_say(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "macos_say", protocol.macos_say, doc= @@ -846,7 +863,7 @@ def protocol.say(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, "say", protocol.say, doc= @@ -993,7 +1010,7 @@ def polly_protocol(~rlog=_, ~maxtime=_, text) = end protocol.add( - static=true, + static=fun (_) -> true, "polly", polly_protocol, doc= @@ -1070,7 +1087,7 @@ def synth_protocol(~rlog=_, ~maxtime=_, text) = end protocol.add( - static=true, + static=fun (_) -> true, temporary=true, "synth", synth_protocol, @@ -1101,7 +1118,7 @@ def file_protocol(~rlog=_, ~maxtime=_, arg) = end protocol.add( - static=true, + static=fun (_) -> true, temporary=false, "file", file_protocol, diff --git a/src/runtime/main.ml b/src/runtime/main.ml index a376d47205..4691f0c774 100644 --- a/src/runtime/main.ml +++ b/src/runtime/main.ml @@ -21,7 +21,6 @@ *****************************************************************************) module Runtime = Liquidsoap_lang.Runtime -module Doc = Liquidsoap_lang.Doc module Environment = Liquidsoap_lang.Environment module Profiler = Liquidsoap_lang.Profiler module Queue = Liquidsoap_lang.Queues.Queue