dtas 0.11.0 → 0.12.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 +4 -4
- data/.gitattributes +4 -0
- data/Documentation/GNUmakefile +1 -1
- data/Documentation/dtas-console.txt +1 -0
- data/Documentation/dtas-player_protocol.txt +19 -5
- data/Documentation/dtas-splitfx.txt +13 -0
- data/Documentation/dtas-tl.txt +16 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/INSTALL +3 -3
- data/README +4 -0
- data/bin/dtas-archive +5 -1
- data/bin/dtas-console +13 -6
- data/bin/dtas-cueedit +1 -1
- data/bin/dtas-mlib +47 -0
- data/bin/dtas-readahead +211 -0
- data/bin/dtas-sinkedit +1 -1
- data/bin/dtas-sourceedit +1 -1
- data/bin/dtas-splitfx +15 -6
- data/bin/dtas-tl +81 -5
- data/dtas.gemspec +2 -2
- data/lib/dtas.rb +17 -0
- data/lib/dtas/buffer/read_write.rb +21 -19
- data/lib/dtas/buffer/splice.rb +1 -2
- data/lib/dtas/format.rb +2 -2
- data/lib/dtas/mlib.rb +500 -0
- data/lib/dtas/mlib/migrations/0001_initial.rb +42 -0
- data/lib/dtas/nonblock.rb +24 -0
- data/lib/dtas/parse_freq.rb +29 -0
- data/lib/dtas/parse_time.rb +5 -2
- data/lib/dtas/pipe.rb +2 -1
- data/lib/dtas/player.rb +21 -41
- data/lib/dtas/player/client_handler.rb +175 -92
- data/lib/dtas/process.rb +41 -17
- data/lib/dtas/sigevent/pipe.rb +6 -5
- data/lib/dtas/sink.rb +1 -1
- data/lib/dtas/source/splitfx.rb +14 -0
- data/lib/dtas/splitfx.rb +52 -36
- data/lib/dtas/track.rb +13 -0
- data/lib/dtas/tracklist.rb +148 -43
- data/lib/dtas/unix_accepted.rb +49 -32
- data/lib/dtas/unix_client.rb +1 -1
- data/lib/dtas/unix_server.rb +17 -9
- data/lib/dtas/watchable.rb +16 -5
- data/test/test_env.rb +16 -0
- data/test/test_mlib.rb +31 -0
- data/test/test_parse_freq.rb +18 -0
- data/test/test_player_client_handler.rb +12 -12
- data/test/test_splitfx.rb +0 -29
- data/test/test_tracklist.rb +75 -17
- data/test/test_unixserver.rb +0 -11
- metadata +16 -4
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
|
4
|
+
Sequel.migration do
|
5
|
+
up do
|
6
|
+
create_table(:nodes) do
|
7
|
+
primary_key :id
|
8
|
+
String :name, null: false # encoding: binary, POSIX
|
9
|
+
Integer :ctime
|
10
|
+
foreign_key :parent_id, :nodes, null: false # parent dir
|
11
|
+
# >= 0: tlen of track, -2: ignore, -1: directory
|
12
|
+
Integer :tlen, null: false
|
13
|
+
unique [ :parent_id, :name ]
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table(:tags) do
|
17
|
+
primary_key :id
|
18
|
+
String :tag, null: false, unique: true # encoding: US-ASCII
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table(:vals) do
|
22
|
+
primary_key :id
|
23
|
+
String :val, null: false, unique: true # encoding: UTF-8
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table(:comments) do
|
27
|
+
foreign_key :node_id, :nodes, null: false
|
28
|
+
foreign_key :tag_id, :tags, null: false
|
29
|
+
foreign_key :val_id, :vals, null: false
|
30
|
+
primary_key [ :node_id, :tag_id, :val_id ]
|
31
|
+
index :node_id
|
32
|
+
index [ :tag_id, :val_id ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
down do
|
37
|
+
drop_table(:nodes)
|
38
|
+
drop_table(:tags)
|
39
|
+
drop_table(:vals)
|
40
|
+
drop_table(:comments)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
|
2
|
+
# License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
|
4
|
+
class DTAS::Nonblock < IO
|
5
|
+
if RUBY_VERSION.to_f <= 2.0
|
6
|
+
EX = {}.freeze
|
7
|
+
def read_nonblock(len, buf = nil, opts = EX)
|
8
|
+
super(len, buf)
|
9
|
+
rescue IO::WaitReadable
|
10
|
+
raise if opts[:exception]
|
11
|
+
:wait_readable
|
12
|
+
rescue EOFError
|
13
|
+
raise if opts[:exception]
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_nonblock(buf, opts = EX)
|
18
|
+
super(buf)
|
19
|
+
rescue IO::WaitWritable
|
20
|
+
raise if opts[:exception]
|
21
|
+
:wait_writable
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright (C) 2015 all contributors <dtas-all@nongnu.org>
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
|
4
|
+
require_relative '../dtas'
|
5
|
+
module DTAS::ParseFreq
|
6
|
+
|
7
|
+
# may return a negative frequency meaning lowpass
|
8
|
+
def parse_freq(val, round = true)
|
9
|
+
case val
|
10
|
+
when String
|
11
|
+
val = val.dup
|
12
|
+
mult = val.sub!(/k\z/, '') ? 1000 : 1
|
13
|
+
val = (val.to_f * mult)
|
14
|
+
when Numeric
|
15
|
+
val
|
16
|
+
else
|
17
|
+
raise ArgumentError, "non-numeric value given"
|
18
|
+
end
|
19
|
+
|
20
|
+
case round
|
21
|
+
when true, :int
|
22
|
+
val.round
|
23
|
+
when :float
|
24
|
+
val.to_f
|
25
|
+
else
|
26
|
+
raise ArgumentError, "usage: parse_freq(val, (true|:round))"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/dtas/parse_time.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
3
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
4
|
require_relative '../dtas'
|
4
5
|
|
5
6
|
module DTAS::ParseTime
|
6
|
-
# convert a string time to seconds, returning a
|
7
|
+
# convert a string time to seconds, returning a Floot or Integer
|
7
8
|
def parse_time(time)
|
8
9
|
case time
|
10
|
+
when Numeric
|
11
|
+
time
|
9
12
|
when /\A\d+\z/
|
10
13
|
time.to_i
|
11
14
|
when /\A[\d\.]+\z/
|
@@ -15,7 +18,7 @@ def parse_time(time)
|
|
15
18
|
rv = hhmmss.sub!(/\.(\d+)\z/, "") ? "0.#$1".to_f : 0
|
16
19
|
|
17
20
|
# deal with HH:MM:SS
|
18
|
-
t = hhmmss.split(
|
21
|
+
t = hhmmss.split(':')
|
19
22
|
raise ArgumentError, "Bad time format: #{hhmmss}" if t.size > 3
|
20
23
|
|
21
24
|
mult = 1
|
data/lib/dtas/pipe.rb
CHANGED
@@ -6,9 +6,10 @@
|
|
6
6
|
end
|
7
7
|
require_relative '../dtas'
|
8
8
|
require_relative 'writable_iter'
|
9
|
+
require_relative 'nonblock'
|
9
10
|
|
10
11
|
# pipe wrapper for -player sinks
|
11
|
-
class DTAS::Pipe <
|
12
|
+
class DTAS::Pipe < DTAS::Nonblock # :nodoc:
|
12
13
|
include DTAS::WritableIter
|
13
14
|
attr_accessor :sink
|
14
15
|
|
data/lib/dtas/player.rb
CHANGED
@@ -190,7 +190,7 @@ def enq_handler(io, msg)
|
|
190
190
|
io.emit("OK")
|
191
191
|
end
|
192
192
|
|
193
|
-
def
|
193
|
+
def dpc_enq_head(io, msg)
|
194
194
|
# check @queue[0] in case we have no sinks
|
195
195
|
if need_to_queue
|
196
196
|
@queue.unshift(msg)
|
@@ -207,53 +207,33 @@ def client_iter(io, msg)
|
|
207
207
|
case command
|
208
208
|
when "enq"
|
209
209
|
enq_handler(io, msg[0])
|
210
|
-
when "enq-head"
|
211
|
-
do_enq_head(io, msg)
|
212
210
|
when "enq-cmd"
|
213
211
|
enq_handler(io, { "command" => msg[0]})
|
214
212
|
when "pause", "play", "play_pause"
|
215
213
|
play_pause_handler(io, command)
|
216
|
-
when "seek"
|
217
|
-
do_seek(io, msg[0])
|
218
|
-
when "clear"
|
219
|
-
@queue.clear
|
220
|
-
wall("clear")
|
221
|
-
io.emit("OK")
|
222
|
-
when "rg"
|
223
|
-
rg_handler(io, msg)
|
224
|
-
when "cue"
|
225
|
-
cue_handler(io, msg)
|
226
|
-
when "skip"
|
227
|
-
skip_handler(io, msg)
|
228
|
-
when "sink"
|
229
|
-
sink_handler(io, msg)
|
230
|
-
when "current"
|
231
|
-
current_handler(io, msg)
|
232
|
-
when "watch"
|
233
|
-
@watchers[io] = true
|
234
|
-
io.emit("OK")
|
235
|
-
when "format"
|
236
|
-
format_handler(io, msg)
|
237
|
-
when "env"
|
238
|
-
env_handler(io, msg)
|
239
|
-
when "restart"
|
240
|
-
restart_pipeline
|
241
|
-
io.emit("OK")
|
242
|
-
when "source"
|
243
|
-
source_handler(io, msg)
|
244
|
-
when "state"
|
245
|
-
state_file_handler(io, msg)
|
246
|
-
when "cd"
|
247
|
-
chdir_handler(io, msg)
|
248
214
|
when "pwd"
|
249
215
|
io.emit(Dir.pwd)
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
trim_handler(io, msg)
|
216
|
+
else
|
217
|
+
m = "dpc_#{command.tr('-', '_')}"
|
218
|
+
__send__(m, io, msg) if respond_to?(m)
|
254
219
|
end
|
255
220
|
end
|
256
221
|
|
222
|
+
def dpc_clear(io, msg)
|
223
|
+
@queue.clear
|
224
|
+
wall('clear')
|
225
|
+
io.emit('OK')
|
226
|
+
end
|
227
|
+
|
228
|
+
def dpc_queue(io, msg)
|
229
|
+
'cat' == msg[0] and io.emit(@queue.to_yaml)
|
230
|
+
end
|
231
|
+
|
232
|
+
def dpc_watch(io, _)
|
233
|
+
@watchers[io] = true
|
234
|
+
io.emit('OK')
|
235
|
+
end
|
236
|
+
|
257
237
|
def event_loop_iter
|
258
238
|
@srv.run_once do |io, msg| # readability handler, request/response
|
259
239
|
case io
|
@@ -307,7 +287,7 @@ def sink_death(sink, status)
|
|
307
287
|
return unless sink.active
|
308
288
|
|
309
289
|
if (@current || @queue[0]) && !@paused
|
310
|
-
# we get here if source/sinks are all killed in
|
290
|
+
# we get here if source/sinks are all killed in dpc_restart
|
311
291
|
__sink_activate(sink)
|
312
292
|
next_source(_next) unless @current
|
313
293
|
end
|
@@ -426,7 +406,7 @@ def next_source(source_spec)
|
|
426
406
|
|
427
407
|
dst = @sink_buf
|
428
408
|
pending.dst_assoc(dst)
|
429
|
-
pending.src_spawn(@format, @rg, out: dst.wr, in:
|
409
|
+
pending.src_spawn(@format, @rg, out: dst.wr, in: DTAS.null)
|
430
410
|
|
431
411
|
# watch and restart on modifications
|
432
412
|
pending.respond_to?(:watch_begin) and
|
@@ -117,7 +117,7 @@ def __sink_snapshot(sink)
|
|
117
117
|
end
|
118
118
|
|
119
119
|
# returns a wait_ctl arg
|
120
|
-
def
|
120
|
+
def dpc_sink(io, msg)
|
121
121
|
name = msg[1]
|
122
122
|
case msg[0]
|
123
123
|
when "ls"
|
@@ -240,7 +240,7 @@ def current_expect_samples(in_samples) # @current.samples
|
|
240
240
|
out_samples(in_samples, @current.format, @format)
|
241
241
|
end
|
242
242
|
|
243
|
-
def
|
243
|
+
def dpc_rg(io, msg)
|
244
244
|
return io.emit(@rg.to_hsh.to_yaml) if msg.empty?
|
245
245
|
before = @rg.to_hsh
|
246
246
|
msg.each do |kv|
|
@@ -277,7 +277,7 @@ def active_sinks
|
|
277
277
|
|
278
278
|
# show current info about what's playing
|
279
279
|
# returns non-blocking iterator retval
|
280
|
-
def
|
280
|
+
def dpc_current(io, msg)
|
281
281
|
tmp = {}
|
282
282
|
if @current
|
283
283
|
tmp["current"] = s = @current.to_hsh
|
@@ -312,6 +312,7 @@ def current_handler(io, msg)
|
|
312
312
|
h
|
313
313
|
end
|
314
314
|
end
|
315
|
+
tmp['tracklist'] = @tl.to_hsh(false)
|
315
316
|
io.emit(tmp.to_yaml)
|
316
317
|
end
|
317
318
|
|
@@ -321,7 +322,7 @@ def __buf_reset(buf) # buf is always @sink_buf for now
|
|
321
322
|
@srv.wait_ctl(buf, :wait_readable)
|
322
323
|
end
|
323
324
|
|
324
|
-
def
|
325
|
+
def dpc_skip(io, msg)
|
325
326
|
__current_drop
|
326
327
|
wall("skip")
|
327
328
|
io.emit("OK")
|
@@ -371,7 +372,8 @@ def seek_internal(cur, offset)
|
|
371
372
|
end
|
372
373
|
end
|
373
374
|
|
374
|
-
def
|
375
|
+
def dpc_seek(io, msg)
|
376
|
+
offset = msg[0]
|
375
377
|
if @current
|
376
378
|
if @current.respond_to?(:infile)
|
377
379
|
begin
|
@@ -408,7 +410,12 @@ def restart_pipeline
|
|
408
410
|
stop_sinks
|
409
411
|
end
|
410
412
|
|
411
|
-
def
|
413
|
+
def dpc_restart(io, _)
|
414
|
+
restart_pipeline
|
415
|
+
io.emit('OK')
|
416
|
+
end
|
417
|
+
|
418
|
+
def dpc_format(io, msg)
|
412
419
|
new_fmt = @format.dup
|
413
420
|
msg.each do |kv|
|
414
421
|
k, v = kv.split(/=/, 2)
|
@@ -443,7 +450,7 @@ def format_handler(io, msg)
|
|
443
450
|
io.emit("OK")
|
444
451
|
end
|
445
452
|
|
446
|
-
def
|
453
|
+
def dpc_env(io, msg)
|
447
454
|
if msg.empty?
|
448
455
|
# this may fail for large envs due to SEQPACKET size restrictions
|
449
456
|
# do we care?
|
@@ -465,7 +472,7 @@ def env_handler(io, msg)
|
|
465
472
|
io.emit("OK")
|
466
473
|
end
|
467
474
|
|
468
|
-
def
|
475
|
+
def dpc_source(io, msg)
|
469
476
|
map = @source_map
|
470
477
|
op = msg.shift
|
471
478
|
case op
|
@@ -509,7 +516,7 @@ def source_handler(io, msg)
|
|
509
516
|
end
|
510
517
|
end
|
511
518
|
|
512
|
-
def
|
519
|
+
def dpc_cd(io, msg)
|
513
520
|
msg.size == 1 or return io.emit("ERR usage: cd DIRNAME")
|
514
521
|
begin
|
515
522
|
Dir.chdir(msg[0])
|
@@ -520,9 +527,27 @@ def chdir_handler(io, msg)
|
|
520
527
|
io.emit("OK")
|
521
528
|
end
|
522
529
|
|
523
|
-
def
|
530
|
+
def state_file_dump_async(io, sf)
|
531
|
+
on_death = lambda { |_| @srv.wait_ctl(io, :wait_readable) }
|
532
|
+
pid = fork do
|
533
|
+
begin
|
534
|
+
begin
|
535
|
+
sf.dump(self)
|
536
|
+
res = 'OK'
|
537
|
+
rescue => e
|
538
|
+
res = "ERR dumping to #{xs(sf.path)} #{e.message}"
|
539
|
+
end
|
540
|
+
io.to_io.send(res, Socket::MSG_EOR)
|
541
|
+
ensure
|
542
|
+
exit!(0)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
DTAS::Process::PIDS[pid] = on_death
|
546
|
+
end
|
547
|
+
|
548
|
+
def dpc_state(io, msg)
|
524
549
|
case msg.shift
|
525
|
-
when
|
550
|
+
when 'dump'
|
526
551
|
dest = msg.shift
|
527
552
|
if dest
|
528
553
|
sf = DTAS::StateFile.new(dest, false)
|
@@ -532,13 +557,9 @@ def state_file_handler(io, msg)
|
|
532
557
|
else
|
533
558
|
return io.emit("ERR no state file configured")
|
534
559
|
end
|
535
|
-
|
536
|
-
|
537
|
-
rescue => e
|
538
|
-
return io.emit("ERR dumping to #{xs(dest)} #{e.message}")
|
539
|
-
end
|
560
|
+
state_file_dump_async(io, sf)
|
561
|
+
:ignore
|
540
562
|
end
|
541
|
-
io.emit("OK")
|
542
563
|
end
|
543
564
|
|
544
565
|
def _tl_skip
|
@@ -546,84 +567,144 @@ def _tl_skip
|
|
546
567
|
__current_drop
|
547
568
|
end
|
548
569
|
|
549
|
-
def
|
570
|
+
def dpc_tl(io, msg)
|
571
|
+
sub = msg.shift
|
572
|
+
m = "_dpc_tl_#{sub.tr('-', '_')}"
|
573
|
+
__send__(m, io, msg) if respond_to?(m)
|
574
|
+
end
|
575
|
+
|
576
|
+
def _dpc_tl_add(io, msg)
|
577
|
+
path = msg.shift
|
578
|
+
after_track_id = msg.shift
|
579
|
+
after_track_id = after_track_id.to_i if after_track_id
|
580
|
+
case set_as_current = msg.shift
|
581
|
+
when 'true' then set_as_current = true
|
582
|
+
when 'false', nil then set_as_current = false
|
583
|
+
else
|
584
|
+
return io.emit('ERR tl add PATH [after_track_id] [true|false]')
|
585
|
+
end
|
586
|
+
begin
|
587
|
+
track_id = @tl.add_track(path, after_track_id, set_as_current)
|
588
|
+
return io.emit('ERR FULL') unless track_id
|
589
|
+
rescue ArgumentError => e
|
590
|
+
return io.emit("ERR #{e.message}")
|
591
|
+
end
|
592
|
+
|
593
|
+
_tl_skip if set_as_current # if @current is playing, it will restart soon
|
594
|
+
|
595
|
+
# start playing if we're currently idle
|
596
|
+
next_source(_next) unless need_to_queue
|
597
|
+
io.emit(track_id.to_s)
|
598
|
+
end
|
599
|
+
|
600
|
+
def _dpc_tl_repeat(io, msg)
|
601
|
+
prev = @tl.repeat.to_s
|
550
602
|
case msg.shift
|
551
|
-
when
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
else
|
559
|
-
return io.emit("ERR tl add PATH [after_track_id] [true|false]")
|
560
|
-
end
|
561
|
-
begin
|
562
|
-
track_id = @tl.add_track(path, after_track_id, set_as_current)
|
563
|
-
rescue ArgumentError => e
|
564
|
-
return io.emit("ERR #{e.message}")
|
565
|
-
end
|
603
|
+
when 'true' then @tl.repeat = true
|
604
|
+
when 'false' then @tl.repeat = false
|
605
|
+
when '1' then @tl.repeat = 1
|
606
|
+
when nil
|
607
|
+
end
|
608
|
+
io.emit("tl repeat #{prev}")
|
609
|
+
end
|
566
610
|
|
567
|
-
|
611
|
+
def _dpc_tl_shuffle(io, msg)
|
612
|
+
prev = (!!@tl.shuffle).to_s
|
613
|
+
v = msg.shift
|
614
|
+
case v
|
615
|
+
when 'debug' then return io.emit(@tl.shuffle.to_yaml) # TODO: remove
|
616
|
+
when nil
|
617
|
+
else
|
618
|
+
set_bool(io, 'tl shuffle', v) { |b| @tl.shuffle = b }
|
619
|
+
end
|
620
|
+
io.emit("tl shuffle #{prev}")
|
621
|
+
end
|
568
622
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
when
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
_tl_skip
|
590
|
-
end
|
591
|
-
# drop it from the queue, too, in case it just got requeued or paused
|
592
|
-
@queue.delete_if { |t| Array === t && t[0].object_id == track_id }
|
593
|
-
io.emit("OK")
|
594
|
-
when "get"
|
595
|
-
res = @tl.get_tracks(msg.map!(&:to_i))
|
596
|
-
res.map! { |tid, file| "#{tid}=#{file ? Shellwords.escape(file) : ''}" }
|
597
|
-
io.emit("#{res.size} #{res.join(' ')}")
|
598
|
-
when "tracks"
|
599
|
-
tracks = @tl.tracks
|
600
|
-
io.emit("#{tracks.size} " << tracks.map!(&:to_s).join(' '))
|
601
|
-
when "goto"
|
602
|
-
track_id = msg.shift or return io.emit("ERR track_id not specified")
|
603
|
-
offset = msg.shift # may be nil
|
604
|
-
if @tl.go_to(track_id.to_i, offset)
|
605
|
-
_tl_skip
|
606
|
-
next_source(_next) unless need_to_queue
|
607
|
-
io.emit("OK")
|
608
|
-
else
|
609
|
-
io.emit("MISSING")
|
610
|
-
end
|
611
|
-
when "current"
|
612
|
-
path = @tl.cur_track
|
613
|
-
io.emit(path ? path : "NONE")
|
614
|
-
when "current-id"
|
615
|
-
path = @tl.cur_track
|
616
|
-
io.emit(path ? path.object_id.to_s : "NONE")
|
617
|
-
when "next"
|
623
|
+
def _dpc_tl_max(io, msg)
|
624
|
+
prev = @tl.max
|
625
|
+
case msg.shift
|
626
|
+
when nil
|
627
|
+
when %r{\A(\d[\d_]*)\z} then @tl.max = $1.to_i
|
628
|
+
else
|
629
|
+
return io.emit('ERR tl max must a non-negative integer')
|
630
|
+
end
|
631
|
+
io.emit("tl max #{prev}")
|
632
|
+
end
|
633
|
+
|
634
|
+
def _dpc_tl_remove(io, msg)
|
635
|
+
track_id = msg.shift or return io.emit('ERR track_id not specified')
|
636
|
+
track_id = track_id.to_i
|
637
|
+
path = @tl.remove_track(track_id) or return io.emit('MISSING')
|
638
|
+
rm = path.object_id
|
639
|
+
|
640
|
+
# skip if we're removing the currently playing track
|
641
|
+
if @current && @current.respond_to?(:infile) &&
|
642
|
+
@current.infile.object_id == rm
|
618
643
|
_tl_skip
|
619
|
-
|
620
|
-
|
621
|
-
|
644
|
+
end
|
645
|
+
# drop it from the queue, too, in case it just got requeued or paused
|
646
|
+
@queue.delete_if { |t| Array === t && t[0].object_id == rm }
|
647
|
+
io.emit(path)
|
648
|
+
end
|
649
|
+
|
650
|
+
def _dpc_tl_get(io, msg)
|
651
|
+
res = @tl.get_tracks(msg.map!(&:to_i))
|
652
|
+
res.map! { |tid, file| "#{tid}=#{file ? Shellwords.escape(file) : ''}" }
|
653
|
+
io.emit("#{res.size} #{res.join(' ')}")
|
654
|
+
end
|
655
|
+
|
656
|
+
def _dpc_tl_tracks(io, msg)
|
657
|
+
tracks = @tl.tracks
|
658
|
+
io.emit("#{tracks.size} " << tracks.map!(&:to_s).join(' '))
|
659
|
+
end
|
660
|
+
|
661
|
+
def _dpc_tl_goto(io, msg)
|
662
|
+
track_id = msg.shift or return io.emit('ERR track_id not specified')
|
663
|
+
offset = msg.shift # may be nil
|
664
|
+
if @tl.go_to(track_id.to_i, offset)
|
622
665
|
_tl_skip
|
623
|
-
|
666
|
+
next_source(_next) unless need_to_queue
|
667
|
+
io.emit('OK')
|
668
|
+
else
|
669
|
+
io.emit('MISSING')
|
624
670
|
end
|
625
671
|
end
|
626
672
|
|
673
|
+
def _dpc_tl_current(io, msg)
|
674
|
+
track = @tl.cur_track
|
675
|
+
io.emit(track ? track.to_path : 'NONE')
|
676
|
+
end
|
677
|
+
|
678
|
+
def _dpc_tl_current_id(io, msg)
|
679
|
+
track = @tl.cur_track
|
680
|
+
io.emit(track ? track.track_id.to_s : 'NONE')
|
681
|
+
end
|
682
|
+
|
683
|
+
def _dpc_tl_next(io, msg)
|
684
|
+
_tl_skip
|
685
|
+
io.emit('OK')
|
686
|
+
end
|
687
|
+
|
688
|
+
def _dpc_tl_prev(io, msg)
|
689
|
+
@tl.previous!
|
690
|
+
_tl_skip
|
691
|
+
io.emit('OK')
|
692
|
+
end
|
693
|
+
|
694
|
+
def _dpc_tl_clear(io, msg)
|
695
|
+
@tl.clear
|
696
|
+
_tl_skip
|
697
|
+
io.emit('OK')
|
698
|
+
end
|
699
|
+
|
700
|
+
def _dpc_tl_swap(io, msg)
|
701
|
+
usage = 'ERR usage: "tl swap TRACK_ID_A TRACK_ID_B"'
|
702
|
+
a_id = msg.shift or return io.emit(usage)
|
703
|
+
b_id = msg.shift or return io.emit(usage)
|
704
|
+
@tl.swap(a_id.to_i, b_id.to_i) or return io.emit('MISSING')
|
705
|
+
io.emit('OK')
|
706
|
+
end
|
707
|
+
|
627
708
|
def __bp_prev_next(io, msg, cur, bp)
|
628
709
|
case type = msg[1]
|
629
710
|
when nil, "track"
|
@@ -663,7 +744,7 @@ def __bp_prev_next(io, msg, cur, bp)
|
|
663
744
|
io.emit("OK")
|
664
745
|
end
|
665
746
|
|
666
|
-
def
|
747
|
+
def dpc_cue(io, msg)
|
667
748
|
cur = @current
|
668
749
|
if cur.respond_to?(:cuebreakpoints)
|
669
750
|
bp = cur.cuebreakpoints
|
@@ -684,10 +765,10 @@ def cue_handler(io, msg)
|
|
684
765
|
end
|
685
766
|
end
|
686
767
|
|
687
|
-
def
|
768
|
+
def dpc_trim(io, msg)
|
769
|
+
t = @trim
|
688
770
|
case msg.size
|
689
|
-
when 0
|
690
|
-
io.emit({ 'trim' => @trim }.to_yaml)
|
771
|
+
when 0 # OK
|
691
772
|
when 1, 2
|
692
773
|
case msg[0]
|
693
774
|
when 'off'
|
@@ -706,8 +787,10 @@ def trim_handler(io, msg)
|
|
706
787
|
end
|
707
788
|
end
|
708
789
|
__current_requeue
|
709
|
-
|
790
|
+
else
|
791
|
+
return io.emit('ERR usage: trim [off|TBEG [TLEN]]')
|
710
792
|
end
|
793
|
+
io.emit(t ? t.map(&:to_s).join(' ') : 'off')
|
711
794
|
end
|
712
795
|
end
|
713
796
|
# :startdoc:
|