Hi all !

I am preparing some extension of liquidsoap to allow implementating custom 
operations using harbor, telnet etc..

The main tool that I need for now is a source.insert_metadata operator in the 
scripting functions, so I have started looking at this.

The current insert_metadata returns a new source which has an internal 
metadata variable and inserts the content of this variable when it is not None 
during the next s#get_frame.

In the spirit of an object-like system, I figured out that we need a 
source.insert_metadata that applies to any source. The other alternative would 
have been something of the type:
  source.insert_metadata: source -> (metadata -> unit) * source
which I find not really attractive..

So, I have backported the method mentioned above to the main source class and 
added a method #insert_metadata.

It turned out that this conflicted with insert_metadata present in input.http 
and etc... In fact, I think now it generalizes this the following way: 
 * For non-buffered sources, insert_metadata adds metadata directly into the 
frame
 * For buffered sources, insert_metadata adds the metadata in the current 
buffer.

So, I have moved insert_metadata from the various generated sources into the 
main generated class. I also factorized the add_track_on_meta option there -- 
which needed a little trick on the general Generator type.

Finally, there was also a insert_metadata in encoded sources, with an implicit 
filtering. This was added some weeks ago. Those two functions do not 
necessarily conflict but, since the name is the same, I think this is good 
practice to make then consistent.

Therefore, I have moved the metadata filtering from Encoder to a general 
Metautils. We do not always want to filter inserted metadata, so I have added 
an option to bypass the filtering. A good default, I think is:
 * Metadata that come from the system are not filtered by default. This is the 
case on the main source class and for source.insert_metadata
 * Metadata that come from the environment are filtered by default. For 
instance, input.http, input.harbor, input.lastfm 
 * Metadata sent to the environement are filtered by default. This  applies 
only to encoded outputs for now.

I think this gives a nice generalization:
 * insert_metadata on sources inserts metadata directly during s#get
 * insert_metadata on buffered sources inserts metadata into the buffer
 * insert_metadata on encoded outputs inserts metadata in the encoded stream
All 3 functions have the same type and may or may nor filter metadata. 
Filtering is configured according to "metadata.export" (list of fields).

Additionally, insert_metadata for buffered sources has been merged into 
generated.ml.

I know this is a long mail, but the diff is huge too and I wanted to explain 
it..

Attached is the diff. I am, of course, waiting for your input before comiting.

I forgot: this diff removes the original insert_metadata operator. My plan is 
to implement it directly in utils.liq -- and to document/implement a similar 
operator using harbor and a HTTP GET request -- once those changes have been 
discussed..

Romain
Index: outputs/output.mli
===================================================================
--- outputs/output.mli	(révision 7979)
+++ outputs/output.mli	(copie de travail)
@@ -69,10 +69,14 @@
   method virtual output_stop : unit
 end
 
+(** Parameters needed to instantiate an encodedoutput. *)
+val encoded_proto : (string * Lang.t * Lang.value option * string option) list
+
 class virtual encoded :
   content_kind:Frame.content_kind ->
   output_kind:string ->
   name:string ->
+  filter:bool -> 
   infallible:bool ->
   on_start:(unit->unit) ->
   on_stop:(unit->unit) ->
@@ -84,7 +88,7 @@
   method output_send : Frame.t -> unit
 
   method virtual encode : Frame.t -> int -> int -> string
-  method virtual insert_metadata : Encoder.Meta.export_metadata -> unit
+  method virtual insert_metadata : Metautils.export_metadata -> unit
   method virtual send : string -> unit
 
   method virtual output_reset : unit
Index: outputs/icecast2.ml
===================================================================
--- outputs/icecast2.ml	(révision 7979)
+++ outputs/icecast2.ml	(copie de travail)
@@ -62,7 +62,7 @@
                               (Lang.string user_agent)
 
 let proto kind =
-  Output.proto @
+  Output.proto @ Output.encoded_proto @
   [ ("restart", Lang.bool_t, Some (Lang.bool false),
      Some "Restart output after a failure. By default, liquidsoap will stop \
            if the output failed.") ;
@@ -289,6 +289,7 @@
       mount
   in
 
+  let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
   let autostart = Lang.to_bool (List.assoc "start" p) in
   let infallible = not (Lang.to_bool (List.assoc "fallible" p)) in
   let on_start =
@@ -333,7 +334,7 @@
 
   inherit Output.encoded
             ~content_kind:kind ~output_kind:"output.icecast"
-            ~infallible ~autostart ~on_start ~on_stop
+            ~infallible ~autostart ~on_start ~on_stop ~filter
             ~name:mount source
 
   (** In this operator, we don't exactly follow the start/stop
@@ -379,7 +380,7 @@
           (k,(Hashtbl.find h k))::l
         with _ -> (k,d)::l
       in
-      let m = Encoder.Meta.to_metadata m in
+      let m = Metautils.to_metadata m in
       let def_title =
         match get m "uri" [] with
           | (_,s)::_ -> let title = Filename.basename s in
@@ -464,7 +465,7 @@
     assert (encoder = None) ;
     let enc = encoder_factory self#id in
     encoder <- 
-       Some (enc (Encoder.Meta.empty_metadata)) ; 
+       Some (enc (Metautils.empty_metadata)) ; 
     assert (Cry.get_status connection = Cry.Disconnected) ;
     begin match dumpfile with
       | Some f -> dump <- Some (open_out_bin f)
Index: outputs/file_output.ml
===================================================================
--- outputs/file_output.ml	(révision 7979)
+++ outputs/file_output.ml	(copie de travail)
@@ -22,7 +22,7 @@
 
 class file_output
   ~reload_delay ~reload_predicate ~reload_on_metadata
-  ~append ~perm ~dir_perm filename
+  ~append ~perm ~dir_perm filename ~filter
   ~infallible ~on_start ~on_stop ~autostart
   ~encoder_factory ~kind source =
 object (self)
@@ -30,7 +30,7 @@
   inherit
     Output.encoded
       ~infallible ~on_start ~on_stop ~autostart
-      ~output_kind:"to_file" ~name:filename
+      ~output_kind:"to_file" ~name:filename ~filter
       ~content_kind:kind source
 
   val mutable fd = None
@@ -47,7 +47,7 @@
     let filename = Utils.home_unrelate filename in
     let current_metadata = 
       match current_metadata with
-        | Some m -> Hashtbl.find (Encoder.Meta.to_metadata m)
+        | Some m -> Hashtbl.find (Metautils.to_metadata m)
         | None -> fun _ -> raise Not_found
     in
     (* Avoid / in metas for filename.. *)
@@ -71,7 +71,7 @@
     let meta = 
       match current_metadata with
         | Some m -> m
-        | None -> Encoder.Meta.empty_metadata
+        | None -> Metautils.empty_metadata
     in
     encoder <- Some (enc meta) ;
 
@@ -123,7 +123,7 @@
 let () =
   let kind = Lang.univ_t 1 in
   Lang.add_operator "output.file"
-    (Output.proto @ [
+    (Output.proto @ Output.encoded_proto @ [
        "append",
        Lang.bool_t,
        Some (Lang.bool false),
@@ -206,9 +206,10 @@
        in
        let filename = Lang.to_string (Lang.assoc "" 2 p) in
        let source = Lang.assoc "" 3 p in
+       let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
        let kind = Encoder.kind_of_format format in
          ((new file_output
              filename ~append ~perm ~dir_perm
-             ~infallible ~on_start ~on_stop
+             ~infallible ~on_start ~on_stop ~filter
              ~reload_delay ~reload_predicate ~reload_on_metadata
              ~autostart ~encoder_factory ~kind source):>Source.source))
Index: outputs/output.ml
===================================================================
--- outputs/output.ml	(révision 7979)
+++ outputs/output.ml	(copie de travail)
@@ -280,18 +280,22 @@
          ((new dummy ~kind ~on_start ~on_stop ~infallible ~autostart
              (List.assoc "" p)):>Source.source))
 
+let encoded_proto = 
+  [ "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+    Some "Filter received metadata." ]
+
 (** More concrete abstract-class, which takes care of the #output_send
   * method for outputs based on encoders. *)
 class virtual encoded
   ~content_kind ~output_kind ~name
-  ~infallible ~on_start ~on_stop
+  ~infallible ~on_start ~on_stop ~filter
   ~autostart source =
 object (self)
   inherit output
             ~infallible ~on_start ~on_stop
             ~content_kind ~output_kind ~name source autostart
 
-  method virtual insert_metadata : Encoder.Meta.export_metadata -> unit
+  method virtual insert_metadata : Metautils.export_metadata -> unit
   method virtual encode : Frame.t -> int -> int -> string
   method virtual send : string -> unit
 
@@ -301,7 +305,7 @@
         begin
           match Frame.get_metadata frame start with
             | None -> ()
-            | Some m -> self#insert_metadata (Encoder.Meta.export_metadata m)
+            | Some m -> self#insert_metadata (Metautils.export_metadata ~filter m)
         end ;
         let data =
           self#encode frame start (stop-start)
Index: sources/harbor_input.ml
===================================================================
--- sources/harbor_input.ml	(révision 7979)
+++ sources/harbor_input.ml	(copie de travail)
@@ -27,8 +27,8 @@
 
 (* {1 Input handling} *)
 
-class http_input_server ~kind ~dumpfile ~logfile
-                        ~bufferize ~max ~icy ~port
+class http_input_server ~kind ~dumpfile ~logfile ~track_on_meta
+                        ~bufferize ~max ~icy ~port ~filter
                         ~mountpoint ~on_connect ~on_disconnect
                         ~login ~debug p =
   let max_ticks = Frame.master_of_seconds max in
@@ -41,7 +41,7 @@
   inherit Generated.source
             (Generator.create
                ~log ~kind ~overfull:(`Drop_old max_ticks) `Undefined)
-            ~empty_on_abort:false ~bufferize as generated
+               ~empty_on_abort:false ~bufferize ~track_on_meta as generated
 
   val mutable relay_socket = None
   val mutable ns = []
@@ -51,22 +51,12 @@
   val mutable dump = None
   val mutable logf = None
 
+  method filter = filter
+
   method login : string*(string -> string -> bool) = login
 
   method stype = Source.Fallible
 
-  (* Insert metadata *)
-  method insert_metadata m =
-    (* Metadata may contain only the "song" value
-     * or "artist" and "title". Here, we use "song"
-     * as the "title" field if "title" is not provided. *)
-    if not (Hashtbl.mem m "title") then
-      (try Hashtbl.add m "title" (Hashtbl.find m "song") with _ -> ());
-    self#log#f 3 "New metadata chunk %s -- %s."
-      (try Hashtbl.find m "artist" with _ -> "?")
-      (try Hashtbl.find m "title" with _ -> "?") ;
-    Generator.add_metadata generator m
-
   method get_mime_type = mime_type
 
   method feed socket =
@@ -266,6 +256,9 @@
         Some (Lang.bool false),
         Some "Enable ICY (shoutcast) protocol.";
 
+        "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+        Some "Filter received metadata." ;
+
         "auth",
         Lang.fun_t [false,"",Lang.string_t;false,"",Lang.string_t] Lang.bool_t,
         Some
@@ -284,6 +277,9 @@
         Some "Log buffer status to file, for debugging purpose. \
               Disabled if empty.";
 
+        "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
+        Some "Treat new metadata as new track." ;
+
         "debug", Lang.bool_t, Some (Lang.bool false),
         Some "Run in debugging mode by not catching some exceptions.";
 
@@ -306,6 +302,7 @@
          let password = Lang.to_string (List.assoc "password" p) in
          let debug = Lang.to_bool (List.assoc "debug" p) in
          let icy = Lang.to_bool (List.assoc "icy" p) in
+         let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
          let port = Lang.to_int (List.assoc "port" p) in
          let auth_function = List.assoc "auth" p in
          let login user pass =
@@ -352,6 +349,9 @@
          in
          let bufferize = Lang.to_float (List.assoc "buffer" p) in
          let max = Lang.to_float (List.assoc "max" p) in
+         let track_on_meta =
+           Lang.to_bool (List.assoc "new_track_on_metadata" p)
+         in
          if bufferize >= max then
            raise (Lang.Invalid_value
                     (List.assoc "max" p,
@@ -372,8 +372,8 @@
            ignore
              (Lang.apply ~t:Lang.unit_t (List.assoc "on_disconnect" p) [])
          in
-         (new http_input_server ~kind
+         (new http_input_server ~kind ~track_on_meta
                    ~bufferize ~max ~login ~mountpoint
-                   ~dumpfile ~logfile ~icy ~port
+                   ~dumpfile ~logfile ~icy ~port ~filter
                    ~on_connect ~on_disconnect ~debug 
                    p :> Source.source))
Index: sources/generated.ml
===================================================================
--- sources/generated.ml	(révision 7979)
+++ sources/generated.ml	(copie de travail)
@@ -26,7 +26,7 @@
 (* Reads data from an audio buffer generator.
  * A thread safe generator should be used if it has to be fed concurrently.
  * Store [bufferize] seconds before declaring itself as ready. *)
-class virtual source ~bufferize ~empty_on_abort gen =
+class virtual source ~bufferize ~empty_on_abort ~track_on_meta gen =
   let bufferize = Frame.master_of_seconds bufferize in
 object (self)
 
@@ -66,6 +66,20 @@
         let l = Generator.remaining generator in
           if buffering && r <= bufferize then 0 else l
 
+  (* Insert metadata *)
+  method insert_metadata m =
+    let m = Metautils.to_metadata m in
+    (* Metadata may contain only the "song" value
+     * or "artist" and "title". Here, we use "song"
+     * as the "title" field if "title" is not provided. *)
+    if not (Hashtbl.mem m "title") then
+      (try Hashtbl.add m "title" (Hashtbl.find m "song") with _ -> ());
+    self#log#f 3 "New metadata chunk %s -- %s."
+      (try Hashtbl.find m "artist" with _ -> "?")
+      (try Hashtbl.find m "title" with _ -> "?") ;
+    Generator.add_metadata generator m ; 
+    if track_on_meta then Generator.add_break ~sync:`Ignore generator
+
   method private get_frame ab =
     buffering <- false ;
     if should_fail then begin
@@ -97,7 +111,7 @@
 class consumer ~kind generator =
 object
   inherit Source.source ~name:"buffer" kind
-  inherit source generator ~bufferize:0. ~empty_on_abort:true
+  inherit source generator ~bufferize:0. ~empty_on_abort:true ~track_on_meta:false
   method stype = Source.Fallible
 end
 
Index: sources/http_source.ml
===================================================================
--- sources/http_source.ml	(révision 7979)
+++ sources/http_source.ml	(copie de travail)
@@ -123,7 +123,8 @@
                   if !readcnt = metaint then begin
                     readcnt := 0;
                     match read_metadata socket with
-                      | Some m -> insert_metadata m
+                      | Some m ->
+                          insert_metadata m
                       | None -> ()
                   end ;
                   b,r
@@ -167,7 +168,7 @@
 class http ~kind
         ~playlist_mode ~poll_delay ~timeout ~track_on_meta ?(force_mime=None)
         ~bind_address ~autostart ~bufferize ~max
-        ~debug ?(logfile=None)
+        ~debug ?(logfile=None) ~filter
         ~user_agent url =
   let max_ticks = Frame.master_of_seconds (Pervasives.max max bufferize) in
   (* We need a temporary log until
@@ -179,7 +180,7 @@
   inherit
     Generated.source
       (Generator.create ~log ~kind ~overfull:(`Drop_old max_ticks) `Undefined)
-      ~empty_on_abort:false ~bufferize as generated
+      ~empty_on_abort:false ~bufferize ~track_on_meta as generated
 
   method stype = Source.Fallible
 
@@ -198,17 +199,14 @@
   val mutable relaying = autostart
   val mutable playlist_mode = playlist_mode
 
-  (* Insert metadata *)
-  method insert_metadata m =
-    self#log#f 3 "New metadata chunk: %s -- %s."
-                (try Hashtbl.find m "artist" with _ -> "?")
-                (try Hashtbl.find m "title" with _ -> "?") ;
-    Generator.add_metadata generator m ;
-    if track_on_meta then Generator.add_break ~sync:`Ignore generator
-
   method feeding ?(newstream=true) create_decoder socket chunked metaint =
     connected <- true ;
-    let read = read_stream socket chunked metaint self#insert_metadata in
+    let insert_metadata m = 
+      self#insert_metadata (Metautils.export_metadata ~filter m)
+    in
+    let read = 
+      read_stream socket chunked metaint insert_metadata
+    in
     let read =
       match logf with
         | None -> read
@@ -323,7 +321,7 @@
                 if not poll_should_stop then
                 let metas = Hashtbl.create 2 in
                   List.iter (fun (a,b) -> Hashtbl.add metas a b) m;
-                  self#insert_metadata metas;
+                  self#insert_metadata (Metautils.export_metadata ~filter metas);
                   self#private_connect uri
               in
               let randomize playlist =
@@ -461,6 +459,9 @@
       "timeout", Lang.float_t, Some (Lang.float 10.),
       Some "Timeout for http connection." ;
 
+      "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+      Some "Filter received metadata." ;
+
       "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
       Some "Treat new metadata as new track." ;
 
@@ -512,6 +513,7 @@
        in
        let url = Lang.to_string (List.assoc "" p) in
        let autostart = Lang.to_bool (List.assoc "autostart" p) in
+       let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
        let bind_address = Lang.to_string (List.assoc "bind_address" p) in
        let user_agent = Lang.to_string (List.assoc "user_agent" p) in
        let track_on_meta =
@@ -542,6 +544,6 @@
                    "Maximum buffering inferior to pre-buffered data"));
        let poll_delay = Lang.to_float (List.assoc "poll_delay" p) in
          ((new http ~kind ~playlist_mode ~timeout ~autostart ~track_on_meta
-                    ~force_mime ~bind_address ~poll_delay
+                    ~force_mime ~bind_address ~poll_delay ~filter
                     ~bufferize ~max ~debug ~logfile ~user_agent url)
             :> Source.source))
Index: sources/external_input.ml
===================================================================
--- sources/external_input.ml	(révision 7979)
+++ sources/external_input.ml	(copie de travail)
@@ -28,7 +28,7 @@
 exception Finished of string*bool
 
 class external_input ~kind ~restart ~bufferize ~channels
-                     ~restart_on_error ~max
+                     ~restart_on_error ~max ~track_on_meta
                      ~samplerate command =
   let abg_max_len = Frame.audio_of_seconds max in
   let in_freq = float samplerate in
@@ -44,7 +44,8 @@
   let priority = Tutils.Non_blocking in
 object (self)
   inherit Source.source kind
-  inherit Generated.source abg ~empty_on_abort:false ~bufferize
+  inherit Generated.source abg ~empty_on_abort:false 
+                           ~bufferize ~track_on_meta
 
   val mutable should_stop = false
 
@@ -155,6 +156,9 @@
         "restart_on_error", Lang.bool_t, Some (Lang.bool false),
         Some "Restart process when exited with error.";
 
+        "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
+        Some "Treat new metadata as new track." ;
+
         "", Lang.string_t, None,
         Some "Command to execute." ]
       ~kind:Lang.audio_any
@@ -172,7 +176,10 @@
          let restart_on_error =
            Lang.to_bool (List.assoc "restart_on_error" p)
          in
+         let track_on_meta =
+           Lang.to_bool (List.assoc "new_track_on_metadata" p)
+         in
          let max = Lang.to_float (List.assoc "max" p) in
           ((new external_input ~kind ~restart ~bufferize ~channels
-                               ~restart_on_error ~max
+                               ~restart_on_error ~max ~track_on_meta
                                ~samplerate command):>Source.source))
Index: sources/lastfm_input.ml
===================================================================
--- sources/lastfm_input.ml	(révision 7979)
+++ sources/lastfm_input.ml	(copie de travail)
@@ -24,13 +24,13 @@
 
 class lastfm ~kind ~autostart ~poll_delay ~track_on_meta 
              ~bufferize ~timeout ~bind_address ~user ~password 
-             ~debug ~max ~user_agent uri =
+             ~debug ~max ~user_agent ~filter uri =
  let playlist_mode = Http_source.First in
  let bufferize_time = Frame.master_of_seconds bufferize in
 object (self)
   inherit Http_source.http ~kind ~playlist_mode ~poll_delay ~timeout 
                            ~autostart ~bind_address ~bufferize 
-                           ~max ~track_on_meta 
+                           ~max ~track_on_meta ~filter 
                            ~debug ~user_agent uri as http
 
   val mutable session = None
@@ -87,7 +87,7 @@
       in
       let metas = Hashtbl.create 2 in
         List.iter (fun (a,b) -> Hashtbl.add metas a b) m;
-        http#insert_metadata metas;
+        http#insert_metadata (Metautils.export_metadata ~filter metas);
         http#connect uri
      with
        | Liqfm.Radio.Error e ->
@@ -145,6 +145,8 @@
         Some "Polling delay." ;
         "new_track_on_metadata", Lang.bool_t, Some (Lang.bool true),
         Some "Treat new metadata as new track." ;
+        "filter_metadata", Lang.bool_t, Some (Lang.bool true),
+        Some "Filter received metadata." ;
         "debug", Lang.bool_t, Some (Lang.bool false),
         Some "Run in debugging mode by not catching some exceptions." ;
         "max", Lang.float_t, Some (Lang.float 25.),
@@ -177,12 +179,13 @@
 	 let timeout = Lang.to_float (List.assoc "timeout" p) in
          let poll_delay =  Lang.to_float (List.assoc "poll_delay" p) in
          let max = Lang.to_float (List.assoc "max" p) in
+         let filter = Lang.to_bool (List.assoc "filter_metadata" p) in
          let user = Lang.to_string (List.assoc "user" p) in
          let password = Lang.to_string (List.assoc "password" p) in
          if bufferize > max then
            raise (Lang.Invalid_value
                     (List.assoc "max" p,
                      "Maximun buffering inferior to pre-buffered data"));
-           ((new lastfm ~kind ~autostart ~poll_delay ~bufferize
+           ((new lastfm ~kind ~autostart ~poll_delay ~bufferize ~filter
                         ~track_on_meta ~bind_address ~timeout ~max ~debug 
                         ~user_agent ~user ~password uri):>Source.source))
Index: stream/generator.mli
===================================================================
--- stream/generator.mli	(révision 7979)
+++ stream/generator.mli	(copie de travail)
@@ -28,6 +28,7 @@
   val clear : t -> unit
   val fill : t -> Frame.t -> unit
   val add_metadata : t -> Frame.metadata -> unit
+  val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
 end
 
 module Generator :
@@ -51,7 +52,8 @@
   val length : t -> int
   val remaining : t -> int
   val add_metadata : t -> Frame.metadata -> unit
-  val add_break : t -> unit
+  (* Sync argument is ignored here. *)
+  val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
   val remove : t -> int -> unit
   val feed : t ->
              ?copy:bool ->
Index: stream/generator.ml
===================================================================
--- stream/generator.ml	(révision 7979)
+++ stream/generator.ml	(copie de travail)
@@ -28,6 +28,7 @@
   val clear : t -> unit
   val fill : t -> Frame.t -> unit
   val add_metadata : t -> Frame.metadata -> unit
+  val add_break : ?sync:[`Strict|`Ignore|`Drop] -> t -> unit
 end
 
 module type S_Asio =
@@ -194,7 +195,7 @@
   let add_metadata fg m =
     fg.metadata <- fg.metadata @ [length fg, m]
 
-  let add_break fg =
+  let add_break ?sync fg =
     fg.breaks <- fg.breaks @ [length fg]
 
   (* Advance metadata and breaks by [len] ticks. *)
Index: tools/harbor.ml
===================================================================
--- tools/harbor.ml	(révision 7985)
+++ tools/harbor.ml	(copie de travail)
@@ -59,7 +59,8 @@
   inherit Source.source kind
 
   method virtual relay : (string*string) list -> Unix.file_descr -> unit
-  method virtual insert_metadata : (string, string) Hashtbl.t -> unit
+  method virtual filter : bool
+  method virtual insert_metadata : Metautils.export_metadata -> unit
   method virtual login : (string*(string -> string -> bool))
   method virtual is_taken : bool
   method virtual register_decoder : string -> unit
@@ -378,7 +379,8 @@
               let args = 
                 Hashtbl.fold f args (Hashtbl.create (Hashtbl.length args))
               in
-              s#insert_metadata args ;
+              let filter = s#filter in
+              s#insert_metadata (Metautils.export_metadata ~filter args) ;
               raise (Answer (fun () -> write_answer c ans))
      | _ -> raise (Answer ans_500)
   in
Index: tools/metautils.mli
===================================================================
--- tools/metautils.mli	(révision 0)
+++ tools/metautils.mli	(révision 0)
@@ -0,0 +1,31 @@
+(*****************************************************************************
+
+  Liquidsoap, a programmable audio stream generator.
+  Copyright 2003-2010 Savonet team
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details, fully stated in the COPYING
+  file at the root of the liquidsoap distribution.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ *****************************************************************************)
+
+(* I would like to use a private
+ * type here but its only for ocaml >= 3.11.. *)
+type export_metadata
+
+val export_metadata : filter:bool -> Frame.metadata -> export_metadata
+    
+val to_metadata : export_metadata -> Frame.metadata
+    
+val empty_metadata : export_metadata
Index: tools/metautils.ml
===================================================================
--- tools/metautils.ml	(révision 0)
+++ tools/metautils.ml	(révision 0)
@@ -0,0 +1,47 @@
+(*****************************************************************************
+
+  Liquidsoap, a programmable audio stream generator.
+  Copyright 2003-2010 Savonet team
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details, fully stated in the COPYING
+  file at the root of the liquidsoap distribution.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ *****************************************************************************)
+
+
+let conf_meta =
+  Dtools.Conf.void ~p:(Configure.conf#plug "metadata") "Metadata settings"
+    ~comments:["Settings for the encoded metadata."]
+
+(** The list of metadata that labels
+  * are exported when encoding data. *)
+let conf_export_metadata =
+  Dtools.Conf.list ~p:(conf_meta#plug "export") "Exported metdata"
+    ~d:["artist";"title";"album";"genre";"date";"tracknumber";
+        "comment"]
+    ~comments:["The list of labels of exported metadata."]
+
+type export_metadata = Frame.metadata
+    
+let export_metadata ~filter m =
+  let ret = Hashtbl.create 10 in
+  let l = conf_export_metadata#get in
+  Hashtbl.iter (fun x y -> if not filter || List.mem (String.lowercase x) l then
+                           Hashtbl.add ret x y) m;
+  ret
+    
+let to_metadata m = m
+
+let empty_metadata = Hashtbl.create 0
Index: ogg_formats/ogg_flac_encoder.ml
===================================================================
--- ogg_formats/ogg_flac_encoder.ml	(révision 7979)
+++ ogg_formats/ogg_flac_encoder.ml	(copie de travail)
@@ -106,7 +106,7 @@
     | Encoder.Ogg.Flac flac -> 
        let reset ogg_enc m =
          let comments = 
-           Utils.list_of_metadata (Encoder.Meta.to_metadata m) 
+           Utils.list_of_metadata (Metautils.to_metadata m) 
          in 
          let enc =
            create_encoder ~flac ~comments ()
Index: ogg_formats/dirac_encoder.ml
===================================================================
--- ogg_formats/dirac_encoder.ml	(révision 7979)
+++ ogg_formats/dirac_encoder.ml	(copie de travail)
@@ -144,7 +144,7 @@
     | Encoder.Ogg.Dirac dirac ->
        let reset ogg_enc m =
          let metadata =
-           Utils.list_of_metadata (Encoder.Meta.to_metadata m)
+           Utils.list_of_metadata (Metautils.to_metadata m)
          in
          let enc =
            create_encoder ~metadata dirac
Index: ogg_formats/theora_encoder.ml
===================================================================
--- ogg_formats/theora_encoder.ml	(révision 7979)
+++ ogg_formats/theora_encoder.ml	(copie de travail)
@@ -164,7 +164,7 @@
     | Encoder.Ogg.Theora theora -> 
        let reset ogg_enc m =
          let metadata = 
-           Utils.list_of_metadata (Encoder.Meta.to_metadata m) 
+           Utils.list_of_metadata (Metautils.to_metadata m) 
          in 
          let enc =
            create_encoder ~theora ~metadata ()
Index: ogg_formats/speex_encoder.ml
===================================================================
--- ogg_formats/speex_encoder.ml	(révision 7979)
+++ ogg_formats/speex_encoder.ml	(copie de travail)
@@ -167,7 +167,7 @@
    | Encoder.Ogg.Speex speex -> 
       let reset ogg_enc m =
         let m = 
-          Utils.list_of_metadata (Encoder.Meta.to_metadata m) 
+          Utils.list_of_metadata (Metautils.to_metadata m) 
         in
         let title =
           try
Index: ogg_formats/vorbis_encoder.ml
===================================================================
--- ogg_formats/vorbis_encoder.ml	(révision 7979)
+++ ogg_formats/vorbis_encoder.ml	(copie de travail)
@@ -105,7 +105,7 @@
       let channels = vorbis.Encoder.Vorbis.channels in
       let samplerate = vorbis.Encoder.Vorbis.samplerate in
       let reset ogg_enc m =
-        let m =  (Encoder.Meta.to_metadata m) in
+        let m =  (Metautils.to_metadata m) in
         let get h k =
           try
             Some (Hashtbl.find h k)
Index: Makefile
===================================================================
--- Makefile	(révision 7991)
+++ Makefile	(copie de travail)
@@ -75,7 +75,7 @@
 	tools/liqMM.ml
 
 operators = \
-	operators/insert_metadata.ml operators/map_metadata.ml \
+	operators/map_metadata.ml \
 	operators/on_metadata.ml operators/store_metadata.ml \
 	operators/on_track.ml operators/on_end.ml operators/delay.ml \
 	operators/switch.ml operators/fade.ml operators/add.ml \
@@ -131,9 +131,9 @@
 #	io/marshal_io.ml
 
 tools = tools/doc.ml tools/plug.ml tools/utils.ml \
-	tools/rqueue.ml \
-	tools/liq_sockets.ml \
-	tools/wav.ml $(if $(BYTE),tools/dynliq.ml) configure.ml \
+	tools/rqueue.ml configure.ml \
+	tools/liq_sockets.ml tools/metautils.ml \
+	tools/wav.ml $(if $(BYTE),tools/dynliq.ml) \
 	tools/tutils.ml tools/http.ml tools/pool.ml
 
 stream =stream/frame.ml stream/generator.ml \
Index: operators/switch.ml
===================================================================
--- operators/switch.ml	(révision 7979)
+++ operators/switch.ml	(copie de travail)
@@ -45,6 +45,29 @@
   * or only at track limits (sensitive). *)
 type track_mode = Sensitive | Insensitive
 
+(** A source that inserts the given metadata
+  * in first returned frame. *)
+class replay ~kind meta src =
+object (self)
+  inherit operator kind [src]
+
+  val mutable first = true
+
+  method stype = src#stype
+  method is_ready = src#is_ready
+  method abort_track = src#abort_track
+  method remaining = src#remaining
+
+  method private get_frame ab =
+    let start = Frame.position ab in
+      src#get ab ;
+      if first then begin
+        if Frame.get_metadata ab start = None then
+          Frame.set_metadata ab start meta ;
+        first <- false
+      end
+end
+
 class virtual switch ~kind ~name
   ?(mode=Sensitive) ?(replay_meta=true) (cases : child list) =
 object (self)
@@ -159,7 +182,7 @@
                      * transition in between. *)
                     match c.cur_meta with
                       | Some m when replay_meta ->
-                          new Insert_metadata.replay ~kind m c.source
+                          new replay ~kind m c.source
                       | _ -> c.source
                   in
                   let s =
Index: operators/insert_metadata.ml
===================================================================
--- operators/insert_metadata.ml	(révision 7979)
+++ operators/insert_metadata.ml	(copie de travail)
@@ -1,126 +0,0 @@
-(*****************************************************************************
-
-  Liquidsoap, a programmable audio stream generator.
-  Copyright 2003-2010 Savonet team
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details, fully stated in the COPYING
-  file at the root of the liquidsoap distribution.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
- *****************************************************************************)
-
-open Source
-open Genlex
-exception Error
-
-class insert_metadata ~kind source =
-object (self)
-  inherit operator kind [source] as super
-
-  method stype = source#stype
-  method is_ready = source#is_ready
-  method remaining = source#remaining
-  method abort_track = source#abort_track
-
-  val mutable metadata = None
-  val mutable ns = []
-
-  method private wake_up l =
-    super#wake_up l ;
-    if ns = [] then
-      ns <- Server.register [self#id] "insert_metadata" ;
-    self#set_id (Server.to_string ns) ;
-    Server.add ~ns "insert" ~usage:"insert key1=\"val1\",key2=\"val2\",.."
-               ~descr:"Insert a metadata chunk."
-      (fun s ->
-         let l = String.length s in
-         let pos = ref 0 in
-         let str =
-           Stream.from (fun i ->
-                          pos := i ;
-                          if i<l then Some s.[i] else None)
-         in
-         let lexer = make_lexer [",";"="] str in
-         let m = Hashtbl.create 10 in
-         let state = ref `Ident in
-           try
-             while true do
-               match Stream.next lexer with
-                 | Kwd ","   when `Comma = !state ->
-                     state := `Ident
-                 | Ident key when `Ident = !state ->
-                     state := `Internal ;
-                     begin match Stream.next lexer with
-                       | Kwd "=" -> begin match Stream.next lexer with
-                           | String s ->
-                               Hashtbl.add m key s ;
-                               state := `Comma
-                           | _ -> raise Error
-                         end
-                       | _ -> raise Error
-                     end
-                 | _ -> raise Error
-             done ;
-             assert false
-           with
-             | Stream.Failure when `Indent = !state || `Comma = !state ->
-                 metadata <- Some m ;
-                 "Done"
-             | Error | Stream.Failure | Stream.Error _ ->
-                 "Syntax error: use key1=\"val1\",key2=\"val2\",..")
-
-  method private get_frame buf =
-    let p = Frame.position buf in
-      source#get buf ;
-      match metadata with
-        | Some m ->
-            Frame.set_metadata buf p m ;
-            metadata <- None
-        | None -> ()
-
-end
-
-let register =
-  let kind = Lang.univ_t 1 in
-  Lang.add_operator "insert_metadata" [ "", Lang.source_t kind, None, None ]
-    ~category:Lang.SoundProcessing
-    ~descr:"Interactively insert metadata using the command \
-            <code>ID.insert key1=\"val1\",key2=\"val2\",...</code>."
-    ~kind:(Lang.Unconstrained kind)
-    (fun p kind ->
-       let source = Lang.to_source (Lang.assoc "" 1 p) in
-         new insert_metadata ~kind source)
-
-(** Insert metadata at the beginning if none is set.
-  * Currently used by the switch classes. *)
-class replay ~kind meta src =
-object (self)
-  inherit operator kind [src]
-
-  val mutable first = true
-
-  method stype = src#stype
-  method is_ready = src#is_ready
-  method abort_track = src#abort_track
-  method remaining = src#remaining
-
-  method private get_frame ab =
-    let start = Frame.position ab in
-      src#get ab ;
-      if first then begin
-        if Frame.get_metadata ab start = None then
-          Frame.set_metadata ab start meta ;
-        first <- false
-      end
-end
Index: encoder/ogg_encoder.ml
===================================================================
--- encoder/ogg_encoder.ml	(révision 7979)
+++ encoder/ogg_encoder.ml	(copie de travail)
@@ -27,7 +27,7 @@
     encode     : Ogg_muxer.t -> nativeint -> 
                  Frame.content -> int -> int -> unit ;
     reset      : Ogg_muxer.t -> 
-                 Encoder.Meta.export_metadata -> nativeint ;
+                 Metautils.export_metadata -> nativeint ;
     mutable id : nativeint option
   }
 
Index: encoder/encoder.ml
===================================================================
--- encoder/encoder.ml	(révision 7979)
+++ encoder/encoder.ml	(copie de travail)
@@ -20,46 +20,6 @@
 
  *****************************************************************************)
 
-let conf = 
-  Dtools.Conf.void ~p:(Configure.conf#plug "encoder") "Encoder settings"
-    ~comments:["Settings for the encoder"]
-
-
-let conf_meta =
-  Dtools.Conf.void ~p:(conf#plug "encoder") "Metadata settings"
-    ~comments:["Settings for the encoded metadata."]
-
-(** The list of metadata that labels
-  * are exported when encoding data. *)
-let conf_export_metadata =
-  Dtools.Conf.list ~p:(conf_meta#plug "export") "Exported metdata"
-    ~d:["artist";"title";"album";"genre";"date";"tracknumber";
-        "comment"]
-    ~comments:["The list of labels of exported metadata."]
-
-(* This is because I am too lazy to 
- * write encoder.mli.. *)
-module Meta : 
-  sig
-    (* I would like to use a private 
-     * type here but its only for ocaml >= 3.11.. *)
-    type export_metadata
-    val export_metadata : Frame.metadata -> export_metadata
-    val to_metadata : export_metadata -> Frame.metadata
-    val empty_metadata : export_metadata
-  end = 
-  struct
-    type export_metadata = Frame.metadata
-    let export_metadata m =
-      let ret = Hashtbl.create 10 in
-      let l = conf_export_metadata#get in
-      Hashtbl.iter (fun x y -> if List.mem (String.lowercase x) l then
-                               Hashtbl.add ret x y) m;
-    ret
-    let to_metadata m = m
-    let empty_metadata = Hashtbl.create 0
-  end
-
 let string_of_stereo s =
   if s then "stereo" else "mono"
 
@@ -414,12 +374,12 @@
   * its stream. This is ok, though, because the ogg container/streams 
   * is meant to be sequentialized but not the mp3 format. *)
 type encoder = {
-  insert_metadata : Meta.export_metadata -> unit ;
+  insert_metadata : Metautils.export_metadata -> unit ;
   encode : Frame.t -> int -> int -> string ;
   stop : unit -> string
 }
 
-type factory = string -> Meta.export_metadata -> encoder
+type factory = string -> Metautils.export_metadata -> encoder
 
 (** A plugin might or might not accept a given format.
   * If it accepts it, it gives a function creating suitable encoders. *)
Index: encoder/flac_encoder.ml
===================================================================
--- encoder/flac_encoder.ml	(révision 7979)
+++ encoder/flac_encoder.ml	(copie de travail)
@@ -24,7 +24,7 @@
 
 let encoder flac meta =
   let comments = 
-    Utils.list_of_metadata (Encoder.Meta.to_metadata meta) 
+    Utils.list_of_metadata (Metautils.to_metadata meta) 
   in
   let channels = flac.Encoder.Flac.channels in
   let samplerate_converter =
Index: source.mli
===================================================================
--- source.mli	(révision 7979)
+++ source.mli	(copie de travail)
@@ -70,6 +70,9 @@
   (** What kind of content does this source produce. *)
   method kind : Frame.content_kind
 
+  (** Insert a metadata block in the current stream. *)
+  method insert_metadata : Metautils.export_metadata -> unit
+
   (** Number of frames left in the current track. Defaults to -1=infinity. *)
   method virtual remaining : int
 
Index: lang/lang_builtins.ml
===================================================================
--- lang/lang_builtins.ml	(révision 7985)
+++ lang/lang_builtins.ml	(copie de travail)
@@ -256,6 +256,17 @@
          Lang.unit)
    end
 
+let () = 
+  add_builtin "metadata.export" ~cat:Liq 
+   ~descr:"Filter-out internal metadata."
+   ["",Lang.metadata_t,None,None] Lang.metadata_t
+   (fun p ->
+     Lang.metadata 
+      (Metautils.to_metadata
+        (Metautils.export_metadata ~filter:true
+          (Lang.to_metadata 
+            (List.assoc "" p)))))
+
 let () =
   let resolver_t =
     Lang.fun_t
@@ -1285,6 +1296,22 @@
         Lang.unit)
 
 let () =
+  add_builtin "source.insert_metadata" ~cat:Liq ~descr:"Insert metadata into a stream."
+    [ "filter", Lang.bool_t, Some (Lang.bool false), 
+      Some "Filter metadata before inserting.";
+      "",Lang.metadata_t,None,None;
+      "",Lang.source_t (Lang.univ_t 1),None,None] Lang.unit_t
+    (fun p -> 
+       let filter = Lang.to_bool (List.assoc "filter" p) in
+       let m = 
+          Metautils.export_metadata ~filter
+           (Lang.to_metadata 
+             (Lang.assoc "" 1 p))
+       in
+       (Lang.to_source (Lang.assoc "" 2 p))#insert_metadata m; 
+       Lang.unit)
+
+let () =
   add_builtin "request.create.raw" ~cat:Liq
     ~descr:"Create a raw request, i.e. for files that should not be decoded \
             for streaming. Creation may fail if there is no available RID, \
Index: source.ml
===================================================================
--- source.ml	(révision 7979)
+++ source.ml	(copie de travail)
@@ -425,6 +425,13 @@
   (* In caching mode, remember what has been given during the current tick *)
   val memo = Frame.create content_kind
 
+  (* Metadata used to dynamically insert metadata. *)
+  val mutable metadata = None
+
+  (* Insert metadata block in current stream. *)
+  method insert_metadata m = 
+    metadata <- Some (Metautils.to_metadata m)
+
   (* [#get buf] completes the frame with the next data in the stream.
    * Depending whether caching is enabled or not, it calls [#get_frame] directly
    * or tries to get data from the cache frame, filling it if needed.
@@ -435,7 +442,15 @@
     assert (Frame.is_partial buf) ;
     if not caching then begin
       let b = Frame.breaks buf in
+        let p = Frame.position buf in
         self#get_frame buf ;
+        begin
+          match metadata with
+            | Some m ->
+                Frame.set_metadata buf p m ;
+                metadata <- None
+            | None -> ()
+        end ;
         if List.length b + 1 <> List.length (Frame.breaks buf) then begin
           self#log#f 2 "#get_frame didn't add exactly one break!" ;
           assert false
------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500!
Tap into the largest installed PC base & get more eyes on your game by
optimizing for Intel(R) Graphics Technology. Get started today with the
Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
Savonet-devl mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/savonet-devl

Répondre à