ektoplayer 0.1.19 → 0.1.20

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: 9900059a06f9aa74a7cb9ba1ea0ae4454c277a5f
4
- data.tar.gz: ed88fdc3dab3eedffd42de732596ca6ff4bbfab6
3
+ metadata.gz: 25039a3262bb6332eef51524e8f210e95f9d5bca
4
+ data.tar.gz: bf810de03fbbeb0b6b78aec743fe22c1b02a79c3
5
5
  SHA512:
6
- metadata.gz: 6c53f882d896c43c7d51ecdea80e407e3211d248726dd08189fbda66ac3e6a74d285a50034f9f3bef945980b120a3fdd6f3c1db58efc66405980606059352064
7
- data.tar.gz: ad2b475b718c3b7aa5e6c6945178000ccffce51680218f0dabbbd1b006068a2878e32bc69966b24b790e77de6f3529496faea563a573d5864cdc1e414d212495
6
+ metadata.gz: 4ee4ff0aae66122c362b6b9ec918e098296b2814a33ef35dd4c6201b53863b37bac6ddf0c49dfd8f397a2f3b92866590730b8248587312fb957348082b8be20e
7
+ data.tar.gz: 58e915a5993a2f93e359d30fbfc6ce300b8dd9dd1c7b198b8bc45f46e00f5e42f5a5fe59b73a4c1870382f4d85f51093ce22d88cdfc49871ce3948eaa90fd0fd
@@ -10,7 +10,7 @@ require 'date'
10
10
 
11
11
  module Ektoplayer
12
12
  class Application
13
- VERSION = '0.1.19'.freeze
13
+ VERSION = '0.1.20'.freeze
14
14
  GITHUB_URL = 'https://github.com/braph/ektoplayer'.freeze
15
15
  EKTOPLAZM_URL = 'http://www.ektoplazm.com'.freeze
16
16
 
@@ -61,6 +61,7 @@ module Ektoplayer
61
61
 
62
62
  def run
63
63
  puts "\033]0;ektoplayer\007"
64
+ Process.setproctitle('ektoplayer')
64
65
  Thread.report_on_exception=(true) if Thread.respond_to? :report_on_exception
65
66
 
66
67
  # make each configuration object globally accessible as a singleton
@@ -151,6 +152,30 @@ module Ektoplayer
151
152
  operations.send(:'playlist.play_next') if reason == :track_completed
152
153
  end
153
154
 
155
+ # ... bindings ...
156
+ Bindings.bind_view(:global, main_w, view_ops, operations)
157
+ %w(splash playlist browser info help).each do |w|
158
+ Bindings.bind_view(w, main_w.send(w), view_ops, operations)
159
+ end
160
+
161
+ player.stop
162
+
163
+ # Preload playlist
164
+ if (n = Config[:playlist_load_newest]) > 0
165
+ r = client.database.select(
166
+ order_by: 'date DESC,album,number',
167
+ limit: n
168
+ )
169
+ playlist.add(*r)
170
+ end
171
+
172
+ # If database is empty, start an initial update
173
+ if browser.tracks(0).size < 1
174
+ operations.send(:update)
175
+ elsif (c = Config[:small_update_pages]) > 0
176
+ operations.send(:update, pages: c)
177
+ end
178
+
154
179
  if Config[:prefetch]
155
180
  Thread.new do
156
181
  current_download_track = nil
@@ -170,29 +195,6 @@ module Ektoplayer
170
195
  end
171
196
  end
172
197
 
173
- # ... bindings ...
174
- Bindings.bind_view(:global, main_w, view_ops, operations)
175
- %w(splash playlist browser info help).each do |w|
176
- Bindings.bind_view(w, main_w.send(w), view_ops, operations)
177
- end
178
-
179
- player.stop
180
-
181
- # If database is empty, start an initial update
182
- if browser.tracks(0).size < 1
183
- operations.send(:update)
184
- elsif (c = Config[:small_update_pages]) > 0
185
- operations.send(:update, pages: c)
186
- end
187
-
188
- if (n = Config[:playlist_load_newest]) > 0
189
- r = client.database.select(
190
- order_by: 'date DESC,album,number',
191
- limit: n
192
- )
193
- playlist.add(*r)
194
- end
195
-
196
198
  rescue
197
199
  Application.log(self, $!)
198
200
  end
@@ -127,10 +127,6 @@ module Ektoplayer
127
127
 
128
128
  @albums << album
129
129
  end
130
-
131
- Application.log(self, "#{src} #{@albums.size} albums found")
132
- rescue
133
- Application.log(self, src, $!)
134
130
  end
135
131
  end
136
132
  end
@@ -1,4 +1,4 @@
1
- unless Array.respond_to? :sum
1
+ unless Array.public_method_defined? :sum
2
2
  class Array
3
3
  def sum
4
4
  reduce(:+)
@@ -6,7 +6,7 @@ unless Array.respond_to? :sum
6
6
  end
7
7
  end
8
8
 
9
- unless Integer.respond_to? :clamp
9
+ unless Integer.public_method_defined? :clamp
10
10
  class Integer
11
11
  def clamp(min, max)
12
12
  return min if self < min
@@ -52,12 +52,12 @@ module Ektoplayer
52
52
  CONFIG_DIR = File.join(Dir.home, '.config', 'ektoplayer').freeze
53
53
  CONFIG_FILE = File.join(CONFIG_DIR, 'ektoplayer.rc').freeze
54
54
 
55
- DEFAULT_PLAYLIST_FORMAT = %{
56
- <number size="3" fg="magenta" />
57
- <artist rel="25" fg="blue" />
58
- <album rel="30" fg="red" />
59
- <title rel="33" fg="yellow" />
60
- <styles rel="20" fg="cyan" />
55
+ DEFAULT_PLAYLIST_FORMAT = %{\
56
+ <number size="3" fg="magenta" />\
57
+ <artist rel="25" fg="blue" />\
58
+ <album rel="30" fg="red" />\
59
+ <title rel="33" fg="yellow" />\
60
+ <styles rel="20" fg="cyan" />\
61
61
  <bpm size="3" fg="green" justify="right" />}.squeeze(' ').freeze
62
62
 
63
63
  DEFAULT_PLAYINGINFO_FORMAT_TOP =
@@ -66,12 +66,12 @@ module Ektoplayer
66
66
  DEFAULT_PLAYINGINFO_FORMAT_BOTTOM =
67
67
  '<artist bold="on" fg="blue" /><text> - </text><album bold="on" fg="red" /><text> (</text><year fg="cyan" /><text>)</text>'.freeze
68
68
 
69
- DEFAULT_PLAYLIST_FORMAT_256 = %{
70
- <number size="3" fg="97" />
71
- <artist rel="25" fg="24" />
72
- <album rel="30" fg="160" />
73
- <title rel="33" fg="178" />
74
- <styles rel="20" fg="37" />
69
+ DEFAULT_PLAYLIST_FORMAT_256 = %{\
70
+ <number size="3" fg="97" />\
71
+ <artist rel="25" fg="24" />\
72
+ <album rel="30" fg="160" />\
73
+ <title rel="33" fg="178" />\
74
+ <styles rel="20" fg="37" />\
75
75
  <bpm size="3" fg="28" justify="right" />}.squeeze(' ').freeze
76
76
 
77
77
  DEFAULT_PLAYINGINFO_FORMAT_TOP_256 =
@@ -156,7 +156,7 @@ module Ektoplayer
156
156
  }
157
157
 
158
158
  reg :threads,
159
- 'Number of donwload threads during database update',
159
+ 'Number of download threads during database update',
160
160
  20, lambda { |v| fail if Integer(v) < 1; Integer(v) }
161
161
 
162
162
  # - Playlist
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  unless ARGV[0]
4
- %w(curses ncurses ncursesw ffi_curses).each do |i|
4
+ %w(curses ncurses ncursesw ffi-curses).each do |i|
5
5
  puts i
6
6
  fail i unless system($0, i)
7
7
  end
@@ -101,12 +101,6 @@ module Ektoplayer
101
101
  defs ||= @theme[8][name] if @current >= 8
102
102
  defs ||= @theme[0][name]
103
103
 
104
- #unless defs
105
- # defs ||= @theme[256][:default] if @current == 256
106
- # defs ||= @theme[8][:default] if @current >= 8
107
- # defs ||= @theme[0][:default]
108
- #end
109
-
110
104
  UI::Colors.set(name, *defs)
111
105
  end
112
106
  end
@@ -58,27 +58,39 @@ module UI
58
58
  false
59
59
  end
60
60
 
61
- def next(*a) @direction == :up ? search_up(*a): search_down(*a) end
61
+ def next(*a) @direction == :up ? search_up(*a) : search_down(*a) end
62
62
  def prev(*a) @direction == :up ? search_down(*a) : search_up(*a) end
63
63
 
64
64
  def search_up(current_pos, source)
65
65
  start_pos = (current_pos - 1).clamp(0, source.size)
66
66
 
67
+ # search up from current pos to 0
67
68
  start_pos.downto(0).each do |i|
68
69
  return i if comp(source[i])
69
70
  end
70
71
 
71
- source.size
72
+ # restart search from bottom to start_pos
73
+ (source.size - 1).downto(start_pos).each do |i|
74
+ return i if comp(source[i])
75
+ end
76
+
77
+ nil # not found
72
78
  end
73
79
 
74
80
  def search_down(current_pos, source)
75
81
  start_pos = (current_pos + 1).clamp(0, source.size)
76
82
 
83
+ # search down from current pos
77
84
  start_pos.upto(source.size).each do |i|
78
85
  return i if comp(source[i])
79
86
  end
80
87
 
81
- 0
88
+ # restart search from top to start_pos
89
+ 0.upto(start_pos).each do |i|
90
+ return i if comp(source[i])
91
+ end
92
+
93
+ nil # not found
82
94
  end
83
95
  end
84
96
 
@@ -95,10 +107,23 @@ module UI
95
107
  @selection = ListSelector.new
96
108
  end
97
109
 
98
- def search_next; self.selected=(@search.next(@selected, @list)) end
99
- def search_prev; self.selected=(@search.prev(@selected, @list)) end
100
- def search_up; self.search_start(:up) end
101
- def search_down; self.search_start(:down) end
110
+ def search_next
111
+ if pos = @search.next(@selected, @list)
112
+ self.selected=(pos)
113
+ self.center
114
+ end
115
+ end
116
+
117
+ def search_prev
118
+ if pos = @search.prev(@selected, @list)
119
+ self.selected=(pos)
120
+ self.center
121
+ end
122
+ end
123
+
124
+ def search_up; self.search_start(:up) end
125
+ def search_down; self.search_start(:down) end
126
+
102
127
  def search_start(direction)
103
128
  UI::Input.readline(@pos, @size.update(height: 1), prompt: '> ', add_hist: true) do |result|
104
129
  if result
@@ -132,14 +157,13 @@ module UI
132
157
 
133
158
  def render(index, **opts)
134
159
  return unless @item_renderer
135
- return Ektoplayer::Application.log(self, 'render todo') unless @list[index]
160
+ return Ektoplayer::Application.log(self, index, caller) unless @list[index]
136
161
 
137
- opts[:selection] = (@selection.started? and (
138
- opts[:selected] or index.between?(
162
+ opts[:selection] = (@selection.started? and
163
+ index.between?(
139
164
  [@selection.start_pos, @selected].min,
140
165
  [@selection.start_pos, @selected].max
141
166
  )
142
- )
143
167
  )
144
168
 
145
169
  @item_renderer.render(@win, @list[index], index, **opts)
@@ -152,97 +176,135 @@ module UI
152
176
 
153
177
  def top; self.selected=(0) end
154
178
  def bottom; self.selected=(index_last) end
155
- def page_up; self.scroll_up(size.height) end
156
- def page_down; self.scroll_down(size.height) end
157
- def up(n=1) self.selected=(selected - n) end
158
- def down(n=1) self.selected=(selected + n) end
179
+ def page_up; self.scroll_list_up(@size.height) end
180
+ def page_down; self.scroll_list_down(@size.height) end
181
+ def up(n=1) self.scroll_cursor_up(1) end
182
+ def down(n=1) self.scroll_cursor_down(1) end
159
183
  def center; self.force_cursorpos(@size.height / 2) end
160
184
 
161
185
  def list=(list)
162
186
  with_lock do
163
187
  @list = list
164
-
165
188
  @cursor = @selected = 0
166
189
  self.selected=(0)
167
190
  self.force_cursorpos(0)
168
-
169
191
  want_redraw
170
192
  end
171
193
  end
172
194
 
173
- def selected=(new_index)
174
- fail ArgumentError unless new_index
175
- old_index_bottom = index_bottom
176
- old_index_top = index_top
177
- old_selected, @selected = @selected, new_index.clamp(0, index_last)
178
- return if old_selected == @selected or @list.empty?
195
+ def scroll_cursor_down(n)
196
+ fail ArgumentError unless n
197
+ fail ArgumentError if n.negative?
198
+ n = n.clamp(0, items_after_cursor)
199
+ return if n == 0
200
+ new_cursor, new_selected = @cursor + n, @selected + n
179
201
 
180
202
  self.lock
181
203
 
182
- old_cursor, new_cursor = @cursor, @cursor + (@selected - old_selected)
204
+ # it's faster to redraw the whole screen
205
+ if n >= @size.height
206
+ new_cursor = cursor_max
207
+ want_redraw
208
+ else
209
+ # new cursor resides in current screen
210
+ if new_cursor <= cursor_max
211
+ if @selection.started?
212
+ want_redraw
213
+ else
214
+ write_at(@cursor); render(@selected)
215
+ write_at(new_cursor); render(new_selected, selected: true)
216
+ want_refresh
217
+ end
218
+ else
219
+ new_cursor = cursor_max
183
220
 
184
- if new_cursor.between?(0, @size.height - 1)
185
- # new selected item resides in current screen,
186
- # just want_redraw the old line and the newly selected one
187
- @cursor = new_cursor
221
+ if @selection.started?
222
+ want_redraw
223
+ else
224
+ write_at(@cursor); render(@selected)
188
225
 
189
- # redraw whole screen in selection mode!
190
- return want_redraw if @selection.started?
226
+ (index_bottom + 1).upto(new_selected - 1).each do |index|
227
+ @win.append_bottom; render(index)
228
+ end
191
229
 
192
- write_at(old_cursor); render(old_selected)
193
- write_at(new_cursor); render(@selected, selected: true)
194
- #_check
195
- want_refresh
196
- elsif (new_cursor.between?(-(@size.height - 1), (2 * @size.height - 1)))
197
- # new selected item is max a half screen size away
198
- if @selected < old_selected
199
- if lines_after_cursor > (old_selected - @selected)
200
- write_at(old_cursor); render(old_selected)
201
- end
230
+ @win.append_bottom; render(new_selected, selected: true)
202
231
 
203
- (old_index_top - 1).downto(@selected + 1).each do |index|
204
- @win.insert_top; render(index)
232
+ want_refresh
205
233
  end
234
+ end
235
+ end
206
236
 
207
- @win.insert_top; render(@selected, selected: true)
208
- @cursor = 0
209
- #_check
210
- else
211
- if lines_before_cursor > (@selected - old_selected)
212
- write_at(old_cursor); render(old_selected)
213
- end
237
+ @cursor, @selected = new_cursor, new_selected
238
+ ensure
239
+ self.unlock
240
+ end
214
241
 
215
- (old_index_bottom + 1).upto(@selected - 1).each do |index|
216
- @win.append_bottom; render(index)
217
- end
242
+ def scroll_cursor_up(n)
243
+ fail ArgumentError unless n
244
+ fail ArgumentError if n.negative?
245
+ n = n.clamp(0, items_before_cursor)
246
+ return if n == 0
247
+ new_cursor, new_selected = @cursor - n, @selected - n
218
248
 
219
- @win.append_bottom; render(@selected, selected: true)
220
- @cursor = cursor_max
221
- #_check
222
- end
249
+ self.lock
223
250
 
224
- want_refresh
225
- else
226
- #@selected = new_index
227
- @cursor = new_index.clamp(0, cursor_max) # todo new_index<>new_cursor? ne muess scho pasn
228
- #_check
251
+ if n >= @size.height
252
+ new_cursor = 0
229
253
  want_redraw
254
+ else
255
+ # new cursor resides in current screen
256
+ if new_cursor >= 0
257
+ if @selection.started?
258
+ want_redraw
259
+ else
260
+ write_at(@cursor); render(@selected)
261
+ write_at(new_cursor); render(new_selected, selected: true)
262
+ want_refresh
263
+ end
264
+ else
265
+ new_cursor = 0
266
+
267
+ if @selection.started?
268
+ want_redraw
269
+ else
270
+ write_at(@cursor); render(@selected)
271
+
272
+ (index_top - 1).downto(new_selected + 1).each do |index|
273
+ @win.insert_top; render(index)
274
+ end
275
+
276
+ @win.insert_top; render(new_selected, selected: true)
277
+
278
+ want_refresh
279
+ end
280
+ end
230
281
  end
231
282
 
283
+ @cursor, @selected = new_cursor, new_selected
232
284
  ensure
233
285
  self.unlock
234
286
  end
235
287
 
288
+ def selected=(new_index)
289
+ fail ArgumentError unless new_index
290
+ fail ArgumentError.new('negative index') if new_index.negative?
291
+ new_index = new_index.clamp(0, index_last)
292
+
293
+ with_lock do
294
+ @selected = new_index
295
+ self.force_cursorpos(@cursor)
296
+ want_redraw
297
+ end
298
+ end
299
+
236
300
  # select an item by its current cursor pos
237
301
  def select_from_cursorpos(new_cursor)
238
302
  fail unless new_cursor.between?(0, cursor_max)
239
- # FIXME: clamp with @list.size ????
240
303
  return if (new_cursor == @cursor) or @list.empty?
241
304
 
242
305
  with_lock do
243
306
  old_cursor, @cursor = @cursor, new_cursor
244
307
  old_selected, @selected = @selected, (@selected - (old_cursor - @cursor)).clamp(0, index_last)
245
- #_check
246
308
 
247
309
  if @selection.started?
248
310
  want_redraw
@@ -255,19 +317,20 @@ module UI
255
317
  end
256
318
 
257
319
  def force_cursorpos(new_cursor)
258
- self.lock
259
- if @selected <= cursor_max
260
- @cursor = @selected
261
- elsif (diff = (index_last - @selected)) < cursor_max
262
- @cursor = @size.height - diff - 1 #cursor_max.clamp(0, index_last - @selected)
263
- else
264
- @cursor = new_cursor.clamp(0, cursor_max)
320
+ with_lock do
321
+ if @selected <= cursor_max / 2
322
+ @cursor = @selected
323
+ elsif (diff = (index_last - @selected)) < cursor_max / 2
324
+ @cursor = @size.height - diff - 1
325
+ else
326
+ @cursor = new_cursor.clamp(cursor_max / 2, cursor_max)
327
+ end
328
+
329
+ want_redraw
265
330
  end
266
- want_redraw
267
- self.unlock
268
331
  end
269
332
 
270
- def scroll_up(n=1)
333
+ def scroll_list_up(n=1)
271
334
  fail ArgumentError unless n
272
335
  n = n.clamp(0, items_before_cursor)
273
336
  return if n == 0 or @list.empty?
@@ -290,19 +353,17 @@ module UI
290
353
 
291
354
  write_at(@cursor); render(@selected, selected: true)
292
355
 
293
- #_check
294
356
  want_refresh
295
357
  else
296
- @selected -= n # TODO: move up?
358
+ @selected -= n
297
359
  force_cursorpos(@cursor)
298
- #_check # todo: move up
299
360
  want_redraw
300
361
  end
301
362
 
302
363
  self.unlock
303
364
  end
304
365
 
305
- def scroll_down(n=1)
366
+ def scroll_list_down(n=1)
306
367
  fail ArgumentError unless n
307
368
  n = n.clamp(0, items_after_cursor)
308
369
  return if n == 0 or @list.empty?
@@ -310,7 +371,6 @@ module UI
310
371
 
311
372
  if index_bottom == index_last
312
373
  select_from_cursorpos((@cursor + n).clamp(0, cursor_max))
313
- #_check
314
374
  elsif n < @size.height
315
375
  old_index_bottom = index_bottom
316
376
  old_selected, @selected = @selected, @selected + n
@@ -325,27 +385,23 @@ module UI
325
385
 
326
386
  write_at(@cursor); render(@selected, selected: true)
327
387
 
328
- #_check
329
388
  want_refresh
330
389
  else
331
390
  @selected += n
332
391
  force_cursorpos(@cursor)
333
- #_check
334
392
  want_redraw
335
393
  end
336
394
 
337
395
  self.unlock
338
- #_check
339
396
  end
340
397
 
341
398
  def draw
342
399
  @win.erase
343
400
  return if @list.empty?
344
- @selected = @selected.clamp(0, index_last)
345
- #_check
401
+ #@selected = @selected.clamp(0, index_last)
346
402
 
347
403
  @cursor.times do |i|
348
- unless row = @list[@selected - (@cursor - i)]
404
+ unless @list[@selected - (@cursor - i)]
349
405
  @cursor = i
350
406
  break
351
407
  end
@@ -353,15 +409,12 @@ module UI
353
409
  write_at(i); render(@selected - (@cursor - i))
354
410
  end
355
411
 
356
- #_check
357
412
  write_at(@cursor); render(@selected, selected: true)
358
413
 
359
414
  (@cursor + 1).upto(@size.height - 1).each_with_index do |c, i|
360
- break unless row = @list[@selected + i + 1]
415
+ break unless @list[@selected + i + 1]
361
416
  write_at(c); render(@selected + i + 1)
362
417
  end
363
-
364
- #_check
365
418
  end
366
419
 
367
420
  def on_mouse_click(mevent, mevent_transformed)
@@ -387,16 +440,8 @@ module UI
387
440
  def items_before_cursor; @selected; end
388
441
  def items_after_cursor; @list.size - @selected - 1 end
389
442
  def cursor_min; 0 end
390
- def cursor_max; [@size.height, @list.size].min - 1 end
391
-
392
- private def _check # debug method
393
- return
394
- fail "@selected = nil" unless @selected
395
- fail "@selected = #{@selected}" unless @selected >= 0
396
- fail "@selected > @list.size" if @selected >= @list.size
397
- fail "@cursor = nil" unless @cursor
398
- fail "@cursor = #{@cursor}" unless @cursor >= 0
399
- fail "@cursor > max" if @cursor > @win.maxy
443
+ def cursor_max
444
+ @list.empty? ? 0 : [@list.size, @size.height].min - 1
400
445
  end
401
446
  end
402
447
  end
@@ -20,8 +20,18 @@ module Ektoplayer
20
20
 
21
21
  def update(start_url: FREE_MUSIC_URL, pages: 0, parallel: 10)
22
22
  queue = parallel > 0 ? SizedQueue.new(parallel) : Queue.new
23
- insert_browserpage(bp = BrowsePage.new(start_url))
24
23
  results = Queue.new
24
+ bp = BrowsePage.new(start_url)
25
+ results << bp
26
+
27
+ insert_thread = Thread.new do
28
+ loop do
29
+ result = results.pop
30
+ @db.transaction
31
+ insert_browserpage(result)
32
+ @db.commit
33
+ end
34
+ end
25
35
 
26
36
  if pages > 0
27
37
  bp.page_urls[(bp.current_page_index + 1)..(bp.current_page_index + pages + 1)]
@@ -30,24 +40,24 @@ module Ektoplayer
30
40
  end.
31
41
  each do |url|
32
42
  queue << Thread.new do
33
- results << BrowsePage.new(url)
43
+
44
+ 3.times do |try|
45
+ begin
46
+ bp = BrowsePage.new(url)
47
+ Application.log(self, url, bp.albums.size, "albums found")
48
+ results << bp
49
+ break
50
+ rescue
51
+ Application.log(self, url, $!, "(retry ##{try})")
52
+ end
53
+ end
54
+
34
55
  queue.pop # unregister our thread
35
56
  end.priority
36
-
37
- if results.size > 40
38
- @db.transaction
39
- 40.times { insert_browserpage(results.pop(true)) }
40
- @db.commit
41
- end
42
57
  end
43
58
 
44
- sleep 1 while not queue.empty?
45
-
46
- @db.transaction
47
- while (result = queue.pop(true) rescue nil)
48
- insert_browserpage(result)
49
- end
50
- @db.commit
59
+ sleep 1 until queue.empty?
60
+ insert_thread.kill
51
61
  rescue
52
62
  Application.log(self, $!)
53
63
  end
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.19
4
+ version: 0.1.20
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-10-02 00:00:00.000000000 Z
11
+ date: 2017-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3