about summary refs log tree commit homepage
path: root/lib/dtas/source
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dtas/source')
-rw-r--r--lib/dtas/source/av.rb7
-rw-r--r--lib/dtas/source/av_ff_common.rb43
-rw-r--r--lib/dtas/source/ff.rb8
-rw-r--r--lib/dtas/source/sox.rb5
4 files changed, 42 insertions, 21 deletions
diff --git a/lib/dtas/source/av.rb b/lib/dtas/source/av.rb
index 39cad6c..dcebcfd 100644
--- a/lib/dtas/source/av.rb
+++ b/lib/dtas/source/av.rb
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
+# Copyright (C) all contributors <dtas-all@nongnu.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
 require_relative '../../dtas'
@@ -13,13 +13,12 @@ class DTAS::Source::Av # :nodoc:
       'avconv -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
       'sox -p $SOXFMT - $TRIMFX $RGFX',
 
-    # this is above ffmpeg because this av is the Debian default and
-    # it's easier for me to test av than ff
-    "tryorder" => 1,
+    "tryorder" => 2,
   )
 
   def initialize
     command_init(AV_DEFAULTS)
+    @mcache = nil
     @av_ff_probe = "avprobe"
   end
 
diff --git a/lib/dtas/source/av_ff_common.rb b/lib/dtas/source/av_ff_common.rb
index 5299fdb..7f197e0 100644
--- a/lib/dtas/source/av_ff_common.rb
+++ b/lib/dtas/source/av_ff_common.rb
@@ -7,10 +7,10 @@ require_relative '../replaygain'
 require_relative '../xs'
 require_relative 'file'
 
-# Common code for libav (avconv/avprobe) and ffmpeg (and ffprobe)
-# TODO: newer versions of both *probes support JSON, which will be easier to
-# parse.  However, the packaged libav version in Debian 7.0 does not
-# support JSON, so we have an ugly parser...
+# Common code for ffmpeg/ffprobe and the abandoned libav (avconv/avprobe).
+# TODO: newer versions of both *probes support JSON, which will be easier
+# to parse.  libav is abandoned, nowadays, and Debian only packages
+# ffmpeg+ffprobe nowadays.
 module DTAS::Source::AvFfCommon # :nodoc:
   include DTAS::Source::File
   include DTAS::XS
@@ -21,10 +21,23 @@ module DTAS::Source::AvFfCommon # :nodoc:
   attr_reader :format
   attr_reader :duration
 
+  CACHE_KEYS = [ :@duration, :@probe_harder, :@comments, :@astreams,
+                 :@format ].freeze
+
+  def mcache_lookup(infile)
+    (@mcache ||= DTAS::Mcache.new).lookup(infile) do |input, dst|
+      tmp = source_file_dup(infile, nil, nil)
+      tmp.av_ff_ok? or return nil
+      CACHE_KEYS.each { |k| dst[k] = tmp.instance_variable_get(k) }
+      dst
+    end
+  end
+
   def try(infile, offset = nil, trim = nil)
-    rv = source_file_dup(infile, offset, trim)
-    rv.av_ff_ok? or return
-    rv
+    ent = mcache_lookup(infile) or return
+    ret = source_file_dup(infile, offset, trim)
+    CACHE_KEYS.each { |k| ret.instance_variable_set(k, ent[k]) }
+    ret
   end
 
   def __parse_astream(cmd, stream)
@@ -79,7 +92,7 @@ module DTAS::Source::AvFfCommon # :nodoc:
 
       err = "".b
       begin
-        s = qx(@env, cmd, err_str: err, no_raise: true)
+        s = qx(@env, cmd, err_str: err, no_raise: true, rlimit_cpu: [ 1, 2 ])
       rescue Errno::ENOENT # avprobe/ffprobe not installed
         return false
       end
@@ -104,13 +117,14 @@ module DTAS::Source::AvFfCommon # :nodoc:
       prev_cmd = cmd
     end while incomplete.compact[0]
 
+    enc = Encoding.default_external # typically Encoding::UTF_8
     # old avprobe
     s.scan(%r{^\[FORMAT\]\n(.*?)\n\[/FORMAT\]\n}m) do |_|
       f = $1.dup
       f =~ /^duration=([\d\.]+)\s*$/nm and @duration = $1.to_f
       # TODO: multi-line/multi-value/repeated tags
       f.gsub!(/^TAG:([^=]+)=(.*)$/ni) { |_|
-        @comments[$1.upcase] = -($2)
+        @comments[-DTAS.try_enc($1.upcase, enc)] = $2
       }
     end
 
@@ -118,13 +132,22 @@ module DTAS::Source::AvFfCommon # :nodoc:
     s.scan(%r{^\[format\.tags\]\n(.*?)\n\n}m) do |_|
       f = $1.dup
       f.gsub!(/^([^=]+)=(.*)$/ni) { |_|
-        @comments[$1.upcase] = -$2
+        @comments[-DTAS.try_enc($1.upcase, enc)] = $2
       }
     end
     s.scan(%r{^\[format\]\n(.*?)\n\n}m) do |_|
       f = $1.dup
       f =~ /^duration=([\d\.]+)\s*$/nm and @duration = $1.to_f
     end
+    comments.each do |k,v|
+      v.chomp!
+      comments[k] = -DTAS.try_enc(v, enc)
+    end
+
+    # ffprobe always uses "track", favor FLAC convention "TRACKNUMBER":
+    if @comments['TRACK'] && !@comments['TRACKNUMBER']
+      @comments['TRACKNUMBER'] = @comments.delete('TRACK')
+    end
 
     ! @astreams.compact.empty?
   end
diff --git a/lib/dtas/source/ff.rb b/lib/dtas/source/ff.rb
index 687cd18..c337b42 100644
--- a/lib/dtas/source/ff.rb
+++ b/lib/dtas/source/ff.rb
@@ -1,12 +1,10 @@
-# Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
+# Copyright (C) all contributors <dtas-all@nongnu.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
 require_relative '../../dtas'
 require_relative 'av_ff_common'
 
 # ffmpeg support
-# note: only tested with the compatibility wrapper in the Debian 7.0 package
-# (so still using avconv/avprobe)
 class DTAS::Source::Ff  # :nodoc:
   include DTAS::Source::AvFfCommon
 
@@ -15,12 +13,12 @@ class DTAS::Source::Ff  # :nodoc:
       'ffmpeg -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
       'sox -p $SOXFMT - $TRIMFX $RGFX',
 
-    # I haven't tested this much since av is in Debian stable and ff is not
-    "tryorder" => 2,
+    "tryorder" => 1,
   )
 
   def initialize
     command_init(FF_DEFAULTS)
+    @mcache = nil
     @av_ff_probe = "ffprobe"
   end
 
diff --git a/lib/dtas/source/sox.rb b/lib/dtas/source/sox.rb
index 6ca29bc..365c7b6 100644
--- a/lib/dtas/source/sox.rb
+++ b/lib/dtas/source/sox.rb
@@ -24,7 +24,7 @@ class DTAS::Source::Sox # :nodoc:
     return if @last_failed == infile
     @last_failed = infile
     case msg
-    when Process::Status then msg = "failed with #{msg.exitstatus}"
+    when Process::Status then msg = "failed with #{msg.inspect}"
     when 0 then msg = 'detected zero samples'
     end
     warn("soxi #{infile}: #{msg}\n")
@@ -39,7 +39,8 @@ class DTAS::Source::Sox # :nodoc:
   def mcache_lookup(infile)
     (@mcache ||= DTAS::Mcache.new).lookup(infile) do |input, dst|
       err = ''.b
-      out = qx(@env.dup, %W(soxi #{input}), err_str: err, no_raise: true)
+      out = qx(@env.dup, %W(soxi #{input}), err_str: err, no_raise: true,
+                rlimit_cpu: [ 1, 2 ])
       return soxi_failed(infile, out) if Process::Status === out
       return soxi_failed(infile, err) if err =~ /soxi FAIL formats:/
       out =~ /^Duration\s*:[^=]*= (\d+) samples /n