about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--Documentation/dtas-player_protocol.txt8
-rw-r--r--lib/dtas/player.rb2
-rw-r--r--lib/dtas/player/client_handler.rb21
-rw-r--r--lib/dtas/state_file.rb2
-rw-r--r--test/test_player_integration.rb18
5 files changed, 51 insertions, 0 deletions
diff --git a/Documentation/dtas-player_protocol.txt b/Documentation/dtas-player_protocol.txt
index efbd065..b431180 100644
--- a/Documentation/dtas-player_protocol.txt
+++ b/Documentation/dtas-player_protocol.txt
@@ -203,6 +203,14 @@ Commands here should be alphabetized according to `LC_ALL=C sort'
         1. input type (flac/opus/mp3/etc)
         2. transport protocol (local FS/http/ftp/sftp/etc)
 
+* state dump [FILENAME]
+  Immediately dump the state of the player.  If a FILENAME is specified,
+  the state is written to that file.  Otherwise, the default state file
+  (configured via DTAS_PLAYER_STATE environment variable, defaulting
+  to ~/.dtas/player_state.yml) is written to.  This does not use fsync(2),
+  users requiring fsync should open(2) that file and fsync(2) it
+  themselves if necessary.
+
 * watch - adds the client to the passive watch list for notifications.
   It is recommended clients issue no further commands and open
   another client socket to issue non-watch commands.
diff --git a/lib/dtas/player.rb b/lib/dtas/player.rb
index ba42490..3571a7f 100644
--- a/lib/dtas/player.rb
+++ b/lib/dtas/player.rb
@@ -212,6 +212,8 @@ class DTAS::Player # :nodoc:
       io.emit("OK")
     when "source"
       source_handler(io, msg)
+    when "state"
+      state_file_handler(io, msg)
     when "cd"
       chdir_handler(io, msg)
     when "pwd"
diff --git a/lib/dtas/player/client_handler.rb b/lib/dtas/player/client_handler.rb
index f601d46..bbdc1c5 100644
--- a/lib/dtas/player/client_handler.rb
+++ b/lib/dtas/player/client_handler.rb
@@ -479,5 +479,26 @@ module DTAS::Player::ClientHandler # :nodoc:
     # echo(%W(cd msg[0])) # should we broadcast this?
     io.emit("OK")
   end
+
+  def state_file_handler(io, msg)
+    case msg.shift
+    when "dump"
+      dest = msg.shift
+      if dest
+        sf = DTAS::StateFile.new(dest, false)
+      elsif @state_file
+        sf = @state_file
+        dest = sf.path
+      else
+        return io.emit("ERR no state file configured")
+      end
+      begin
+        sf.dump(self)
+      rescue => e
+        return io.emit("ERR dumping to #{xs(Array(dest))} #{e.message}")
+      end
+    end
+    io.emit("OK")
+  end
 end
 # :startdoc:
diff --git a/lib/dtas/state_file.rb b/lib/dtas/state_file.rb
index c009797..0ba876f 100644
--- a/lib/dtas/state_file.rb
+++ b/lib/dtas/state_file.rb
@@ -3,6 +3,8 @@
 require 'yaml'
 require 'tempfile'
 class DTAS::StateFile # :nodoc:
+  attr_reader :path
+
   def initialize(path, do_fsync = false)
     @path = path
     @do_fsync = do_fsync
diff --git a/test/test_player_integration.rb b/test/test_player_integration.rb
index b60ed36..e7f7306 100644
--- a/test/test_player_integration.rb
+++ b/test/test_player_integration.rb
@@ -188,6 +188,24 @@ class TestPlayerIntegration < Minitest::Unit::TestCase
     assert_equal "/", s.req("pwd")
   end
 
+  def test_state_file
+    state = Tempfile.new(%w(state .yml))
+    state_path = state.path
+    state.close!
+    s = client_socket
+    s.req_ok(%W(state dump #{state_path}))
+    hash = YAML.load(IO.binread(state_path))
+    assert_equal @sock_path, hash["socket"]
+    assert_equal "default", hash["sinks"][0]["name"]
+
+    assert_equal "", IO.binread(@state_tmp.path)
+    s.req_ok(%W(state dump))
+    orig = YAML.load(IO.binread(@state_tmp.path))
+    assert_equal orig, hash
+  ensure
+    File.unlink(state_path)
+  end
+
   def test_source_ed
     s = client_socket
     assert_equal "sox av ff", s.req("source ls")