[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/2] player: implement basic tracklist functionality
From: |
Eric Wong |
Subject: |
[PATCH 2/2] player: implement basic tracklist functionality |
Date: |
Mon, 9 Sep 2013 04:12:29 +0000 |
This should allow us to repeat through a list of tracks with relative
ease. There is a rudimentary dtas-tl client implemented. This
may be removed in the future.
---
bin/dtas-tl | 25 +++++++++++++
lib/dtas/player.rb | 28 +++++++++++++--
lib/dtas/player/client_handler.rb | 74 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 123 insertions(+), 4 deletions(-)
create mode 100755 bin/dtas-tl
diff --git a/bin/dtas-tl b/bin/dtas-tl
new file mode 100755
index 0000000..cbe1b83
--- /dev/null
+++ b/bin/dtas-tl
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+# Copyright (C) 2013, Eric Wong <address@hidden> and all contributors
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require 'dtas/unix_client'
+require 'yaml'
+require 'shellwords'
+
+c = DTAS::UNIXClient.new
+case cmd = ARGV[0]
+when "cat"
+ track_ids = c.req("tl tracks")
+ # we could get more, but SEQPACKET limits size...
+ track_ids.split(/ /).each do |track_id|
+ puts c.req("tl get #{track_id}")
+ end
+when "add-all"
+ ARGV.shift
+ ARGV.reverse.each do |path|
+ res = c.req_ok("tl add #{path}")
+ puts "#{path} #{res}"
+ end
+else
+ # act like dtas-ctl for now...
+ puts c.req([ "tl", *ARGV ])
+end
diff --git a/lib/dtas/player.rb b/lib/dtas/player.rb
index 2bf2716..eddaf70 100644
--- a/lib/dtas/player.rb
+++ b/lib/dtas/player.rb
@@ -15,6 +15,7 @@
require_relative 'sigevent'
require_relative 'rg_state'
require_relative 'state_file'
+require_relative 'tracklist'
class DTAS::Player # :nodoc:
require_relative 'player/client_handler'
@@ -25,6 +26,7 @@ class DTAS::Player # :nodoc:
attr_reader :sinks
def initialize
+ @tl = DTAS::Tracklist.new
@state_file = nil
@socket = nil
@srv = nil
@@ -87,6 +89,8 @@ def to_hsh
rv[k] = instance_variable_get("@#{k}").to_hsh
end
+ rv["tracklist"] = @tl.to_hsh
+
# no empty hashes or arrays
rv.delete_if do |k,v|
case v
@@ -111,6 +115,9 @@ def to_hsh
def self.load(hash)
rv = new
rv.instance_eval do
+ if v = hash["tracklist"]
+ @tl = DTAS::Tracklist.load(v)
+ end
@rg = DTAS::RGState.load(hash["rg"])
if v = hash["sink_buf"]
v = v["buffer_size"]
@@ -218,6 +225,8 @@ def client_iter(io, msg)
chdir_handler(io, msg)
when "pwd"
io.emit(Dir.pwd)
+ when "tl"
+ tl_handler(io, msg)
end
end
@@ -242,7 +251,7 @@ def reap_iter
obj.on_death(status) if obj.respond_to?(:on_death)
case obj
when @current
- next_source(@paused ? nil : @queue.shift)
+ next_source(@paused ? nil : _next)
when DTAS::Sink # on unexpected sink death
sink_death(obj, status)
end
@@ -250,6 +259,10 @@ def reap_iter
:wait_readable
end
+ def _next
+ @queue.shift || @tl.next_track
+ end
+
def sink_death(sink, status)
deleted = []
@targets.delete_if do |t|
@@ -272,7 +285,7 @@ def sink_death(sink, status)
if (@current || @queue[0]) && address@hidden
# we get here if source/sinks are all killed in restart_pipeline
__sink_activate(sink)
- next_source(@queue.shift) unless @current
+ next_source(_next) unless @current
end
end
@@ -337,6 +350,15 @@ def try_file(*args)
rv = src.try(*source_spec) and return rv
end
end
+
+ # don't get stuck in an infinite loop if @tl.repeat==true and we can't
+ # decode anything (FS errors, sox uninstalled, etc...)
+ while path = @tl.next_track(false)
+ @sources.each do |src|
+ rv = src.try(path) and return rv
+ end
+ end
+
echo "idle"
nil
end
@@ -410,7 +432,7 @@ def run
@srv.wait_ctl(sev, :wait_readable)
old_chld = trap(:CHLD) { sev.signal }
create_default_sink
- next_source(@paused ? nil : @queue.shift)
+ next_source(@paused ? nil : (@queue.shift || @tl.cur_track))
begin
event_loop_iter
rescue => e # just in case...
diff --git a/lib/dtas/player/client_handler.rb
b/lib/dtas/player/client_handler.rb
index 7f1c72e..2966193 100644
--- a/lib/dtas/player/client_handler.rb
+++ b/lib/dtas/player/client_handler.rb
@@ -343,7 +343,7 @@ def do_play
# no echo, next_source will echo on new track
@paused = false
return if @current
- next_source(@queue.shift)
+ next_source(_next)
end
def do_play_pause
@@ -508,5 +508,77 @@ def state_file_handler(io, msg)
end
io.emit("OK")
end
+
+ def _tl_skip
+ @queue.clear
+ __current_drop
+ end
+
+ def tl_handler(io, msg)
+ case msg.shift
+ when "add"
+ path = msg.shift
+ after_track_id = msg.shift
+ after_track_id = after_track_id.to_i if after_track_id
+ case set_as_current = msg.shift
+ when "true" then set_as_current = true
+ when "false", nil then set_as_current = false
+ else
+ return io.emit("ERR tl add PATH [after_track_id] [true|false]")
+ end
+ begin
+ @tl.add_track(path, after_track_id, set_as_current)
+ rescue ArgumentError => e
+ return io.emit("ERR #{e.message}")
+ end
+ _tl_skip if set_as_current
+
+ # start playing if we're the only track
+ if @tl.size == 1 && !(@current || @queue[0] || @paused)
+ next_source(path)
+ end
+ io.emit("OK")
+ when "repeat"
+ case msg.shift
+ when "true" then @tl.repeat = true
+ when "false" then @tl.repeat = false
+ when nil
+ return io.emit("repeat address@hidden")
+ end
+ io.emit("OK")
+ when "remove"
+ track_id = msg.shift or return io.emit("ERR track_id not specified")
+ track_id = track_id.to_i
+ cur = @tl.cur_track
+
+ # skip if we're removing the currently playing track
+ if cur.object_id == track_id && @current &&
+ @current.respond_to?(:infile) && @current.infile == cur
+ _tl_skip
+ end
+
+ io.emit(@tl.remove_track(track_id) ? "OK" : "MISSING")
+ when "get"
+ res = @tl.get_tracks(msg.map! { |i| i.to_i })
+ res.map! { |tid, file| "#{tid}=#{file ? Shellwords.escape(file) : ''}" }
+ io.emit(res.join(' '))
+ when "tracks"
+ io.emit(@tl.tracks.map! { |i| i.to_s }.join(' '))
+ when "goto"
+ track_id = msg.shift or return io.emit("ERR track_id not specified")
+ if @tl.go_to(track_id.to_i)
+ _tl_skip
+ io.emit("OK")
+ else
+ io.emit("MISSING")
+ end
+ when "current"
+ path = @tl.cur_track
+ io.emit(path ? path : "NONE")
+ when "current-id"
+ path = @tl.cur_track
+ io.emit(path ? path.object_id.to_s : "NONE")
+ end
+ end
end
# :startdoc:
--
1.8.3.2.701.g8c4e4ec