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 +5 -5
- data/GIT-VERSION-GEN +2 -2
- data/INSTALL +7 -9
- data/README +2 -2
- data/Rakefile +2 -2
- data/bin/dtas-console +3 -3
- data/bin/dtas-readahead +2 -2
- data/dtas-linux.gemspec +3 -4
- data/lib/dtas.rb +3 -1
- data/lib/dtas/buffer.rb +4 -2
- data/lib/dtas/buffer/read_write.rb +2 -2
- data/lib/dtas/buffer/splice.rb +12 -10
- data/lib/dtas/encoding.rb +58 -0
- data/lib/dtas/mlib.rb +38 -31
- data/lib/dtas/pipe.rb +9 -6
- data/lib/dtas/pipeline.rb +75 -0
- data/lib/dtas/source/sox.rb +4 -2
- data/lib/dtas/watchable.rb +8 -4
- data/test/test_buffer.rb +14 -10
- data/test/test_encoding.rb +20 -0
- data/test/test_mlib.rb +3 -3
- data/test/test_pipeline.rb +47 -0
- data/test/test_sink_pipe_size.rb +3 -3
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d887afd41dc71abd3448e72c3d33d188e4629b8dbc576af3237c3bd47b246581
|
4
|
+
data.tar.gz: cf5262f4c45becd00e75dc2f1c3de5ab05b27bd9a029c9fef458faec5cc58303
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94f470ad51f9723bf5d3161b445d0434e6e5e45349fc6c5227ecb80aa0c9d1409357979cd8f97edc909bed6c65b75494757b731d2f7cd97f4ed345640465aa7a
|
7
|
+
data.tar.gz: 631dc0a9b16771662bb6f8215ab9ee71acd078ac7cae26f1b90408106da840909d59f99cdf569425b2be05f2c9018f032ad426405ca1bbe1df28af005d2f020c
|
data/GIT-VERSION-GEN
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# Copyright (C) 2013-
|
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.
|
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
|
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 "
|
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/
|
42
|
+
https://80x24.org/dtas/2019/dtas-0.16.0.tar.gz
|
43
43
|
|
44
|
-
$ tar zxvf dtas-0.
|
45
|
-
$ cd dtas-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 "
|
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-
|
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
|
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-
|
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-
|
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
|
|
data/bin/dtas-console
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# Copyright (C) 2013-
|
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
|
data/bin/dtas-readahead
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# Copyright (C) 2015-
|
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+/
|
78
|
+
`ps h -o pid --ppid=#{ppid}`.split(/\s+/).map(&:to_i)
|
79
79
|
end
|
80
80
|
|
81
81
|
def expand_pid(pid)
|
data/dtas-linux.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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<
|
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
|
data/lib/dtas.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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)
|
data/lib/dtas/buffer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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 '
|
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-
|
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
|
-
# "
|
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
|
data/lib/dtas/buffer/splice.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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 '
|
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 "
|
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 =
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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 =
|
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
|
data/lib/dtas/mlib.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# Copyright (C) 2015-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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]+)/
|
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
|
-
|
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
|
113
|
-
|
114
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/dtas/pipe.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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 '
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
data/lib/dtas/source/sox.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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
|
data/lib/dtas/watchable.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
data/test/test_buffer.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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
|
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
|
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]
|
90
|
+
max = '*' * pipe_size(a[0])
|
91
91
|
assert_equal max.size, a[1].write(max)
|
92
|
-
assert_equal a[0].nread, a[0]
|
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
|
111
|
+
return unless defined?(SleepyPenguin::F_GETPIPE_SZ)
|
112
112
|
|
113
113
|
b[1].nonblock = true
|
114
|
-
b[1].write('*' * b[1]
|
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]
|
161
|
-
b[1].write('*' * b[1]
|
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
|
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
|
data/test/test_mlib.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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
|
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
|
data/test/test_sink_pipe_size.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Copyright (C) 2013-
|
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 '
|
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
|
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.
|
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:
|
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
|
-
|
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
|