[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 1/2] tracklist: preliminary tracklist class
From: |
Eric Wong |
Subject: |
[PATCH 1/2] tracklist: preliminary tracklist class |
Date: |
Mon, 9 Sep 2013 04:12:28 +0000 |
This should allow us to eventually implement a MPRIS 2.0-compliant
tracklist.
---
lib/dtas/tracklist.rb | 108 +++++++++++++++++++++++++++++++++++++++++++++++++
test/test_tracklist.rb | 75 ++++++++++++++++++++++++++++++++++
2 files changed, 183 insertions(+)
create mode 100644 lib/dtas/tracklist.rb
create mode 100644 test/test_tracklist.rb
diff --git a/lib/dtas/tracklist.rb b/lib/dtas/tracklist.rb
new file mode 100644
index 0000000..2c4ad03
--- /dev/null
+++ b/lib/dtas/tracklist.rb
@@ -0,0 +1,108 @@
+# Copyright (C) 2013, Eric Wong <address@hidden> and all contributors
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative '../dtas'
+require_relative 'serialize'
+
+# this is inspired by the MPRIS 2.0 TrackList spec
+class DTAS::Tracklist
+ include DTAS::Serialize
+ attr_accessor :repeat
+
+ SIVS = %w(list pos repeat)
+ TL_DEFAULTS = {
+ "list" => [],
+ "pos" => -1,
+ "repeat" => false,
+ }
+
+ def self.load(hash)
+ obj = new
+ obj.instance_eval do
+ list = hash["list"] and @list.replace(list)
+ @pos = hash["pos"] || -1
+ @repeat = hash["repeat"] || false
+ end
+ obj
+ end
+
+ def to_hsh
+ ivars_to_hash(SIVS).delete_if { |k,v| TL_DEFAULTS[k] == v }
+ end
+
+ def initialize
+ TL_DEFAULTS.each { |k,v| instance_variable_set("@#{k}", v) }
+ @list = []
+ end
+
+ def size
+ @list.size
+ end
+
+ # caching this probably isn't worth it. a tracklist is usually
+ # a few tens of tracks, maybe a hundred at most.
+ def _track_id_map
+ by_track_id = {}
+ @list.each_with_index { |t,i| by_track_id[t.object_id] = i }
+ by_track_id
+ end
+
+ def get_tracks(track_ids)
+ by_track_id = _track_id_map
+ track_ids.map do |track_id|
+ idx = by_track_id[track_id]
+ # dtas-mpris fills in the metadata, we just return a path
+ [ track_id, idx ? @list[idx] : nil ]
+ end
+ end
+
+ def tracks
+ @list.map { |t| t.object_id }
+ end
+
+ def next_track(repeat_ok = true)
+ return if @list.empty?
+ next_pos = @pos + 1
+ if @list[next_pos]
+ @pos = next_pos
+ elsif @repeat && repeat_ok
+ next_pos = @pos = 0
+ else
+ return
+ end
+ @list[next_pos]
+ end
+
+ def cur_track
+ @pos >= 0 ? @address@hidden : nil
+ end
+
+ def add_track(track, after_track_id = nil, set_as_current = false)
+ if after_track_id
+ by_track_id = _track_id_map
+ idx = by_track_id[after_track_id] or
+ raise ArgumentError, "after_track_id invalid"
+ @list[idx, 1] = [ @list[idx], track ]
+ @pos = idx + 1 if set_as_current
+ else # nil = first_track
+ @list.unshift(track)
+ @pos = 0 if set_as_current
+ end
+ end
+
+ def remove_track(track_id)
+ by_track_id = _track_id_map
+ if idx = by_track_id.delete(track_id)
+ @list[idx] = nil
+ @list.compact!
+ # TODO: what do we do with @pos (and the currently-playing track)
+ end
+ end
+
+ def go_to(track_id)
+ by_track_id = _track_id_map
+ if idx = by_track_id[track_id]
+ return @address@hidden = idx]
+ end
+ # noop if track_id is invalid
+ end
+end
diff --git a/test/test_tracklist.rb b/test/test_tracklist.rb
new file mode 100644
index 0000000..cc462c5
--- /dev/null
+++ b/test/test_tracklist.rb
@@ -0,0 +1,75 @@
+# Copyright (C) 2013, Eric Wong <address@hidden> and all contributors
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative 'helper'
+require 'dtas/tracklist'
+class TestTracklist < Testcase
+ def test_tl_add_tracks
+ tl = DTAS::Tracklist.new
+ tl.add_track("/foo.flac")
+ assert_equal(%w(/foo.flac), tl.instance_variable_get(:@list))
+
+ oids = tl.tracks
+ assert_kind_of Array, oids
+ assert_equal 1, oids.size
+ assert_equal [ [ oids[0], "/foo.flac" ] ], tl.get_tracks(oids)
+
+ tl.add_track("/bar.flac")
+ assert_equal(%w(/bar.flac /foo.flac), tl.instance_variable_get(:@list))
+
+ tl.add_track("/after.flac", oids[0])
+ assert_equal(%w(/bar.flac /foo.flac /after.flac),
+ tl.instance_variable_get(:@list))
+ end
+
+ def test_add_current
+ tl = DTAS::Tracklist.new
+ tl.instance_variable_get(:@list).replace(%w(a b c d e f g))
+ tl.add_track('/foo.flac', nil, true)
+ assert_equal '/foo.flac', tl.cur_track
+ end
+
+ def test_next_track
+ tl = DTAS::Tracklist.new
+ tl.instance_variable_get(:@list).replace(%w(a b c d e f g))
+ %w(a b c d e f g).each do |t|
+ assert_equal t, tl.next_track
+ end
+ assert_nil tl.next_track
+ tl.repeat = true
+ assert_equal 'a', tl.next_track
+ end
+
+ def _build_mapping(tl)
+ tracks = tl.get_tracks(tl.tracks)
+ Hash[tracks.map { |(oid,name)| [ name, oid ] }]
+ end
+
+ def test_goto
+ tl = DTAS::Tracklist.new
+ tl.instance_variable_get(:@list).replace(%w(a b c d e f g))
+ mapping = _build_mapping(tl)
+ assert_equal 'f', tl.go_to(mapping['f'])
+ assert_nil tl.go_to(1 << 128)
+ assert_equal 'g', tl.next_track
+ end
+
+ def test_remove_track
+ tl = DTAS::Tracklist.new
+ tl.instance_variable_get(:@list).replace(%w(a b c d e f g))
+ mapping = _build_mapping(tl)
+ %w(a b c d e f g).each { |t| assert_kind_of Integer, mapping[t] }
+
+ tl.remove_track(mapping['a'])
+ assert_equal %w(b c d e f g), tl.instance_variable_get(:@list)
+
+ tl.remove_track(mapping['d'])
+ assert_equal %w(b c e f g), tl.instance_variable_get(:@list)
+
+ tl.remove_track(mapping['g'])
+ assert_equal %w(b c e f), tl.instance_variable_get(:@list)
+
+ # it'll be a while before OIDs require >128 bits, right?
+ tl.remove_track(1 << 128)
+ assert_equal %w(b c e f), tl.instance_variable_get(:@list), "no change"
+ end
+end
--
1.8.3.2.701.g8c4e4ec
- [PATCH 1/2] tracklist: preliminary tracklist class,
Eric Wong <=