dtas-all
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]