ektoplayer 0.1.16 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1567475105c5b1d0e3a099a4324ca19cdd67efe8
4
- data.tar.gz: e48caa107e5f934866a720023a5cb86fbb9153a9
3
+ metadata.gz: 5e5ff8d4fa934d30ecd7374ab072cc6a4fe76fdc
4
+ data.tar.gz: 598356fac5e4260e419a967afb96d960c29f05d9
5
5
  SHA512:
6
- metadata.gz: f5b706485a36e6398a981314c93f244ad4c85e35afbe55cce4e2d211752059abd3cfe743316d5edc85011f01002ecf35b5895e0aab54551bff565e407b1e7db0
7
- data.tar.gz: 12636fee134acc3f234ddbb6f7637e6a2ca0186998d76929820eb1f5a36c347ed5ec6c86aab8f128be13cf7240b9d01ad8e68c68b3dd15ac8347c783cb765ead
6
+ metadata.gz: 8deb97e80245b3dcbb3fcdf9847b213781ed7148b10a55ea1e89cbe931bfbc0c2087adc03d21fc42f3d3797bd15efa0c6cc379a02f82a7bdaaa55a7f4ed66f5d
7
+ data.tar.gz: 96dec00f1913908bca31b7917889f298caf3480a224e006b93f55d6648e78848de26870a5539ac9e84bd55b711b287d88fd8f16171478e743de40baf84a4d371
@@ -10,7 +10,7 @@ require 'date'
10
10
 
11
11
  module Ektoplayer
12
12
  class Application
13
- VERSION = '0.1.16'.freeze
13
+ VERSION = '0.1.17'.freeze
14
14
  GITHUB_URL = 'https://github.com/braph/ektoplayer'.freeze
15
15
  EKTOPLAZM_URL = 'http://www.ektoplazm.com'.freeze
16
16
 
@@ -186,7 +186,10 @@ module Ektoplayer
186
186
  end
187
187
 
188
188
  if (n = Config[:playlist_load_newest]) > 0
189
- r = client.database.select(order_by: 'date desc,album,number', limit: n)
189
+ r = client.database.select(
190
+ order_by: 'date DESC,album,number',
191
+ limit: n
192
+ )
190
193
  playlist.add(*r)
191
194
  end
192
195
 
@@ -3,6 +3,12 @@ require_relative 'events'
3
3
 
4
4
  module Ektoplayer
5
5
  class Database
6
+ SELECT_DESCRIPTION = %q[
7
+ SELECT description
8
+ FROM albums
9
+ WHERE url = ?
10
+ ].squeeze(' ').freeze
11
+
6
12
  SELECT_ARCHIVES = %q[
7
13
  SELECT archive_url, archive_type
8
14
  FROM archive_urls
@@ -141,7 +147,7 @@ module Ektoplayer
141
147
  end
142
148
 
143
149
  def select(
144
- columns: ?*,
150
+ columns: 'number,artist,album,title,styles,date,year,rating,votes,download_count,bpm,album_url,url',
145
151
  filters: [],
146
152
  group_by: 'url',
147
153
  order_by: 'album,number',
@@ -187,6 +193,10 @@ module Ektoplayer
187
193
  execute(SELECT_ARCHIVES, [url])
188
194
  end
189
195
 
196
+ def get_description(album_url)
197
+ @db.get_first_value(SELECT_DESCRIPTION, [album_url])
198
+ end
199
+
190
200
  def track_count
191
201
  @db.get_first_value('SELECT COUNT(*) FROM tracks')
192
202
  end
@@ -39,7 +39,7 @@ module Ncurses
39
39
  end
40
40
 
41
41
  if $USING_CURSES == 'ncurses'
42
- ### FIX: 'attrset' in ncurses is broken!
42
+ ### FIX: 'attrset' in ncurses seems broken?
43
43
  def attrset(attributes)
44
44
  Ncurses.send(:wattr_get, @w, old_a=[], old_c=[], nil)
45
45
  Ncurses.send(:wattroff, @w, old_a[0] | old_c[0])
@@ -62,6 +62,11 @@ module ICurses
62
62
  def initscr; end # do nothing
63
63
  module_function :initscr
64
64
 
65
+ def stdscr
66
+ ICurses::IWindow.new Ncurses.stdscr
67
+ end
68
+ module_function :stdscr
69
+
65
70
  def newwin(*a)
66
71
  ICurses::IWindow.new( Ncurses.newwin(*a) )
67
72
  end
@@ -2,7 +2,8 @@ module ICurses
2
2
  class IMouseEvent
3
3
  attr_accessor :x, :y, :z, :bstate
4
4
 
5
- def initialize(mouse_event=nil)
5
+ def initialize(mouse_event=nil, bstate: 0, x: 0, y: 0, z: 0)
6
+ @x, @y, @z, @bstate = x, y, z, bstate
6
7
  from_mouse_event!(mouse_event)
7
8
  end
8
9
 
@@ -12,16 +13,16 @@ module ICurses
12
13
  def from_mouse_event!(m)
13
14
  @x, @y, @z, @bstate = m.x, m.y, m.z, m.bstate
14
15
  rescue
15
- @x, @y, @z, @bstate = m[:x], m[:y], m[:z], m[:bstate]
16
- rescue
17
- @x= @y= @z= @bstate = 0
16
+ begin @x, @y, @z, @bstate = m[:x], m[:y], m[:z], m[:bstate]
17
+ rescue
18
+ end
18
19
  end
19
20
 
20
21
  def update!(x: nil, y: nil, z: nil, bstate: nil)
21
22
  @x = x if x
22
23
  @y = y if y
23
24
  @z = z if z
24
- @bstate = (bstate or @bstate)
25
+ @bstate = bstate if bstate
25
26
  end
26
27
  end
27
28
  end
@@ -36,7 +37,6 @@ module ICurses
36
37
  method_missing(meth_up, *args)
37
38
  end
38
39
  )
39
-
40
40
  module_function(meth)
41
41
  elsif not respond_to? meth_up
42
42
  alias_method(meth_up, meth) rescue (
@@ -120,9 +120,7 @@ module Ektoplayer
120
120
  end
121
121
 
122
122
  def tracks(index)
123
- @database.select(
124
- order_by: 'album,' + CONTENTS[index].to_s + ",album,number"
125
- )
123
+ @database.select(order_by: 'album,' + CONTENTS[index].to_s + ',album,number')
126
124
  end
127
125
  end
128
126
  end
@@ -21,6 +21,10 @@ module Ektoplayer
21
21
 
22
22
  def track_count; @client.database.track_count end
23
23
  def album_count; @client.database.album_count end
24
+
25
+ def get_description(*a)
26
+ @client.database.get_description(*a)
27
+ end
24
28
 
25
29
  def updating?
26
30
  @update_thread and @update_thread.alive?
@@ -133,7 +133,7 @@ class MpgWrapperPlayer
133
133
  Thread.new do
134
134
  begin
135
135
  @mpg123_in, @mpg123_out, mpg123_err, @mpg123_thread =
136
- Open3.popen3('mpg123', '-o', 'jack,pulse,alsa,oss', '--fuzzy', '-b', '1024', '-R')
136
+ Open3.popen3('mpg123', '-o', 'alsa,jack,pulse,oss', '--fuzzy', '-R')
137
137
 
138
138
  while (line = @mpg123_out.readline)
139
139
  cmd, line = line.split(' ', 2)
@@ -0,0 +1,42 @@
1
+ require_relative '../icurses'
2
+
3
+ module UI
4
+ class EscapeSequenceTranslator
5
+ KEYS = {
6
+ ?\r => ICurses::KEY_ENTER,
7
+ ?\r.ord => ICurses::KEY_ENTER
8
+ }
9
+
10
+ def self.reg(key, fallback, curses_key)
11
+ code = `tput #{key} 2>/dev/null` rescue ''
12
+ code = fallback if code.empty?
13
+ KEYS[code] = curses_key if not code.empty?
14
+ end
15
+
16
+ self.reg('kcuu1', "\033[A", ICurses::KEY_UP)
17
+ self.reg('kcud1', "\033[B", ICurses::KEY_DOWN)
18
+ self.reg('kcuf1', "\033[C", ICurses::KEY_RIGHT)
19
+ self.reg('kcub1', "\033[D", ICurses::KEY_LEFT)
20
+ self.reg('khome', "\033[1~", ICurses::KEY_HOME)
21
+ self.reg('kich1', "\033[2~", ICurses::KEY_IC)
22
+ self.reg('kdch1', "\033[3~", ICurses::KEY_DC)
23
+ self.reg('kend', "\033[4~", ICurses::KEY_END)
24
+ self.reg('kpp', "\033[5~", ICurses::KEY_PPAGE)
25
+ self.reg('knp', "\033[6~", ICurses::KEY_NPAGE)
26
+ (0..60).each { |i| self.reg("kf#{i}", '', ICurses.const_get("KEY_F#{i}")) }
27
+
28
+ KEYS.freeze
29
+
30
+ def self.to_curses(key)
31
+ if KEYS.include? key
32
+ KEYS[key]
33
+ elsif key.is_a? Integer
34
+ key
35
+ elsif ((key.is_a? String or key.is_a? Array) and key[0] == 27.chr and key[1] == ?[ and key[2] == ?M)
36
+ ICurses::IMouseEvent.new(bstate: ICurses::BUTTON1_CLICKED, x: key[4].ord - 33, y: key[5].ord - 33)
37
+ elsif key.size == 1
38
+ key.ord rescue nil
39
+ end
40
+ end
41
+ end
42
+ end
@@ -191,7 +191,7 @@ module UI
191
191
 
192
192
  write_at(old_cursor); render(old_selected)
193
193
  write_at(new_cursor); render(@selected, selected: true)
194
- _check
194
+ #_check
195
195
  want_refresh
196
196
  elsif (new_cursor.between?(-(@size.height - 1), (2 * @size.height - 1)))
197
197
  # new selected item is max a half screen size away
@@ -206,7 +206,7 @@ module UI
206
206
 
207
207
  @win.insert_top; render(@selected, selected: true)
208
208
  @cursor = 0
209
- _check
209
+ #_check
210
210
  else
211
211
  if lines_before_cursor > (@selected - old_selected)
212
212
  write_at(old_cursor); render(old_selected)
@@ -218,14 +218,14 @@ module UI
218
218
 
219
219
  @win.append_bottom; render(@selected, selected: true)
220
220
  @cursor = cursor_max
221
- _check
221
+ #_check
222
222
  end
223
223
 
224
224
  want_refresh
225
225
  else
226
226
  #@selected = new_index
227
227
  @cursor = new_index.clamp(0, cursor_max) # todo new_index<>new_cursor? ne muess scho pasn
228
- _check
228
+ #_check
229
229
  want_redraw
230
230
  end
231
231
 
@@ -242,7 +242,7 @@ module UI
242
242
  with_lock do
243
243
  old_cursor, @cursor = @cursor, new_cursor
244
244
  old_selected, @selected = @selected, (@selected - (old_cursor - @cursor)).clamp(0, index_last)
245
- _check
245
+ #_check
246
246
 
247
247
  if @selection.started?
248
248
  want_redraw
@@ -290,12 +290,12 @@ module UI
290
290
 
291
291
  write_at(@cursor); render(@selected, selected: true)
292
292
 
293
- _check
293
+ #_check
294
294
  want_refresh
295
295
  else
296
296
  @selected -= n # TODO: move up?
297
297
  force_cursorpos(@cursor)
298
- _check # todo: move up
298
+ #_check # todo: move up
299
299
  want_redraw
300
300
  end
301
301
 
@@ -310,7 +310,7 @@ module UI
310
310
 
311
311
  if index_bottom == index_last
312
312
  select_from_cursorpos((@cursor + n).clamp(0, cursor_max))
313
- _check
313
+ #_check
314
314
  elsif n < @size.height
315
315
  old_index_bottom = index_bottom
316
316
  old_selected, @selected = @selected, @selected + n
@@ -325,24 +325,24 @@ module UI
325
325
 
326
326
  write_at(@cursor); render(@selected, selected: true)
327
327
 
328
- _check
328
+ #_check
329
329
  want_refresh
330
330
  else
331
331
  @selected += n
332
332
  force_cursorpos(@cursor)
333
- _check
333
+ #_check
334
334
  want_redraw
335
335
  end
336
336
 
337
337
  self.unlock
338
- _check
338
+ #_check
339
339
  end
340
340
 
341
341
  def draw
342
342
  @win.erase
343
343
  return if @list.empty?
344
344
  @selected = @selected.clamp(0, index_last)
345
- _check
345
+ #_check
346
346
 
347
347
  @cursor.times do |i|
348
348
  unless row = @list[@selected - (@cursor - i)]
@@ -353,7 +353,7 @@ module UI
353
353
  write_at(i); render(@selected - (@cursor - i))
354
354
  end
355
355
 
356
- _check
356
+ #_check
357
357
  write_at(@cursor); render(@selected, selected: true)
358
358
 
359
359
  (@cursor + 1).upto(@size.height - 1).each_with_index do |c, i|
@@ -361,7 +361,7 @@ module UI
361
361
  write_at(c); render(@selected + i + 1)
362
362
  end
363
363
 
364
- _check
364
+ #_check
365
365
  end
366
366
 
367
367
  def on_mouse_click(mevent, mevent_transformed)
@@ -373,7 +373,7 @@ module UI
373
373
 
374
374
  protected
375
375
 
376
- def write_at(pos) @win.line_start(pos).clrtoeol end
376
+ def write_at(pos) @win.move(pos, 0) end
377
377
 
378
378
  def index_first; 0 end
379
379
  def index_last; [@list.size, 1].max - 1 end
@@ -48,7 +48,7 @@ module UI
48
48
  layout;
49
49
  @want ^= WANT_LAYOUT
50
50
  end
51
- return if not visible?
51
+ return if not visible?
52
52
 
53
53
  if @want & WANT_REDRAW > 0
54
54
  draw
@@ -66,7 +66,7 @@ module UI
66
66
  layout;
67
67
  @want ^= WANT_LAYOUT
68
68
  end
69
- return if not visible?
69
+ return if not visible?
70
70
 
71
71
  if @want & WANT_REDRAW > 0 or force_redraw
72
72
  draw
@@ -120,7 +120,7 @@ module UI
120
120
  def mouse_click(mevent)
121
121
  if new_event = mouse_event_transform(mevent)
122
122
  trigger(@mouse, new_event)
123
- trigger(@mouse_event, new_event)
123
+ trigger(@mouse_section, new_event)
124
124
  end
125
125
  end
126
126
 
data/lib/ektoplayer/ui.rb CHANGED
@@ -1,35 +1,24 @@
1
- require_relative 'icurses'
2
1
  require 'readline'
3
2
  require 'io/console'
4
3
 
4
+ require_relative 'icurses'
5
5
  require_relative 'ui/colors'
6
+ require_relative 'ui/escapesequencetranslator'
6
7
  require_relative 'events'
7
8
 
8
9
  module UI
9
10
  class WidgetSizeError < Exception; end
10
11
 
11
12
  class Canvas
12
- extend ICurses
13
-
14
- def self.size
15
- UI::Size.new(height: ICurses.lines, width: ICurses.cols)
16
- end
17
-
18
- def self.cursor
19
- #UI::Point.new(y: ICurses.cury, x: ICurses.curx)
20
- end
21
-
22
- def self.pos
23
- UI::Point.new
24
- end
25
-
26
13
  def self.start
27
14
  @@widget = nil
15
+ @@want_resize = false
16
+ @@updating = Mutex.new
28
17
 
29
18
  %w(initscr cbreak noecho start_color use_default_colors).
30
19
  each(&ICurses.method(:send))
31
20
  ICurses.mousemask(ICurses::ALL_MOUSE_EVENTS | ICurses::REPORT_MOUSE_POSITION)
32
- ICurses.stdscr.keypad(true)
21
+ ICurses.stdscr.getch1(1) # first getch() clears screen?
33
22
  UI::Colors.start
34
23
 
35
24
  self.enable_resize_detection
@@ -39,21 +28,19 @@ module UI
39
28
  Signal.trap('WINCH') { @@want_resize = true }
40
29
  end
41
30
 
42
- def self.widget; @@widget end
43
- def self.widget=(w) @@widget = w end
44
- def self.stop; ICurses.endwin end
45
- def self.visible?; true end
46
- def self.inivsibile?; false end
31
+ def self.widget; @@widget end
32
+ def self.widget=(w) @@widget = w end
33
+ def self.stop; ICurses.endwin end
47
34
 
48
35
  def self.sub(cls, **opts)
49
- @@widget ||= (widget = cls.new(parent: self, **opts))
36
+ opts[:size] ||= UI::Size.new(height: ICurses.lines, width: ICurses.cols)
37
+ opts[:pos] ||= UI::Point.new
38
+ widget = cls.new(**opts)
39
+ @@widget ||= widget
50
40
  widget
51
41
  end
52
42
 
53
43
  def self.update_screen(force_redraw=false, force_resize=false)
54
- @@updating ||= Mutex.new
55
- @@want_resize ||= false
56
-
57
44
  if @@updating.try_lock
58
45
  begin
59
46
  if @@want_resize or force_resize
@@ -78,7 +65,7 @@ module UI
78
65
 
79
66
  def self.run
80
67
  self.start
81
- return yield
68
+ yield
82
69
  ensure
83
70
  self.stop
84
71
  end
@@ -87,51 +74,56 @@ module UI
87
74
  class Input
88
75
  KEYMAP_WORKAROUND = {
89
76
  13 => ICurses::KEY_ENTER,
90
- 127 => ICurses::KEY_BACKSPACE
77
+ 127 => ICurses::KEY_BACKSPACE,
91
78
  }
92
79
  KEYMAP_WORKAROUND.default_proc = proc { |h,k| k }
93
80
  KEYMAP_WORKAROUND.freeze
94
81
 
95
82
  def self.start_loop
96
83
  @@readline_obj ||= ReadlineWindow.new
97
- inactive = 0
98
84
 
99
85
  loop do
100
86
  unless @@readline_obj.active?
87
+ ICurses.stdscr.keypad(true)
101
88
  ICurses.curs_set(0)
102
89
  ICurses.nonl
103
90
 
104
- begin
105
- UI::Canvas.widget.win.keypad(true)
91
+ until @@readline_obj.active?
92
+ c = EscapeSequenceTranslator.to_curses(s = STDIN.readpartial(10))
93
+ #Ektoplayer::Application.log(self, s)
106
94
 
107
- if (c = (UI::Canvas.widget.win.getch1(500).ord rescue -1)) > -1
108
- inactive = 0
95
+ if c.is_a? ICurses::IMouseEvent
96
+ UI::Canvas.widget.mouse_click(c)
97
+ elsif c
98
+ UI::Canvas.widget.key_press(c)
99
+ end
109
100
 
110
- if c == ICurses::KEY_MOUSE
111
- if c = ICurses.getmouse
112
- UI::Canvas.widget.mouse_click(c)
101
+ inactivity_count = 0
102
+ until @@readline_obj.active? or inactivity_count > 10
103
+ if (c = (ICurses.stdscr.getch1(1000).ord rescue -1)) > -1
104
+ inactivity_count = 0
105
+
106
+ if c == ICurses::KEY_MOUSE
107
+ if (c = ICurses.getmouse)
108
+ UI::Canvas.widget.mouse_click(c)
109
+ end
110
+ else
111
+ UI::Canvas.widget.key_press(KEYMAP_WORKAROUND[c])
113
112
  end
114
113
  else
115
- UI::Canvas.widget.key_press(KEYMAP_WORKAROUND[c])
116
- end
117
- else
118
- if (inactive += 1) > 40
119
- s = STDIN.readpartial(10)
120
- ICurses.ungetch(s[0].ord) if s.size == 1
114
+ inactivity_count += 1
115
+ GC.start
121
116
  end
122
117
  end
123
-
124
- ICurses.doupdate
125
- end while !@@readline_obj.active?
118
+ end
126
119
  else
120
+ ICurses.stdscr.keypad(false)
127
121
  ICurses.curs_set(1)
128
122
  ICurses.nl
129
123
 
130
124
  begin
131
- win = UI::Canvas.widget.win
132
- win.keypad(false)
133
125
  @@readline_obj.redraw
134
- next unless (c = (win.getch1(100).ord rescue -1)) > -1
126
+ next unless (c = (ICurses.stdscr.getch1(100).ord rescue -1)) > -1
135
127
 
136
128
  if c == 10 or c == 4
137
129
  @@readline_obj.feed(?\n.ord)
@@ -139,10 +131,10 @@ module UI
139
131
  @@readline_obj.feed(c)
140
132
 
141
133
  if c == 27 # pass 3-character escape sequence
142
- win.timeout(5)
143
- if (c = (win.getch.ord rescue -1)) > -1
134
+ ICurses.stdscr.timeout(5)
135
+ if (c = (ICurses.stdscr.getch.ord rescue -1)) > -1
144
136
  @@readline_obj.feed(c)
145
- if (c = (win.getch.ord rescue -1)) > -1
137
+ if (c = (ICurses.stdscr.getch.ord rescue -1)) > -1
146
138
  @@readline_obj.feed(c)
147
139
  end
148
140
  end
@@ -168,7 +160,7 @@ module UI
168
160
  @thread = nil
169
161
  end
170
162
 
171
- def active?; @thread; end
163
+ def active?; !@thread.nil? end
172
164
 
173
165
  def redraw
174
166
  @window.resize(@size.height, @size.width)
@@ -245,8 +237,8 @@ module UI
245
237
  Size.new(width: (width or @width), height: (height or @height))
246
238
  end
247
239
 
248
- def calc(height: 0, width: 0)
249
- Size.new(height: @height + height, width: @width + width)
240
+ def calc(width: 0, height: 0)
241
+ Size.new(width: @width + width, height: @height + height)
250
242
  end
251
243
 
252
244
  def ==(s) s.height == @height and s.width == @width end
@@ -300,7 +292,6 @@ module ICurses
300
292
  class IWindow
301
293
  alias :height :maxy
302
294
  alias :width :maxx
303
- alias :clear_line :clrtoeol
304
295
 
305
296
  def cursor; UI::Point.new(y: cury, x: curx) end
306
297
  def pos; UI::Point.new(y: begy, x: begx) end
@@ -331,7 +322,6 @@ module ICurses
331
322
  def on_column(n) move(cury, n) ;self;end
332
323
  def next_line; move(cury + 1, 0) ;self;end
333
324
  def mv_left(n) move(cury, curx - 1) ;self;end
334
- def line_start(l=0) move(l, 0) ;self;end
335
325
  def from_left(size) move(cury, size) ;self;end
336
326
  def from_right(size) move(cury, (maxx - size)) ;self;end
337
327
  def center(size) move(cury, (maxx / 2) - (size / 2)) ;self;end
@@ -363,11 +353,7 @@ module ICurses
363
353
  end
364
354
 
365
355
  def to_s
366
- name = ICurses.constants.
367
- select { |c| c =~ /^BUTTON_/ }.
368
- select { |c| ICurses.const_get(c) & @bstate > 0 }[0]
369
- name ||= @button
370
- "[(IMouseEvent) button=#{name}, x=#{x}, y=#{y}, z=#{z}]"
356
+ "[(IMouseEvent) bstate=#{bstate}, x=#{x}, y=#{y}, z=#{z}]"
371
357
  end
372
358
  end
373
359
  end
@@ -111,11 +111,12 @@ module Ektoplayer
111
111
 
112
112
  # -- description
113
113
  draw_heading('Description')
114
+ description = @database.get_description(@track['album_url'])
114
115
  line_length = START_TAG
115
116
  wrap_length = @size.width.clamp(1, LINE_WRAP)
116
117
  @win.move(@win.cury + 1, START_TAG)
117
118
 
118
- Nokogiri::HTML("<p>#{@track['description']}</p>").css(?p).each do |p|
119
+ Nokogiri::HTML("<p>#{description}</p>").css(?p).each do |p|
119
120
  p.children.each do |element|
120
121
  if element['href']
121
122
  if (line_length += element.text.size) > wrap_length
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ektoplayer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.16
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Abendroth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-31 00:00:00.000000000 Z
11
+ date: 2017-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -91,6 +91,7 @@ files:
91
91
  - lib/ektoplayer/trackloader.rb
92
92
  - lib/ektoplayer/ui.rb
93
93
  - lib/ektoplayer/ui/colors.rb
94
+ - lib/ektoplayer/ui/escapesequencetranslator.rb
94
95
  - lib/ektoplayer/ui/widgets.rb
95
96
  - lib/ektoplayer/ui/widgets/container.rb
96
97
  - lib/ektoplayer/ui/widgets/labelwidget.rb