dtas 0.15.0 → 0.16.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b38ad68d0ac0b303e95ef7a6de996581fd4a5c73
4
- data.tar.gz: 84066e2d52ede911d045b0d9f62a6d0cb741a0a3
2
+ SHA256:
3
+ metadata.gz: d887afd41dc71abd3448e72c3d33d188e4629b8dbc576af3237c3bd47b246581
4
+ data.tar.gz: cf5262f4c45becd00e75dc2f1c3de5ab05b27bd9a029c9fef458faec5cc58303
5
5
  SHA512:
6
- metadata.gz: e02cd6429e8df6fca2d6d0745e5fa1094910c6688ecbbc404d309f044a6abba32dfc0f0b03f60f5d8a487f94fc64fe6cedc0365c5a5cf539fb6d1db80b05bcad
7
- data.tar.gz: 8849acee6e2ec2ec588f686d7d56e741db25e480a8d9661d0feee4f95fe6f8dbd030ae07806654b7aa6020abd46243bfab8a7f33e25e977467aafe1b7a824352
6
+ metadata.gz: 94f470ad51f9723bf5d3161b445d0434e6e5e45349fc6c5227ecb80aa0c9d1409357979cd8f97edc909bed6c65b75494757b731d2f7cd97f4ed345640465aa7a
7
+ data.tar.gz: 631dc0a9b16771662bb6f8215ab9ee71acd078ac7cae26f1b90408106da840909d59f99cdf569425b2be05f2c9018f032ad426405ca1bbe1df28af005d2f020c
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  CONSTANT = "DTAS::VERSION"
6
6
  RVF = "lib/dtas/version.rb"
7
7
  GVF = "GIT-VERSION-FILE"
8
- DEF_VER = "v0.15.0"
8
+ DEF_VER = "v0.16.0"
9
9
  vn = DEF_VER
10
10
 
11
11
  # First see if there is a version file (included in release tarballs),
data/INSTALL CHANGED
@@ -19,13 +19,13 @@ Debian 7+ users can install dependencies easily:
19
19
  # installing dtas RubyGem on GNU/Linux (Linux kernel 2.6.32+)
20
20
 
21
21
  Be sure to have Ruby development headers and a working C compiler.
22
- This will pull in the io_splice and sleepy_penguin RubyGems for minor
22
+ This will pull in the sleepy_penguin RubyGems for minor
23
23
  speedups. If you cannot be bothered to have a development
24
24
  environment, just use "gem install dtas".
25
25
 
26
26
  sudo gem install dtas-linux
27
27
 
28
- This should pull in the "io_splice" and "sleepy_penguin" RubyGems
28
+ This should pull in the "sleepy_penguin" RubyGems
29
29
 
30
30
  For future upgrades of dtas (upgrades to dtas-linux will be infrequent)
31
31
 
@@ -39,16 +39,14 @@ For future upgrades of dtas (upgrades to dtas-linux will be infrequent)
39
39
 
40
40
  Grab the latest tarball from our HTTPS site:
41
41
 
42
- https://80x24.org/dtas/2017/dtas-0.15.0.tar.gz
42
+ https://80x24.org/dtas/2019/dtas-0.16.0.tar.gz
43
43
 
44
- $ tar zxvf dtas-0.15.0.tar.gz
45
- $ cd dtas-0.15.0
44
+ $ tar zxvf dtas-0.16.0.tar.gz
45
+ $ cd dtas-0.16.0
46
46
  $ sudo ruby setup.rb
47
47
 
48
- GNU/Linux users may optionally install "io_splice" and
49
- "sleepy_penguin" packages:
48
+ GNU/Linux users may optionally install the "sleepy_penguin" package:
50
49
 
51
- * io_splice - https://bogomips.org/ruby_io_splice/
52
50
  * sleepy_penguin - https://bogomips.org/sleepy_penguin/
53
51
 
54
52
  # CONTACT
@@ -61,5 +59,5 @@ No subscription is necessary to post to the mailing list.
61
59
 
62
60
  # COPYRIGHT
63
61
 
64
- Copyright 2013-2016 all contributors <dtas-all@nongnu.org>
62
+ Copyright 2013-2019 all contributors <dtas-all@nongnu.org>
65
63
  License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
data/README CHANGED
@@ -59,7 +59,7 @@ Coming:
59
59
 
60
60
  ## Source code
61
61
 
62
- git clone git://80x24.org/dtas
62
+ git clone https://80x24.org/dtas.git
63
63
 
64
64
  Please use git-format-patch(1) and git-send-email(1) distributed with
65
65
  the git(7) suite for generating and sending patches. Please format
@@ -86,7 +86,7 @@ Atom: <https://80x24.org/dtas-all/new.atom>
86
86
 
87
87
  ## Copyright
88
88
 
89
- Copyright 2013-2016 all contributors <dtas-all@nongnu.org>\
89
+ Copyright 2013-2019 all contributors <dtas-all@nongnu.org>\
90
90
  License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
91
91
 
92
92
  dtas is copyrighted Free Software by all contributors, see logs
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>.
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>.
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'tempfile'
@@ -93,7 +93,7 @@ def tags
93
93
  end
94
94
 
95
95
  task rsync_docs: %w(NEWS NEWS.atom) do
96
- dest = ENV["RSYNC_DEST"] || "80x24.org:/srv/dtas/"
96
+ dest = ENV["RSYNC_DEST"] || "80x24.org:/srv/80x24/dtas/"
97
97
  top = %w(INSTALL NEWS README COPYING NEWS.atom)
98
98
  files = []
99
99
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  #
@@ -173,11 +173,11 @@ def may_fail(res, events)
173
173
  Curses.setpos(lineno += 1, 0)
174
174
  Curses.clrtoeol
175
175
  Curses.addstr(infile)
176
- total = " [#{Time.at(samples / rate).strftime(tfmt)}]" if samples != 0
176
+ total = " [#{Time.at(samples / rate).utc.strftime(tfmt)}]" if samples != 0
177
177
  Curses.setpos(lineno += 1, 0)
178
178
  Curses.clrtoeol
179
179
  if rate != 0
180
- Curses.addstr("#{Time.at(elapsed).strftime(tfmt)}#{total} #{fmt}")
180
+ Curses.addstr("#{Time.at(elapsed).utc.strftime(tfmt)}#{total} #{fmt}")
181
181
  else
182
182
  Curses.addstr("#{elapsed} samples #{total} #{fmt}")
183
183
  end
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2015-2019 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  #
@@ -75,7 +75,7 @@ def seek_to_cur_pos(cur_pid, fp)
75
75
  end
76
76
 
77
77
  def children_of(ppid)
78
- `ps h -o pid --ppid=#{ppid}`.split(/\s+/s).map(&:to_i)
78
+ `ps h -o pid --ppid=#{ppid}`.split(/\s+/).map(&:to_i)
79
79
  end
80
80
 
81
81
  def expand_pid(pid)
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  #
4
4
  # this just declares dependencies to make gem installation a little easier
@@ -13,8 +13,7 @@
13
13
  s.email = %q{e@80x24.org}
14
14
  s.files = []
15
15
  s.homepage = 'https://80x24.org/dtas/'
16
- s.add_dependency(%q<dtas>)
17
- s.add_dependency(%q<io_splice>, '~> 4')
18
- s.add_dependency(%q<sleepy_penguin>, '~> 3')
16
+ s.add_dependency(%q<dtas>, '~> 0.16')
17
+ s.add_dependency(%q<sleepy_penguin>, '~> 3.5')
19
18
  s.licenses = 'GPL-3.0+'
20
19
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
 
@@ -42,3 +42,5 @@ def self.dedupe_str(str)
42
42
 
43
43
  require_relative 'dtas/compat_onenine'
44
44
  require_relative 'dtas/spawn_fix'
45
+ require_relative 'dtas/encoding'
46
+ DTAS.extend(DTAS::Encoding)
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'io/wait'
@@ -8,7 +8,9 @@
8
8
  class DTAS::Buffer # :nodoc:
9
9
  begin
10
10
  raise LoadError, "no splice with _DTAS_POSIX" if ENV["_DTAS_POSIX"]
11
- require 'io/splice' # splice is only in Linux for now...
11
+ require 'sleepy_penguin' # splice is only in Linux for now...
12
+ SleepyPenguin.respond_to?(:splice) or
13
+ raise LoadError, 'sleepy_penguin 3.5+ required for splice', []
12
14
  require_relative 'buffer/splice'
13
15
  include DTAS::Buffer::Splice
14
16
  rescue LoadError
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'io/nonblock'
@@ -7,7 +7,7 @@
7
7
  require_relative '../nonblock'
8
8
 
9
9
  # compatibility code for systems lacking "splice" support via the
10
- # "io-splice" RubyGem. Used only by -player
10
+ # "sleepy_penguin" 3.5+ RubyGem. Used only by -player
11
11
  module DTAS::Buffer::ReadWrite # :nodoc:
12
12
  MAX_AT_ONCE = 512 # min PIPE_BUF value in POSIX
13
13
  attr_accessor :buffer_size
@@ -1,16 +1,18 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'io/nonblock'
5
- require 'io/splice'
5
+ require 'sleepy_penguin'
6
6
  require_relative '../../dtas'
7
7
  require_relative '../pipe'
8
8
 
9
- # Used by -player on Linux systems with the "io-splice" RubyGem installed
9
+ # Used by -player on Linux systems with the "sleepy_penguin" RubyGem installed
10
10
  module DTAS::Buffer::Splice # :nodoc:
11
11
  MAX_AT_ONCE = 4096 # page size in Linux
12
12
  MAX_AT_ONCE_1 = 65536
13
- F_MOVE = IO::Splice::F_MOVE
13
+ F_MOVE = SleepyPenguin::F_MOVE
14
+ F_NONBLOCK = SleepyPenguin::F_NONBLOCK
15
+ TRY = { exception: false }.freeze
14
16
 
15
17
  def buffer_size
16
18
  @to_io.pipe_size
@@ -24,13 +26,13 @@ def buffer_size=(bytes)
24
26
 
25
27
  # be sure to only call this with nil when all writers to @wr are done
26
28
  def discard(bytes)
27
- IO.splice(@to_io, nil, DTAS.null, nil, bytes)
29
+ SleepyPenguin.splice(@to_io, DTAS.null, bytes)
28
30
  end
29
31
 
30
32
  def broadcast_one(targets, limit = nil)
31
33
  # single output is always non-blocking
32
34
  limit ||= MAX_AT_ONCE_1
33
- s = IO.trysplice(@to_io, nil, targets[0], nil, limit, F_MOVE)
35
+ s = SleepyPenguin.splice(@to_io, targets[0], limit, F_MOVE, TRY)
34
36
  if Symbol === s
35
37
  targets # our one and only target blocked on write
36
38
  else
@@ -46,7 +48,7 @@ def broadcast_one(targets, limit = nil)
46
48
  def __tee_in_full(src, dst, bytes)
47
49
  rv = 0
48
50
  while bytes > 0
49
- s = IO.tee(src, dst, bytes)
51
+ s = SleepyPenguin.tee(src, dst, bytes)
50
52
  bytes -= s
51
53
  rv += s
52
54
  end
@@ -56,7 +58,7 @@ def __tee_in_full(src, dst, bytes)
56
58
  def __splice_in_full(src, dst, bytes, flags)
57
59
  rv = 0
58
60
  while bytes > 0
59
- s = IO.splice(src, nil, dst, nil, bytes, flags)
61
+ s = SleepyPenguin.splice(src, dst, bytes, flags)
60
62
  rv += s
61
63
  bytes -= s
62
64
  end
@@ -69,7 +71,7 @@ def __broadcast_tee(blocked, targets, chunk_size)
69
71
  targets.delete_if do |dst|
70
72
  begin
71
73
  t = (dst.nonblock? || most_teed == 0) ?
72
- IO.trytee(@to_io, dst, chunk_size) :
74
+ SleepyPenguin.tee(@to_io, dst, chunk_size, F_NONBLOCK, TRY) :
73
75
  __tee_in_full(@to_io, dst, chunk_size)
74
76
  if Integer === t
75
77
  if t > most_teed
@@ -117,7 +119,7 @@ def broadcast_inf(targets, limit = nil)
117
119
  begin
118
120
  targets << last
119
121
  if last.nonblock? || most_teed == 0
120
- s = IO.trysplice(@to_io, nil, last, nil, bytes, F_MOVE)
122
+ s = SleepyPenguin.splice(@to_io, last, bytes, F_MOVE|F_NONBLOCK, TRY)
121
123
  if Symbol === s
122
124
  blocked << last
123
125
 
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2018-2019 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+
5
+ # This module gets included in DTAS
6
+ module DTAS::Encoding # :nodoc:
7
+ def self.extended(mod)
8
+ mod.instance_eval { @charlock_holmes = nil}
9
+ end
10
+
11
+ private
12
+
13
+ def try_enc_harder(str, enc, old) # :nodoc:
14
+ case @charlock_holmes
15
+ when nil
16
+ begin
17
+ require 'charlock_holmes'
18
+ @charlock_holmes = CharlockHolmes::EncodingDetector.new
19
+ rescue LoadError
20
+ warn "`charlock_holmes` gem not available for encoding detection"
21
+ @charlock_holmes = false
22
+ end
23
+ when false
24
+ enc_fallback(str, enc, old)
25
+ else
26
+ res = @charlock_holmes.detect(str)
27
+ if det = res[:ruby_encoding]
28
+ str.force_encoding(det)
29
+ warn "charlock_holmes detected #{str.inspect} as #{det}..."
30
+ str.valid_encoding? or enc_fallback(str, det, old)
31
+ else
32
+ enc_fallback(str, enc, old)
33
+ end
34
+ end
35
+ str
36
+ end
37
+
38
+ def enc_fallback(str, enc, old) # :nodoc:
39
+ str.force_encoding(old)
40
+ warn "could not detect encoding for #{str.inspect} (not #{enc})"
41
+ end
42
+
43
+ public
44
+
45
+ def try_enc(str, enc, harder = true) # :nodoc:
46
+ old = str.encoding
47
+ return str if old == enc
48
+ str.force_encoding(enc)
49
+ unless str.valid_encoding?
50
+ if harder
51
+ try_enc_harder(str, enc, old)
52
+ else
53
+ enc_fallback(str, enc, old)
54
+ end
55
+ end
56
+ str
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2015-2019 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  #
@@ -10,6 +10,7 @@
10
10
  require_relative 'source/ff'
11
11
  require_relative 'source/splitfx'
12
12
  require 'socket'
13
+ require 'tempfile'
13
14
 
14
15
  # For the DTAS Music Library, based on what MPD uses.
15
16
  class DTAS::Mlib # :nodoc:
@@ -37,16 +38,13 @@ class DTAS::Mlib # :nodoc:
37
38
 
38
39
  def initialize(db)
39
40
  if String === db
41
+ require 'sequel'
42
+ opts = { single_threaded: true }
40
43
  db = "sqlite://#{db}" unless db.include?('://')
41
- require 'sequel/no_core_ext'
42
- db = Sequel.connect(db, single_threaded: true)
43
- end
44
- if db.class.to_s.downcase.include?('sqlite')
45
- db.transaction_mode = :immediate
46
- db.synchronous = :off
47
- db.case_sensitive_like = false
48
- else
49
- warn 'non-SQLite databases may not work in the future'
44
+ opts[:transaction_mode] = :immediate
45
+ opts[:synchronous] = :off
46
+ opts[:case_sensitive_like] = false # only for 'search'
47
+ db = Sequel.connect(db, opts)
50
48
  end
51
49
  @db = db
52
50
  @pwd = nil
@@ -65,15 +63,23 @@ def initialize(db)
65
63
  ]
66
64
  end
67
65
 
66
+ def synchronize
67
+ @lock.flock(File::LOCK_EX)
68
+ @db.transaction { yield }
69
+ ensure
70
+ @lock.flock(File::LOCK_UN)
71
+ end
72
+
68
73
  def init_suffixes
69
- `sox --help 2>/dev/null` =~ /\nAUDIO FILE FORMATS:\s*([^\n]+)/s
74
+ `sox --help 2>/dev/null` =~ /\nAUDIO FILE FORMATS:\s*([^\n]+)/
70
75
  re = $1.split(/\s+/).map! { |x| Regexp.quote(x) }.join('|')
71
76
  @suffixes = Regexp.new("\\.(?:#{re}|yml)\\z", Regexp::IGNORECASE)
72
77
  end
73
78
 
74
- def worker(todo)
79
+ def worker(todo, lock)
80
+ @lock = lock
75
81
  @work.close
76
- @db.tables # reconnect before chdir
82
+ synchronize { @db.tables } # reconnect before chdir
77
83
  @pwd = Dir.pwd.b
78
84
  begin
79
85
  buf = todo.recv(16384) # 4x bigger than PATH_MAX ought to be enough
@@ -87,7 +93,7 @@ def worker(todo)
87
93
  end
88
94
 
89
95
  def ignore(job)
90
- @db.transaction do
96
+ synchronize do
91
97
  node_ensure(job.parent_id, job.path, DM_IGN, job.ctime)
92
98
  end
93
99
  end
@@ -106,24 +112,16 @@ def worker_work(job)
106
112
  end
107
113
  return ignore(job) unless found
108
114
  tlen = found.duration
109
- return ignore(job) if tlen < 0
115
+ return ignore(job) if tlen.nil? || tlen < 0
110
116
  tlen = tlen.round
111
117
  tmp = {}
112
- found.comments.each do |tag, value|
113
- tag_id = @tag_map[tag] or next
114
- value.strip!
115
-
116
- # FIXME: this fallback needs testing
117
- [ Encoding::UTF_8, Encoding::ISO_8859_1 ].each do |enc|
118
- value.force_encoding(enc)
119
- if value.valid_encoding?
120
- value.encode!(Encoding::UTF_8) if enc != Encoding::UTF_8
121
- tmp[tag_id] = value
122
- break
123
- end
118
+ if comments = found.comments
119
+ comments.each do |tag, value|
120
+ tag_id = @tag_map[tag] or next
121
+ tmp[tag_id] = value if value.valid_encoding?
124
122
  end
125
123
  end
126
- @db.transaction do
124
+ synchronize do
127
125
  node_id = node_ensure(job.parent_id, path, tlen, job.ctime)[:id]
128
126
  vals = @db[:vals]
129
127
  comments = @db[:comments]
@@ -154,11 +152,20 @@ def update(path, opts = nil)
154
152
  @work and raise 'update already running'
155
153
  todo, @work = UNIXSocket.pair(:SOCK_SEQPACKET)
156
154
  @db.disconnect
157
- jobs.times { |i| fork { worker(todo) } }
155
+
156
+ # like a Mutex between processes
157
+ @lock = Tempfile.new('dtas.mlib.lock')
158
+ jobs.times do |i|
159
+ lock = File.open(@lock.path, 'w')
160
+ fork { worker(todo, lock) }
161
+ lock.close
162
+ end
163
+ @lock.unlink
158
164
  todo.close
159
165
  scan_dir(path, st)
160
166
  @work.close
161
167
  Process.waitall
168
+ @lock.close
162
169
  ensure
163
170
  @work = nil
164
171
  end
@@ -246,7 +253,7 @@ def root_node
246
253
  end
247
254
 
248
255
  def dir_vivify(parts, ctime)
249
- @db.transaction do
256
+ synchronize do
250
257
  dir = root_node
251
258
  last = parts.pop
252
259
  parts.each do |name|
@@ -300,7 +307,7 @@ def scan_dir(path, st, parent_id = nil)
300
307
  dir = dir_vivify(@pwd.split(%r{/+}n), st.ctime.to_i)
301
308
  dir_id = dir[:id]
302
309
 
303
- @db.transaction do
310
+ synchronize do
304
311
  @db[:nodes].where(parent_id: dir_id).each do |node|
305
312
  File.exist?(node[:name]) or remove_entry(node)
306
313
  end
@@ -1,8 +1,8 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  begin
5
- require 'io/splice'
5
+ require 'sleepy_penguin'
6
6
  rescue LoadError
7
7
  end
8
8
  require_relative '../dtas'
@@ -20,12 +20,15 @@ def self.new
20
20
  rv
21
21
  end
22
22
 
23
- # create no-op methods for non-Linux
24
- unless method_defined?(:pipe_size=)
25
- def pipe_size=(_)
26
- end
23
+ def pipe_size=(nr)
24
+ defined?(SleepyPenguin::F_SETPIPE_SZ) and
25
+ fcntl(SleepyPenguin::F_SETPIPE_SZ, nr)
27
26
  end
28
27
 
28
+ def pipe_size
29
+ fcntl(SleepyPenguin::F_GETPIPE_SZ)
30
+ end if defined?(SleepyPenguin::F_GETPIPE_SZ)
31
+
29
32
  # avoid syscall, we never change IO#nonblock= directly
30
33
  def nonblock?
31
34
  false
@@ -0,0 +1,75 @@
1
+ # Copyright (C) 2017-2019 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+ require_relative '../dtas'
5
+ require_relative 'spawn_fix'
6
+
7
+ module DTAS::Pipeline # :nodoc:
8
+ include DTAS::SpawnFix
9
+
10
+ # Process.spawn wrapper which supports running Proc-like objects in
11
+ # a separate process, not just external commands.
12
+ # Returns the pid of the spawned process
13
+ def pspawn(env, cmd, rdr = {})
14
+ case cmd
15
+ when Array
16
+ spawn(env, *cmd, rdr)
17
+ else # support running Proc-like objects, too:
18
+ fork do
19
+ ENV.update(env) if env
20
+
21
+ # setup redirects
22
+ [ $stdin, $stdout, $stderr ].each_with_index do |io, fd|
23
+ dst = rdr[fd] and io.reopen(dst)
24
+ end
25
+
26
+ # close all other pipes, since we can't rely on FD_CLOEXEC
27
+ # (as we do not exec, here)
28
+ rdr.each do |k, v|
29
+ k.close if v == :close
30
+ end
31
+ cmd.call
32
+ end
33
+ end
34
+ end
35
+
36
+ # +pipeline+ is an Array of (Arrays or Procs)
37
+ def run_pipeline(env, pipeline)
38
+ pids = {} # pid => pipeline index
39
+ work = pipeline.dup
40
+ last = work.pop
41
+ nr = work.size
42
+ rdr = {} # redirect mapping for Process.spawn
43
+
44
+ # we need to make sure pipes are closed in any forked processes
45
+ # (they are redirected to stdin or stdout, first)
46
+ pipes = nr.times.map { IO.pipe.each { |io| rdr[io] = :close } }
47
+
48
+ # start the first and last commands first, they only have one pipe, each
49
+ last_pid = pspawn(env, last, rdr.merge(0 => pipes[-1][0]))
50
+ pids[last_pid] = nr
51
+ first = work.shift
52
+ first_pid = pspawn(env, first, rdr.merge(1 => pipes[0][1]))
53
+ pids[first_pid] = 0
54
+
55
+ # start the middle commands, they both have two pipes:
56
+ work.each_with_index do |cmd, i|
57
+ pid = pspawn(env, cmd, rdr.merge(0 => pipes[i][0], 1 => pipes[i+1][1]))
58
+ pids[pid] = i + 1
59
+ end
60
+
61
+ # all pipes handed off to children, close so they see EOF
62
+ pipes.flatten!.each(&:close).clear
63
+
64
+ # wait for children to finish
65
+ fails = []
66
+ until pids.empty?
67
+ pid, status = Process.waitpid2(-1)
68
+ nr = pids.delete(pid)
69
+ status.success? or
70
+ fails << "reaped #{nr} #{pipeline[nr].inspect} #{status.inspect}"
71
+ end
72
+ # behave like "set -o pipefail" in bash
73
+ raise fails.join("\n") if fails[0]
74
+ end
75
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  # encoding: binary
@@ -50,17 +50,19 @@ def mcache_lookup(infile)
50
50
  out =~ /^Sample Rate\s*:\s*(\d+)/n and dst['rate'] = $1.to_i
51
51
  out =~ /^Precision\s*:\s*(\d+)-bit/n and dst['bits'] = $1.to_i
52
52
 
53
+ enc = Encoding.default_external # typically Encoding::UTF_8
53
54
  if out =~ /\nComments\s*:[ \t]*\n?(.*)\z/mn
54
55
  comments = dst['comments'] = {}
55
56
  key = nil
56
57
  $1.split(/\n/n).each do |line|
57
58
  if line.sub!(/^([^=]+)=/ni, '')
58
- key = DTAS.dedupe_str($1.upcase)
59
+ key = DTAS.dedupe_str(DTAS.try_enc($1.upcase, enc))
59
60
  end
60
61
  (comments[key] ||= ''.b) << "#{line}\n" unless line.empty?
61
62
  end
62
63
  comments.each do |k,v|
63
64
  v.chomp!
65
+ DTAS.try_enc(v, enc)
64
66
  comments[k] = DTAS.dedupe_str(v)
65
67
  end
66
68
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  begin
@@ -37,9 +37,13 @@ def watch_files(paths, blk)
37
37
  @dir2wd = {}
38
38
  Array(paths).each do |path|
39
39
  watchdir, watchbase = File.split(File.expand_path(path))
40
- wd = @dir2wd[watchdir] ||= add_watch(watchdir, FLAGS)
41
- m = @watches[wd] ||= {}
42
- m[watchbase] = true
40
+ begin
41
+ wd = @dir2wd[watchdir] ||= add_watch(watchdir, FLAGS)
42
+ m = @watches[wd] ||= {}
43
+ m[watchbase] = true
44
+ rescue SystemCallError => e
45
+ warn "#{watchdir.dump}: #{e.message} (#{e.class})"
46
+ end
43
47
  end
44
48
  end
45
49
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require './test/helper'
@@ -49,14 +49,14 @@ def test_set_buffer_size
49
49
  buf = new_buffer
50
50
  buf.buffer_size = @@max_size
51
51
  assert_equal @@max_size, buf.buffer_size
52
- end if @@max_size
52
+ end if defined?(SleepyPenguin::F_GETPIPE_SZ)
53
53
 
54
54
  def test_buffer_size
55
55
  buf = new_buffer
56
56
  assert_operator buf.buffer_size, :>, 128
57
57
  buf.buffer_size = @@max_size
58
58
  assert_equal @@max_size, buf.buffer_size
59
- end if @@max_size
59
+ end if defined?(SleepyPenguin::F_GETPIPE_SZ)
60
60
 
61
61
  def test_broadcast_1
62
62
  buf = new_buffer
@@ -87,9 +87,9 @@ def test_broadcast_tee
87
87
  assert_empty blocked
88
88
  assert_equal "HELLO", a[0].read(5)
89
89
  assert_equal "HELLO", b[0].read(5)
90
- max = '*' * a[0].pipe_size
90
+ max = '*' * pipe_size(a[0])
91
91
  assert_equal max.size, a[1].write(max)
92
- assert_equal a[0].nread, a[0].pipe_size
92
+ assert_equal a[0].nread, pipe_size(a[0])
93
93
  a[1].nonblock = true
94
94
  assert_equal 5, buf.__broadcast_tee(blocked, [a[1], b[1]], 5)
95
95
  assert_equal [a[1]], blocked
@@ -108,10 +108,10 @@ def test_broadcast
108
108
  assert_equal "HELLO", a[0].read(5)
109
109
  assert_equal "HELLO", b[0].read(5)
110
110
 
111
- return unless b[1].respond_to?(:pipe_size)
111
+ return unless defined?(SleepyPenguin::F_GETPIPE_SZ)
112
112
 
113
113
  b[1].nonblock = true
114
- b[1].write('*' * b[1].pipe_size)
114
+ b[1].write('*' * pipe_size(b[1]))
115
115
  buf.wr.write "BYE"
116
116
  assert_equal :wait_readable, buf.broadcast([a[1], b[1]])
117
117
  assert_equal 8, buf.bytes_xfer
@@ -157,8 +157,8 @@ def test_broadcast_all_full
157
157
  a = pipe
158
158
  b = pipe
159
159
  buf = new_buffer
160
- a[1].write('*' * a[1].pipe_size)
161
- b[1].write('*' * b[1].pipe_size)
160
+ a[1].write('*' * pipe_size(a[1]))
161
+ b[1].write('*' * pipe_size(b[1]))
162
162
 
163
163
  a[1].nonblock = true
164
164
  b[1].nonblock = true
@@ -167,7 +167,7 @@ def test_broadcast_all_full
167
167
  buf.wr.write "HELLO"
168
168
  assert_equal tmp, buf.broadcast(tmp)
169
169
  assert_equal [a[1], b[1]], tmp
170
- end if IO.method_defined?(:pipe_size)
170
+ end if defined?(SleepyPenguin::F_GETPIPE_SZ)
171
171
 
172
172
  def test_serialize
173
173
  buf = new_buffer
@@ -204,4 +204,8 @@ def test_load_size
204
204
  assert_equal 4096, buf.buffer_size
205
205
  buf.close!
206
206
  end
207
+
208
+ def pipe_size(io)
209
+ io.fcntl(SleepyPenguin::F_GETPIPE_SZ)
210
+ end
207
211
  end
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2018-2019 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+ require './test/helper'
5
+ require 'dtas'
6
+ require 'yaml'
7
+
8
+ class TestEncoding < Testcase
9
+ def test_encoding
10
+ data = <<EOD # <20180111114546.77906b35@cumparsita.ch>
11
+ ---
12
+ comments:
13
+ ARTIST: !binary |-
14
+ RW5yaXF1ZSBSb2Ryw61ndWV6
15
+ EOD
16
+ hash = YAML.load(data)
17
+ artist = DTAS.try_enc(hash['comments']['ARTIST'], Encoding::UTF_8)
18
+ assert_equal 'Enrique Rodríguez', artist
19
+ end
20
+ end
@@ -1,10 +1,10 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative 'helper'
5
5
  begin
6
6
  require 'dtas/mlib'
7
- require 'sequel/no_core_ext'
7
+ require 'sequel'
8
8
  require 'sqlite3'
9
9
  rescue LoadError => err
10
10
  warn "skipping mlib test: #{err.message}"
@@ -13,7 +13,7 @@
13
13
 
14
14
  class TestMlib < Testcase
15
15
  def setup
16
- @db = Sequel.sqlite(':memory:')
16
+ @db = Sequel.sqlite(':memory:', case_sensitive_like: false)
17
17
  end
18
18
 
19
19
  def test_migrate
@@ -0,0 +1,47 @@
1
+ # Copyright (C) 2017-2019 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+ require './test/helper'
5
+ require 'dtas/pipeline'
6
+
7
+ class TestPipeline < Testcase
8
+ include DTAS::Pipeline
9
+ def setup
10
+ @env = ENV.to_hash
11
+ end
12
+
13
+ def pipeline_result
14
+ IO.pipe do |rd, wr|
15
+ begin
16
+ pid = fork do
17
+ rd.close
18
+ $stdout.reopen(wr)
19
+ yield
20
+ exit!(0)
21
+ end
22
+ wr.close
23
+ return rd.read
24
+ ensure
25
+ _, status = Process.waitpid2(pid)
26
+ assert_predicate status, :success?
27
+ end
28
+ end
29
+ nil
30
+ end
31
+
32
+ def test_pipeline
33
+ assert_equal("BYYRU\n", pipeline_result do
34
+ run_pipeline(@env, [
35
+ %w(echo hello), # anything which generates something to stdout
36
+ %w(tr [a-z] [A-Z]), # upcase
37
+ # this lambda runs inside its own process
38
+ lambda do
39
+ $stdin.each_line { |l| $stdout.write("#{l.chomp.reverse}\n") }
40
+ exit!(0)
41
+ end,
42
+ # rot13
43
+ %w(tr [a-m][n-z][A-M][N-Z] [n-z][a-m][N-Z][A-M])
44
+ ])
45
+ end)
46
+ end
47
+ end
@@ -1,8 +1,8 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  begin
5
- require 'io/splice'
5
+ require 'sleepy_penguin'
6
6
  require './test/player_integration'
7
7
  class TestSinkPipeSizeIntegration < Testcase
8
8
  include PlayerIntegration
@@ -14,7 +14,7 @@ def test_sink_pipe_size_integration
14
14
  s.req_ok("sink ed default pipe_size=0x10000")
15
15
  s.req_ok("sink ed default pipe_size=")
16
16
  s.req_ok("sink ed default pipe_size=4096")
17
- end if IO.method_defined?(:pipe_size=)
17
+ end if SleepyPenguin.const_defined?(:F_SETPIPE_SZ)
18
18
  end
19
19
  rescue LoadError
20
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dtas
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dtas hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-07 00:00:00.000000000 Z
11
+ date: 2019-01-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  Free Software command-line tools for audio playback, mastering, and
@@ -98,6 +98,7 @@ files:
98
98
  - lib/dtas/cue_index.rb
99
99
  - lib/dtas/disclaimer.rb
100
100
  - lib/dtas/edit_client.rb
101
+ - lib/dtas/encoding.rb
101
102
  - lib/dtas/fadefx.rb
102
103
  - lib/dtas/format.rb
103
104
  - lib/dtas/mcache.rb
@@ -108,6 +109,7 @@ files:
108
109
  - lib/dtas/parse_time.rb
109
110
  - lib/dtas/partstats.rb
110
111
  - lib/dtas/pipe.rb
112
+ - lib/dtas/pipeline.rb
111
113
  - lib/dtas/player.rb
112
114
  - lib/dtas/player/client_handler.rb
113
115
  - lib/dtas/process.rb
@@ -164,6 +166,7 @@ files:
164
166
  - test/helper.rb
165
167
  - test/player_integration.rb
166
168
  - test/test_buffer.rb
169
+ - test/test_encoding.rb
167
170
  - test/test_env.rb
168
171
  - test/test_fadefx.rb
169
172
  - test/test_format.rb
@@ -171,6 +174,7 @@ files:
171
174
  - test/test_mcache.rb
172
175
  - test/test_mlib.rb
173
176
  - test/test_parse_freq.rb
177
+ - test/test_pipeline.rb
174
178
  - test/test_player.rb
175
179
  - test/test_player_client_handler.rb
176
180
  - test/test_player_integration.rb
@@ -206,8 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
210
  - !ruby/object:Gem::Version
207
211
  version: '0'
208
212
  requirements: []
209
- rubyforge_project:
210
- rubygems_version: 2.6.10
213
+ rubygems_version: 3.0.1
211
214
  signing_key:
212
215
  specification_version: 4
213
216
  summary: duct tape audio suite for *nix