Le mardi 23 mars 2010 04:29:21, David Baelde a écrit :
> Hi,

        Hi !

> Thanks for the patch, I think it's a good way to go. 

Thanks for the review !

> I read it, a
> couple remarks:
> 
>  - The main remark is that I would have thought that post-processors
> can be kept local to the resolution loop, no need to store them in the
> request. This could be done perhaps more easily (and more elegantly
> than accumulating continuations in a list ref) by using a recursive
> function instead of a while.

I have done this.

>  - Forcing temporary to false in annotate is useless, and meaningless
> if the argument URI is not a file.

I do not understand very well this parameter, it seems it describe wether the 
request can be kept or should be revoled each time one need it. Can you 
describe a bit more ?

>  - In the Lang API I would prefer two functions, add_protocol as
> before and the new post-process: this would avoid the string
> (possibility of a typo hard to catch) and allow different types (e.g.
> tell the post-processor if it's working on a temporary file or not).

I have done: 
 - add_file_resolver
 - add_post_processor
I believe it is more relevant that way..

>  - Same thing in the ML API (i.e. in Request), where it would seem
> better to start with type protocol = (Resolver of ... | Post-processor
> of ...).

I am not sure to undertand this very well..

>  - I'm not sure about the addition of new status: post-proc could be
> part of Resolving. This seems harmless, but note that it indirectly
> changes the server API, since it introduces non-destroyed non-idle
> requests that won't be found in "request.resolving" for a while
> (post-proc could be long).

Done as well..

Please find an updated patch attached.

Romain
Index: scripts/utils.liq
===================================================================
--- scripts/utils.liq	(révision 7225)
+++ scripts/utils.liq	(copie de travail)
@@ -487,13 +487,48 @@
  extract_replaygain = "#{configure.libdir}/extract-replaygain"
  x = get_process_lines("#{extract_replaygain} #{quote(arg)}")
  if list.hd(x) != "" then
-  ["annotate:replay_gain=\"#{list.hd(x)}\":#{arg}"]
+  ([arg],[("replay_gain",list.hd(x))])
  else
-  [arg]
+  ([arg],[])
  end
 end
-add_protocol("replay_gain", replaygain_protocol)
+add_post_processor("replay_gain", temporary = false, replaygain_protocol)
 
+# Register the cut protocol
+def cut_protocol(arg,delay)
+ # The extraction program
+ cut_file = "#{configure.libdir}/cut-file"
+ # Parse args 
+ ret = string.extract(pattern="cut_start=(\d+)",arg)
+ start = 
+   if list.length(ret) == 0 then 
+     "0"
+   else
+     ret["1"]
+   end
+ ret = string.extract(pattern="cut_stop=(\d+)",arg)
+ stop =
+   if list.length(ret) == 0 then
+     "0"
+   else
+     ret["1"]
+   end
+ ret = string.extract(pattern=":(.*)$",arg)
+ uri = 
+   if list.length(ret) == 0 then
+     ""
+   else
+     ret["1"]
+   end
+ x = get_process_lines("#{cut_file} #{quote(uri)} #{start} #{stop}")
+ if list.hd(x) != "" then
+   ([list.hd(x)],[])
+ else
+   ([uri],[])
+ end
+end
+add_post_processor("cut_file", temporary=true, cut_protocol)
+
 # Enable replay gain metadata resolver. This resolver will
 # process any file decoded by liquidsoap and add a @replay_gain@
 # metadata when this value could be computed. For a finer-grained
Index: scripts/cut-file
===================================================================
--- scripts/cut-file	(révision 0)
+++ scripts/cut-file	(révision 0)
@@ -0,0 +1,87 @@
+#!/usr/bin/perl -w
+
+use strict ;
+
+my $file = $ARGV[0] || die ;
+
+my $start = $ARGV[1] || "0" ;
+
+my $stop = $ARGV[2] || "0" ;
+
+sub mktemp {
+  my $file = `mktemp --tmpdir --suffix=".osb" "liqXXXX"`;
+  $file =~ s/\n//;
+  $file;
+}
+
+my $out_file = mktemp(); 
+
+sub test_mime {
+  my $file = shift ;
+  if (`which file`) {
+    return `file -b --mime-type "$file"`;
+  }
+}
+
+if (($file =~ /\.mp3$/i) || (test_mime($file) =~ /audio\/mpeg/))  {
+
+  if (`which cutmp3`) {
+
+    my $start_option = "00:00";
+    my $stop_option = "99999:0";
+
+    if ($start ne "0") {
+       $start_option = "00:$start"
+    } 
+  
+    if ($stop ne "0") {
+       $stop_option = "00:$stop"
+    }
+ 
+    system("nice -n 20 cutmp3 -c -q -a $start_option -b $stop_option -O \"$out_file\" -i \"$file\" >/dev/null 2>\&1");
+
+  } else {
+
+    print STDERR "Cannot find cutmp3 binary!\n";
+    system("cp \"$file\" \"$out_file\"");
+
+  }
+
+} elsif (($file =~ /\.ogg$/i) || (test_mime($file) =~ /application\/ogg/)) {
+
+  if (`which vcut`) {
+
+    my $start_file = $file;
+
+    # Cut beginning
+    if ($start ne "0") {
+       $start_file = mktemp();
+       system("nice -n 20 vcut \"$file\" /dev/null \"$start_file\" +$start  >/dev/null 2>\&1"); 
+    }
+
+    # Cut end
+    if ($stop ne "0") {
+       system("nice -n 20 vcut \"$start_file\" \"$out_file\" /dev/null +$stop >/dev/null 2>\&1"); 
+       system("rm -f \"$start_file\"");
+    } else {
+      if ("$start_file" ne "$file") {
+        system("mv $start_file $out_file");
+      }
+    }
+
+  } else {
+
+    print STDERR "Cannot find vcut binary!\n";
+    system("cp \"$file\" \"$out_file\"");
+
+  }
+
+} else {
+
+  print STDERR "File format not supported...\n";
+  system("cp \"$file\" \"$out_file\"");
+
+}
+
+print "$out_file\n";
+

Modification de propriétés sur scripts/cut-file
___________________________________________________________________
Ajouté : svn:executable
   + *

Index: scripts/Makefile
===================================================================
--- scripts/Makefile	(révision 7221)
+++ scripts/Makefile	(copie de travail)
@@ -1,6 +1,6 @@
 
 DISTFILES = $(wildcard *.in) Makefile ask-liquidsoap.rb ask-liquidsoap.pl \
-	    $(wildcard *.liq) extract-replaygain
+	    $(wildcard *.liq) extract-replaygain cut-file
 
 top_srcdir = ..
 include $(top_srcdir)/Makefile.rules
Index: src/protocols/annotate.ml
===================================================================
--- src/protocols/annotate.ml	(révision 7226)
+++ src/protocols/annotate.ml	(copie de travail)
@@ -55,7 +55,8 @@
         | _ -> raise Error
     in
     let metadata,uri = parse [] in
-      [Request.indicator ~metadata:(Utils.hashtbl_of_list metadata) uri]
+      [Request.indicator ~temporary:false 
+                         ~metadata:(Utils.hashtbl_of_list metadata) uri]
   with
     | Error
     | Stream.Failure | Stream.Error _ -> log "annotate: syntax error" ; []
@@ -64,4 +65,6 @@
   Request.protocols#register "annotate"
     ~sdoc:("[annotate:key=\"val\",key2=\"val2\",...:uri] adds "^
            "the metadata to the request and is then resolved into uri")
-    { Request.resolve = annotate ; Request.static = false }
+    { Request.resolve = annotate ; 
+      Request.static = false ;
+      Request.protocol_type = Request.Post_processor }
Index: src/protocols/say.ml
===================================================================
--- src/protocols/say.ml	(révision 7226)
+++ src/protocols/say.ml	(copie de travail)
@@ -83,7 +83,9 @@
   Request.protocols#register
     ~sdoc:"Speech synthesis, with optional voice choice using say:voice/blah."
     "say"
-    { Request.resolve = say ; Request.static = true }
+    { Request.resolve = say ; 
+      Request.static = true ;
+      Request.protocol_type = Request.Resolver }
 
 let time arg ~log timeout =
   let tm = Unix.localtime (Unix.gettimeofday ()) in
@@ -101,4 +103,6 @@
     "time"
     ~sdoc:("Speech synthesis of a message where $(time) is replaced by "^
            "the current time")
-    { Request.resolve = time ; Request.static = false }
+    { Request.resolve = time ; 
+      Request.static = false ;
+      Request.protocol_type = Request.Resolver }
Index: src/protocols/lastfm_req.ml
===================================================================
--- src/protocols/lastfm_req.ml	(révision 7226)
+++ src/protocols/lastfm_req.ml	(copie de travail)
@@ -38,4 +38,6 @@
 let () =
   Request.protocols#register "lastfm"
     ~sdoc:("Play a song on last.fm.")
-    { Request.resolve = lastfm ; Request.static = false }
+    { Request.resolve = lastfm ; 
+      Request.static = false ; 
+      Request.protocol_type = Request.Resolver }
Index: src/protocols/mpd.ml
===================================================================
--- src/protocols/mpd.ml	(révision 7226)
+++ src/protocols/mpd.ml	(copie de travail)
@@ -160,4 +160,6 @@
   Request.protocols#register "mpd"
     ~sdoc:("[mpd:tag=\"value\"] finds all files with a tag equal " ^
            "to a given value using mpd.")
-    { Request.resolve = mpd ; Request.static = false }
+    { Request.resolve = mpd ; 
+      Request.static = false ;
+      Request.protocol_type = Request.Resolver }
Index: src/protocols/extproto.ml
===================================================================
--- src/protocols/extproto.ml	(révision 7226)
+++ src/protocols/extproto.ml	(copie de travail)
@@ -89,7 +89,8 @@
                   ~sdoc:(Printf.sprintf "Fetch files using %S." prog)
                   proto
                   { Request.resolve = resolve proto prog command ;
-                    Request.static = false })
+                    Request.static = false ;
+                    Request.protocol_type = Request.Resolver })
              protos
        with
          | Not_found ->
Index: src/request.mli
===================================================================
--- src/request.mli	(révision 7226)
+++ src/request.mli	(copie de travail)
@@ -92,9 +92,20 @@
   * At each step [protocol.resolve first_uri timeout] is called,
   * and the function is expected to push the new URIs in the request. *)
 
+(** A protocol is one of:
+  * - Resolver: returns a fresh local file, e.g. file:, ftp:, http: ...
+  * - Post_processor: operates on a fresh file, possibly creating a
+  *   new file. URIs for post_processors are of the form:
+  *   protocol:(args:)next_uri where next_uri defines the file
+  *   on which the post_processor will operate. A post-processing
+  *   operator should return a temporary indicator iff it creates
+  *   a new file. In this case, it *must* always create a new file. *)
+type protocol_type = Resolver | Post_processor
+
 type protocol = {
-  resolve : string -> log:(string->unit) -> float -> indicator list ;
-  static : bool
+  resolve       : string -> log:(string->unit) -> float -> indicator list ;
+  static        : bool ;
+  protocol_type : protocol_type
 }
 
 (** A static request [r] is such that every resolving leads to the same file.
Index: src/lang/lang_builtins.ml
===================================================================
--- src/lang/lang_builtins.ml	(révision 7226)
+++ src/lang/lang_builtins.ml	(copie de travail)
@@ -216,13 +216,13 @@
          Request.mresolvers#register format resolver ;
          Lang.unit)
 
-let () =
+let protocol_op name descr protocol_type =
   let protocol_t =
     Lang.fun_t
       [false,"",Lang.string_t ; false,"",Lang.float_t]
-      (Lang.list_t Lang.string_t)
+      (Lang.product_t (Lang.list_t Lang.string_t) Lang.metadata_t)
   in
-    add_builtin "add_protocol" ~cat:Liq ~descr:"Register a new protocol."
+    add_builtin name ~cat:Liq ~descr
       ["temporary",Lang.bool_t,Some (Lang.bool false),
        Some "if true, file removed when it is finished.";
        "",Lang.string_t,None,None ;
@@ -234,6 +234,7 @@
          let temporary = Lang.to_bool (List.assoc "temporary" p) in
            Request.protocols#register name
              { Request.static = false ;
+               Request.protocol_type = protocol_type ;
                Request.resolve =
                  fun arg ~log timeout ->
                    let l =
@@ -241,12 +242,24 @@
                        f ["",Lang.string arg;
                           "",Lang.float timeout]
                    in
+                   let indicators,metadata = 
+                     Lang.to_product l 
+                   in
+                   let metadata = Lang.to_metadata metadata in
                      List.map
                        (fun s ->
-                          Request.indicator ~temporary (Lang.to_string s))
-                       (Lang.to_list l) } ;
+                          Request.indicator ~temporary ~metadata (Lang.to_string s))
+                       (Lang.to_list indicators) } ;
            Lang.unit)
 
+let () = 
+  protocol_op "add_file_resolver" 
+              "Register a new protocol to resolve new files."
+              Request.Resolver ;
+  protocol_op "add_post_processor" 
+              "Register a new protocol to process resolved files."
+              Request.Post_processor
+
 let () =
   let t = "",Lang.int_t,None,None in
     add_builtin "time_in_mod" ~cat:Other ~flags:[Lang.Hidden]
Index: src/request.ml
===================================================================
--- src/request.ml	(révision 7226)
+++ src/request.ml	(copie de travail)
@@ -184,6 +184,12 @@
   metadata : metadata
 }
 
+type post_processor = {
+  protocol   : string ;
+  parameters : string ;
+  handler    : string -> log:(string->unit) -> float -> indicator list 
+}
+
 type status = Idle | Resolving | Ready | Playing | Destroyed
 
 type t = {
@@ -336,13 +342,7 @@
     let hd = List.hd l in
       add_log t (Printf.sprintf "Pushed [%S;...]." hd.string) ;
       t.indicators <- l::t.indicators ;
-      t.decoder <- None ;
-      (* Performing a local check is quite fast and allows the request
-       * to be instantly available if it is only made of valid local files,
-       * without any need for a resolution process. *)
-      (* TODO sometimes it's not that fast actually, and it'd be nice
-       * to be able to disable this check in some cases, like playlist.safe. *)
-      local_check t
+      t.decoder <- None
 
 let is_ready t =
   t.indicators <> [] &&
@@ -512,9 +512,12 @@
 
 (** Plugins registration. *)
 
+type protocol_type = Resolver | Post_processor 
+
 type protocol = {
-  resolve : string -> log:(string->unit) -> float -> indicator list ;
-  static : bool ;
+  resolve        : string -> log:(string->unit) -> float -> indicator list ;
+  static         : bool ;
+  protocol_type : protocol_type
 }
 
 let protocols_doc =
@@ -543,41 +546,121 @@
   t.resolving <- Some (Unix.time ()) ;
   t.status <- Resolving ;
   let maxtime = (Unix.time ()) +. timeout in
-  let resolve_step () =
-    let i = peek_indicator t in
-    (* If the file is local we only need to check that it's valid,
-     * we'll actually do that in a single local_check for all local indicators
-     * on the top of the stack. *)
-    if Sys.file_exists i.string then local_check t else
-    let proto,arg = parse_uri i.string in
-      match protocols#get proto with
-        | Some handler ->
-            add_log t
-              (Printf.sprintf
-                 "Resolving %S (timeout %.fs)..."
-                 i.string timeout) ;
-            let production = handler.resolve ~log:(add_log t) arg maxtime in
+  let rec resolve_step post_processors =
+    let timeleft = maxtime -. (Unix.time ()) in
+    if timeleft > 0. then
+     begin
+      let i = peek_indicator t in
+      (* If the file is local we only need to check that it's valid,
+       * we'll actually do that in a single local_check for all local indicators
+       * on the top of the stack. *)
+      if Sys.file_exists i.string then post_processors else
+      let proto,arg = parse_uri i.string in
+        match protocols#get proto with
+          | Some handler ->
+             begin
+              match handler.protocol_type with
+                | Resolver ->
+                   add_log t
+                     (Printf.sprintf
+                        "Resolving %S (timeout %.fs)..."
+                        i.string timeout) ;
+                   let production = handler.resolve ~log:(add_log t) arg 
+                                    maxtime 
+                   in
+                   if production = [] then begin
+                    log#f 4
+                      "Failed to resolve %S! \
+                       For more info, see server command 'trace %d'."
+                      i.string t.id ;
+                    ignore (pop_indicator t)
+                  end else
+                    push_indicators t production ;
+                    resolve_step post_processors
+                | Post_processor -> 
+                  (* Retrieve parameters *)
+                  let params,next = 
+                    let x = parse_uri arg in
+                    if x = ("","") then
+                      "",arg
+                    else
+                      x
+                  in
+                  let parameters = 
+                    if params = "" then "no parameters" 
+                    else Printf.sprintf "parameters %s" params
+                  in
+                  add_log t
+                    (Printf.sprintf
+                       "Adding post-process %s with %s..." 
+                     proto parameters) ;
+                  (* Push the next part *)
+                  push_indicators t [ { string    = next ;
+                                        temporary = false ;
+                                        metadata  = Hashtbl.create 0 }] ;
+                  resolve_step ({ protocol   = proto ;
+                                  parameters = params ;
+                                  handler    = handler.resolve } 
+                                 :: post_processors) 
+             end
+          | None ->
+              add_log t "Unknown protocol!" ;
+              pop_indicator t ;
+              post_processors
+     end
+    else
+      ( add_log t "Global timeout." ; raise ExnTimeout ) 
+  in
+  let rec post_process post_processors = 
+    let timeleft = maxtime -. (Unix.time ()) in
+    if timeleft > 0. then
+     begin
+      let i = peek_indicator t in
+      match post_processors with
+        | [] -> local_check t
+        | processor::rem -> 
+          let uri = 
+            if processor.parameters <> "" then
+              (Printf.sprintf "%s:%s") processor.parameters i.string 
+            else
+              i.string
+          in
+          add_log t
+             (Printf.sprintf
+                "Post-processing %s with post_processor %s..."
+              uri processor.protocol) ;
+          let production = processor.handler ~log:(add_log t) uri maxtime in
+            begin
               if production = [] then begin
                 log#f 4
-                  "Failed to resolve %S! \
+                  "Failed to post-process %s:%s! \
                    For more info, see server command 'trace %d'."
-                  i.string t.id ;
-                ignore (pop_indicator t)
+                  processor.protocol uri t.id ;
               end else
-                push_indicators t production
-        | None ->
-            add_log t "Unknown protocol!" ;
-            pop_indicator t
+                begin
+                 (* Merge old metadata with new metadata *)
+                 let merge x y = 
+                   Hashtbl.iter (Hashtbl.add x) y ;
+                   x 
+                 in
+                 let production = 
+                   List.map 
+                     (fun x -> { x with metadata = merge x.metadata i.metadata })
+                   production
+                 in
+                 push_indicators t production
+                end
+            end ;
+          post_process rem
+     end
+    else
+      ( add_log t "Global timeout." ; raise ExnTimeout )
   in
   let result =
     try
-      while not (is_ready t) do
-        let timeleft = maxtime -. (Unix.time ()) in
-          if timeleft > 0. then
-            resolve_step ()
-          else
-            ( add_log t "Global timeout." ; raise ExnTimeout )
-      done ;
+      let post_processors = resolve_step [] in
+      (* Apply post_processors *)
+      post_process post_processors ;
       Resolved
     with
       | ExnTimeout -> Timeout
Index: Makefile
===================================================================
--- Makefile	(révision 7221)
+++ Makefile	(copie de travail)
@@ -76,6 +76,7 @@
 	$(INSTALL_DIRECTORY) $(libdir)/liquidsoap/$(libs_dir_version)
 	$(INSTALL_PROGRAM) scripts/liquidtts $(libdir)/liquidsoap/$(libs_dir_version)
 	$(INSTALL_PROGRAM) scripts/extract-replaygain $(libdir)/liquidsoap/$(libs_dir_version)
+	$(INSTALL_PROGRAM) scripts/cut-file $(libdir)/liquidsoap/$(libs_dir_version)
 	$(INSTALL_DATA) scripts/utils.liq $(libdir)/liquidsoap/$(libs_dir_version)
 	$(INSTALL_DIRECTORY) ${sysconfdir}/liquidsoap
 	$(INSTALL_DATA) examples/radio.liq \
------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Savonet-devl mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/savonet-devl

Répondre à