about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2015-12-14 02:15:28 +0000
committerEric Wong <e@80x24.org>2015-12-14 02:55:22 +0000
commit93f9583bb32dda6f6c003ef0487053974f167b27 (patch)
treece55dd088d1266ad97a796d5f1131a13386291ae
parentd9eacbda3c5bc3b6d3a7c15f8c47a0657ef7e683 (diff)
downloaddtas-93f9583bb32dda6f6c003ef0487053974f167b27.tar.gz
This should allow convenient rearranging and deleting of tracks from
the tracklist from your favorite $EDITOR.
-rw-r--r--Documentation/dtas-tl.txt16
-rwxr-xr-xbin/dtas-tl82
2 files changed, 97 insertions, 1 deletions
diff --git a/Documentation/dtas-tl.txt b/Documentation/dtas-tl.txt
index 41d039e..51c8bc1 100644
--- a/Documentation/dtas-tl.txt
+++ b/Documentation/dtas-tl.txt
@@ -26,6 +26,8 @@ client).
 * cat - display a newline-delimited list of TRACK_ID=PATH output
   Note: this is shell-escaped, multibyte characters may not show up properly.
 * clear - remove all tracks from the tracklist
+* edit - spawn an editor to allow editing the tracklist as a text file
+  See EDITING for more information.
 * goto TRACKID [POS] - play TRACKID immediately, optionally seek to POS
 TRACKID may be looked up via cat, and POS should be a timestamp in
 HH:MM:SS.FRAC format.
@@ -57,8 +59,22 @@ to skip backwards in the tracklist
 
         $ dtas-tl prev
 
+# EDITING
+
+It is possible to edit the player tracklist from your favorite text
+editor.  Existing lines denoting tracks may be rearranged, deleted,
+copied or entirely new lines with path names (without a trailing "
+=TRACK_ID") may be added.
+
+Changes to the tracklist are sent to the player when the user exits the
+editor.  This functionality only works when there is one user editing
+the tracklist at a time, and when no files in the tracklist contain rare
+newline characters.
+
 # ENVIRONMENT
 
+VISUAL / EDITOR - your favorite *nix text editor, defaults to 'vi' if unset.
+
 DTAS_PLAYER_SOCK - the path to the dtas-player control socket.
 This defaults to ~/.dtas/player.sock
 
diff --git a/bin/dtas-tl b/bin/dtas-tl
index 3d1d946..fd5ced9 100755
--- a/bin/dtas-tl
+++ b/bin/dtas-tl
@@ -5,7 +5,6 @@
 # WARNING: totally unstable API, use dtas-ctl for scripting (but the protocol
 # itself is also unstable, but better than this one probably).
 require 'dtas/unix_client'
-require 'yaml'
 require 'shellwords'
 
 def get_track_ids(c)
@@ -16,6 +15,86 @@ def get_track_ids(c)
   track_ids
 end
 
+def do_edit(c)
+  require 'dtas/edit_client'
+  require 'yaml'
+  require 'tempfile'
+  extend DTAS::EditClient
+  tmp = Tempfile.new(%w(dtas-tl-edit .txt))
+  tmp.binmode
+  tmp_path = tmp.path
+  orig = []
+  orig_idx = {}
+
+  get_track_ids(c).each_slice(128) do |track_ids|
+    res = c.req("tl get #{track_ids.join(' ')}")
+    res = Shellwords.split(res.sub!(/\A\d+ /, ''))
+    while line = res.shift
+      line.sub!(/\A(\d+)=/, '') or abort "unexpected line=#{line.inspect}\n"
+      track_id = $1.to_i
+      orig_idx[track_id] = orig.size
+      orig << track_id
+      tmp.write("#{Shellwords.escape(line)} =#{track_id}\n")
+    end
+  end
+  tmp.flush
+
+  ed = editor
+  # jump to the line of the currently playing track if using vi or vim
+  # Patches for other editors welcome: dtas-all@nongnu.org
+  if ed =~ /vim?\z/
+    cur = YAML.load(c.req('current'))
+    if tl = cur['tracklist']
+      if pos = tl['pos']
+        ed += " +#{pos + 1}"
+      end
+    end
+  end
+  # Run the editor and let the user edit!
+  system("#{ed} #{Shellwords.escape tmp_path}") or return
+
+  edit = []
+  edit_idx = {}
+  add = []
+  # editor may rename/link a new file into place
+  File.open(tmp_path) do |fp|
+    while line = fp.gets
+      line.chomp!
+      if line.sub!(/ =(\d+)\z/, '') # existing tracks
+        track_id = $1.to_i
+        if edit_idx[track_id] # somebody copy+pasted an existing line
+          add << [ File.expand_path(line), edit.last ]
+        else # moved line
+          edit_idx[track_id] = edit.size
+          edit << track_id
+        end
+      else # entirely new line
+        add << [ File.expand_path(line), edit.last ]
+      end
+    end
+  end
+  edit.each_with_index do |track_id, i|
+    oi = orig_idx[track_id] or warn("unknown track_id=#{track_id}") or next
+    next if oi == i # no change, yay!
+    prev_track_id = orig[i] or warn("unknown index at #{i}") or next
+    c.req("tl swap #{track_id} #{prev_track_id}")
+    orig_idx[track_id] = i
+    orig_idx[prev_track_id] = oi
+    orig[i] = track_id
+    orig[oi] = prev_track_id
+  end
+  orig.each do |track_id|
+    edit_idx[track_id] or c.req("tl remove #{track_id}")
+  end
+  add.each do |path, after_id|
+    cmd = %W(tl add #{path})
+    cmd << after_id.to_s if after_id
+    c.req(cmd)
+  end
+ensure
+  tmp.close! if tmp
+end
+
 c = DTAS::UNIXClient.new
 case cmd = ARGV[0]
 when "cat"
@@ -63,6 +142,7 @@ when "reto"
   end
   warn "#{re.inspect} not found"
   exit 1
+when 'edit' then do_edit(c)
 else
   # act like dtas-ctl for now...
   puts c.req([ "tl", *ARGV ])