ektoplayer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +49 -0
  3. data/bin/ektoplayer +7 -0
  4. data/lib/ektoplayer.rb +10 -0
  5. data/lib/ektoplayer/application.rb +148 -0
  6. data/lib/ektoplayer/bindings.rb +230 -0
  7. data/lib/ektoplayer/browsepage.rb +138 -0
  8. data/lib/ektoplayer/client.rb +18 -0
  9. data/lib/ektoplayer/common.rb +91 -0
  10. data/lib/ektoplayer/config.rb +247 -0
  11. data/lib/ektoplayer/controllers/browser.rb +47 -0
  12. data/lib/ektoplayer/controllers/controller.rb +9 -0
  13. data/lib/ektoplayer/controllers/help.rb +21 -0
  14. data/lib/ektoplayer/controllers/info.rb +22 -0
  15. data/lib/ektoplayer/controllers/mainwindow.rb +40 -0
  16. data/lib/ektoplayer/controllers/playlist.rb +60 -0
  17. data/lib/ektoplayer/database.rb +199 -0
  18. data/lib/ektoplayer/events.rb +56 -0
  19. data/lib/ektoplayer/models/browser.rb +127 -0
  20. data/lib/ektoplayer/models/database.rb +49 -0
  21. data/lib/ektoplayer/models/model.rb +15 -0
  22. data/lib/ektoplayer/models/player.rb +28 -0
  23. data/lib/ektoplayer/models/playlist.rb +72 -0
  24. data/lib/ektoplayer/models/search.rb +42 -0
  25. data/lib/ektoplayer/models/trackloader.rb +17 -0
  26. data/lib/ektoplayer/mp3player.rb +151 -0
  27. data/lib/ektoplayer/operations/browser.rb +19 -0
  28. data/lib/ektoplayer/operations/operations.rb +26 -0
  29. data/lib/ektoplayer/operations/player.rb +11 -0
  30. data/lib/ektoplayer/operations/playlist.rb +67 -0
  31. data/lib/ektoplayer/theme.rb +102 -0
  32. data/lib/ektoplayer/trackloader.rb +146 -0
  33. data/lib/ektoplayer/ui.rb +404 -0
  34. data/lib/ektoplayer/ui/colors.rb +105 -0
  35. data/lib/ektoplayer/ui/widgets.rb +195 -0
  36. data/lib/ektoplayer/ui/widgets/container.rb +125 -0
  37. data/lib/ektoplayer/ui/widgets/labelwidget.rb +43 -0
  38. data/lib/ektoplayer/ui/widgets/listwidget.rb +332 -0
  39. data/lib/ektoplayer/ui/widgets/tabbedcontainer.rb +110 -0
  40. data/lib/ektoplayer/updater.rb +77 -0
  41. data/lib/ektoplayer/views/browser.rb +25 -0
  42. data/lib/ektoplayer/views/help.rb +46 -0
  43. data/lib/ektoplayer/views/info.rb +208 -0
  44. data/lib/ektoplayer/views/mainwindow.rb +64 -0
  45. data/lib/ektoplayer/views/playinginfo.rb +135 -0
  46. data/lib/ektoplayer/views/playlist.rb +39 -0
  47. data/lib/ektoplayer/views/progressbar.rb +51 -0
  48. data/lib/ektoplayer/views/splash.rb +99 -0
  49. data/lib/ektoplayer/views/trackrenderer.rb +137 -0
  50. data/lib/ektoplayer/views/volumemeter.rb +74 -0
  51. metadata +164 -0
@@ -0,0 +1,39 @@
1
+ require_relative '../ui/widgets/listwidget'
2
+ require_relative '../config'
3
+
4
+ require_relative 'trackrenderer'
5
+
6
+ module Ektoplayer
7
+ module Views
8
+ class Playlist < UI::ListWidget
9
+ def initialize(**opts)
10
+ super(**opts)
11
+ self.item_renderer=(TrackRenderer.new(
12
+ width: @size.width, format: Config[:'playlist.format']
13
+ ))
14
+ end
15
+
16
+ def attach(playlist)
17
+ self.list=(playlist.to_a)
18
+
19
+ playlist.events.on(:changed) do
20
+ with_lock { self.list=(playlist.to_a); want_redraw }
21
+ end
22
+
23
+ playlist.events.on(:current_changed) do
24
+ self.current_playing=(playlist.current_playing)
25
+ end
26
+ end
27
+
28
+ def render(index, **opts)
29
+ opts[:marked] = true if index == @current_playing
30
+ super(index, **opts)
31
+ end
32
+
33
+ def current_playing=(p)
34
+ return if @current_playing == p
35
+ with_lock { @current_playing = p; want_redraw }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../ui/widgets'
2
+ require_relative '../config'
3
+ require_relative '../theme'
4
+
5
+ module Ektoplayer
6
+ module Views
7
+ class ProgressBar < UI::Window
8
+ def attach(player)
9
+ player.events.on(:position_change) do
10
+ self.percent_playing = player.position_percent
11
+ end
12
+
13
+ view=self # TODO
14
+ [Curses::BUTTON1_CLICKED, Curses::BUTTON2_CLICKED, Curses::BUTTON3_CLICKED].
15
+ each do |button|
16
+ view.mouse.on(button) do |mevent|
17
+ pos = Float(mevent.x) / (self.size.width - 1) * player.length rescue player.position
18
+ player.seek(pos.to_i)
19
+ end
20
+ end
21
+ end
22
+
23
+ def percent_playing=(percent_playing)
24
+ char_width = (percent_playing * @size.width).to_i
25
+ return if char_width == @progress_width
26
+
27
+ with_lock do
28
+ @progress_width = char_width
29
+ want_redraw
30
+ end
31
+ end
32
+
33
+ def draw
34
+ @win.setpos(0,0)
35
+
36
+ @progress_width ||= 0
37
+ @progress_char ||= Config[:'progressbar.progress_char']
38
+ @rest_char ||= Config[:'progressbar.rest_char']
39
+
40
+ @win.with_attr(Theme[:'progressbar.progress']) do
41
+ repeat = (@progress_width - @progress_char.size)
42
+ @win << @progress_char[0] * repeat if repeat > 0
43
+ @win << @progress_char[1..-1] if @progress_width > 0
44
+ end
45
+
46
+ @win.attron(Theme[:'progressbar.rest'])
47
+ @win << @rest_char * (@size.width - @win.curx)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,99 @@
1
+ require_relative '../ui/colors'
2
+ require_relative '../ui/widgets'
3
+ require_relative '../theme'
4
+ require_relative '../common'
5
+
6
+ module Ektoplayer
7
+ module Views
8
+ class Splash < UI::Window
9
+ EKTOPLAZM_LOGO = %q.
10
+ ____ _ _ _ ____ _ _____ ______ _ ___ ___
11
+ / __) ; | | ; | | | _ \ | | /___ \ |____ | | '_ `_ \
12
+ / / | | | | _| |__ ___ | | | | | | | | | | | | | | | |
13
+ ( ( | | | | |_ __) / _ \ | | | | | | ___| | / , | | | | | |
14
+ \ \_ | | | | | | | | | | | | | | | | / _| | / / | | | | | |
15
+ ) _) | | | | | | | | | | | |_| ; | | | | | | / / | | | | | |
16
+ / / | |_|/ | | | |_| | | __/ | | | | | | / / | | | | | |
17
+ ( ( | _{ | | \___/ | | | | | | | | / / | | |_| | |
18
+ \ \__ | | |\ | |__ | | | :___ | |_| | , /____ | | | |
19
+ \____) |_| |_| \___/ |_| \____/ \_____| |______| |_| |_|..
20
+ split(?\n)[1..-1].freeze
21
+ EKTOPLAZM_SIGNATURE = %q;
22
+ ___ _ _ _ _ _ _
23
+ / __| ___ _ _ _ _ __| | | | (_) |__ ___ _ _ __ _| |_(_)___ _ _
24
+ \__ \/ _ \ || | ' \/ _` | | |__| | '_ \/ -_) '_/ _` | _| / _ \ ' \
25
+ |___/\___/\_,_|_||_\__,_| |____|_|_.__/\___|_| \__,_|\__|_\___/_||_|;.
26
+ split(?\n)[1..-1].freeze
27
+
28
+ BUBBLES = [
29
+ UI::Point.new(x: 6, y: 3).frz, UI::Point.new(x: 6, y: 7).frz,
30
+ UI::Point.new(x: 28, y: 1).frz, UI::Point.new(x: 28, y: 9).frz,
31
+ UI::Point.new(x: 46, y: 7).frz, UI::Point.new(x: 71, y: 9).frz
32
+ ].freeze
33
+
34
+ def load_colors
35
+ @signature_fade = @ekto_logo_fade = @bubble_fade = [-1].freeze
36
+
37
+ if Theme.current == 256
38
+ @bubble_fade = [161,162,167,168]
39
+ @signature_fade = [99, 105, 111, 117]
40
+ @ekto_logo_fade = [23, 23, 29, 36, 42, 48, 42, 36, 29, 23]
41
+ elsif Theme.current == 8
42
+ @bubble_fade = [:red]
43
+ @ekto_logo_fade = [:blue]
44
+ @signature_fade = [:magenta]
45
+ end
46
+
47
+ @bubble_fade = UI::ColorFader.new(@bubble_fade)
48
+ @signature_fade = UI::ColorFader.new(@signature_fade)
49
+ @ekto_logo_fade = UI::ColorFader.new(@ekto_logo_fade)
50
+ end
51
+
52
+ def draw
53
+ @win.erase
54
+ return if (EKTOPLAZM_LOGO.size >= @size.height or
55
+ EKTOPLAZM_LOGO.max.size >= @size.width)
56
+ load_colors
57
+
58
+ w_center = @size.width / 2
59
+ h_center = @size.height / 2
60
+ left_pad = w_center - (EKTOPLAZM_LOGO.max.size / 2)
61
+
62
+ if EKTOPLAZM_LOGO.size + EKTOPLAZM_SIGNATURE.size + 3 > @size.height
63
+ top_pad = h_center - (EKTOPLAZM_LOGO.size / 2)
64
+ draw_signature = false
65
+ else
66
+ top_pad = h_center - (EKTOPLAZM_LOGO.size / 2 + 3)
67
+ draw_signature = true
68
+ end
69
+
70
+ @ekto_logo_fade.fade(EKTOPLAZM_LOGO.size).each_with_index do |c,i|
71
+ @win.with_attr(c) do
72
+ @win.setpos(top_pad + i, left_pad)
73
+ @win << EKTOPLAZM_LOGO[i]
74
+ end
75
+ end
76
+
77
+ f = @bubble_fade.fade(EKTOPLAZM_LOGO.size)
78
+ BUBBLES.each do |p|
79
+ @win.setpos(top_pad + p.y - 1, left_pad + p.x + 1)
80
+ @win.attron(f[p.y - 1]) { @win.addch(?_) }
81
+ @win.setpos(top_pad + p.y, left_pad + p.x)
82
+ @win.attron(f[p.y]) { @win << '(_)' }
83
+ end
84
+
85
+ return unless draw_signature
86
+
87
+ top_pad += EKTOPLAZM_LOGO.size + 2
88
+ left_pad = w_center - (EKTOPLAZM_SIGNATURE.max.size / 2)
89
+
90
+ EKTOPLAZM_SIGNATURE.each_with_index do |line, i|
91
+ @win.setpos(top_pad + i, left_pad)
92
+ @signature_fade.fade2(line.size).each_with_index do |color,y|
93
+ @win.with_attr(color) { @win << line[y] }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,137 @@
1
+ require_relative '../ui/widgets'
2
+
3
+ class PercentualDistributor
4
+ def initialize(total: 0, parts: [])
5
+ @total, @parts = total, parts
6
+ @results = nil
7
+ end
8
+
9
+ def increment_total(n)
10
+ @total += n
11
+ @results = nil
12
+ end
13
+
14
+ def add_part(n)
15
+ @parts << n
16
+ @results = nil
17
+ end
18
+
19
+ def get_for(part)
20
+ fail ArgumentError unless @parts.include? part
21
+
22
+ unless @results
23
+ @results = Hash.new { |h,k| h[k] = [] }
24
+
25
+ part_total, results_total = @parts.sum, 0
26
+ @parts.each_with_index do |p,i|
27
+ v = @total * p / part_total
28
+ @results[p] << v
29
+ results_total += v
30
+ end
31
+
32
+ @results.each do |k, values|
33
+ values.map! do |v|
34
+ if results_total > @total
35
+ results_total -= 1
36
+ v - 1
37
+ elsif results_total < @total
38
+ results_total += 1
39
+ v + 1
40
+ else
41
+ v
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ @results[part].unshift(r = @results[part].pop)
48
+ r
49
+ end
50
+ end
51
+
52
+ module Ektoplayer
53
+ module Views
54
+ class TrackRenderer < UI::ListItemRenderer
55
+ def initialize(width: nil, format: nil)
56
+ super(width: width)
57
+ self.column_format=(format)
58
+ end
59
+
60
+ def column_format=(format)
61
+ return unless format
62
+ @column_format = format
63
+ layout
64
+ end
65
+
66
+ def layout
67
+ return unless @column_format
68
+ fail if @column_format.empty?
69
+ fail unless @width
70
+
71
+ pd = PercentualDistributor.new(
72
+ total: (@width - (@column_format.size - 1))
73
+ )
74
+
75
+ @column_format.each do |c|
76
+ if c[:rel]
77
+ pd.add_part(c[:rel])
78
+ else
79
+ c[:render_size] = c[:size]
80
+ pd.increment_total(-1 * c[:size])
81
+ end
82
+
83
+ c[:curses_codes] = UI::Colors.set(nil, *c[:curses_attrs])
84
+ end
85
+
86
+ @column_format.each do |c|
87
+ if c[:rel]
88
+ c[:render_size] = (pd.get_for(c[:rel]) or fail)
89
+ end
90
+ end
91
+ end
92
+
93
+ def render(scr, item, index, selected: false, marked: false)
94
+ fail ArgumentError, 'item is nil' unless item
95
+ return unless @column_format
96
+
97
+ additional_attributes = 0
98
+ additional_attributes |= Curses::A_BOLD if marked
99
+ additional_attributes |= Curses::A_STANDOUT if selected
100
+
101
+ if item.is_a? String or item.is_a? Symbol
102
+ if index % 2 == 0
103
+ color = Theme[:'list.item_even']
104
+ else
105
+ color = Theme[:'list.item_odd']
106
+ end
107
+
108
+ scr.with_attr(color | additional_attributes) do
109
+ scr << "[#{item}]".ljust(@width)
110
+ end
111
+ return
112
+ end
113
+
114
+ @column_format.each_with_index do |c,i|
115
+ scr.with_attr(c[:curses_codes] | additional_attributes) do
116
+ value = (item[c[:tag]] or '')
117
+
118
+ if value.is_a?(Integer)
119
+ value = "%.2d" % value
120
+ else
121
+ value = value.to_s[0..(c[:render_size] - 1)]
122
+ end
123
+
124
+ if c[:justify] == :right
125
+ value = value.rjust(c[:render_size])
126
+ else
127
+ value = value.ljust(c[:render_size])
128
+ end
129
+
130
+ scr.addstr(value)
131
+ scr.addstr(' ') if i < (@column_format.size - 1)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../ui/widgets'
2
+ require_relative '../config'
3
+ require_relative '../theme'
4
+
5
+ module Ektoplayer
6
+ module Views
7
+ class VolumeMeter < UI::Window
8
+ def layout
9
+ super
10
+ load_colors(true)
11
+ end
12
+
13
+ def load_colors(force=false)
14
+ return if @fade and not force
15
+
16
+ if Theme.current == 256
17
+ fader = UI::ColorFader.new([25,26,27,32,39,38,44,44,45,51,87,159,195])
18
+ elsif Theme.current == 8
19
+ fader = UI::ColorFader.new([:blue])
20
+ else
21
+ fader = UI::ColorFader.new([-1])
22
+ end
23
+
24
+ @fade = fader.fade(@size.width)
25
+ @level_width = @size.width
26
+ @level_char = Config[:'volumemeter.level_char']
27
+ @rest_char = Config[:'volumemeter.rest_char']
28
+ end
29
+
30
+ def level=(level)
31
+ new_level_width = (1.4 * level * @size.width).to_i.clamp(0, @size.width - 1) rescue 0
32
+ return if @level_width == new_level_width
33
+ load_colors
34
+
35
+ if new_level_width > @level_width
36
+ @win.setpos(@win.cury, @level_width)
37
+ (@level_width).upto(new_level_width).each do |i|
38
+ @win.attron(@fade[i])
39
+ @win << @level_char
40
+ end
41
+ else
42
+ @win.setpos(@win.cury, new_level_width)
43
+ end
44
+
45
+ if (repeat = @size.width - @win.curx - 1) > 0
46
+ @win.attron(Theme[:'volumemeter.rest'])
47
+ @win << @rest_char * (repeat + 1)
48
+ end
49
+
50
+ @level_width = new_level_width
51
+ @win.refresh if visible?
52
+ end
53
+
54
+ def attach(player)
55
+ player.events.on(:position_change) { self.level=(player.level) }
56
+ player.events.on(:stop) { self.level=(90) }
57
+ player.events.on(:pause) { self.level=(90) }
58
+ end
59
+
60
+ def draw
61
+ load_colors
62
+ @win.setpos(0,0)
63
+
64
+ @level_width.times do |i|
65
+ @win.attron(@fade[i])
66
+ @win << @level_char
67
+ end
68
+
69
+ @win.attron(Theme[:'volumemeter.rest'])
70
+ @win << @rest_char * (@size.width - @win.curx)
71
+ end
72
+ end
73
+ end
74
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ektoplayer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Abendroth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: audite
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: curses
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubyzip
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.2'
83
+ description: Console audio player for ektoplazm.com
84
+ email: braph93@gmx.de
85
+ executables:
86
+ - ektoplayer
87
+ extensions: []
88
+ extra_rdoc_files:
89
+ - README.md
90
+ files:
91
+ - README.md
92
+ - bin/ektoplayer
93
+ - lib/ektoplayer.rb
94
+ - lib/ektoplayer/application.rb
95
+ - lib/ektoplayer/bindings.rb
96
+ - lib/ektoplayer/browsepage.rb
97
+ - lib/ektoplayer/client.rb
98
+ - lib/ektoplayer/common.rb
99
+ - lib/ektoplayer/config.rb
100
+ - lib/ektoplayer/controllers/browser.rb
101
+ - lib/ektoplayer/controllers/controller.rb
102
+ - lib/ektoplayer/controllers/help.rb
103
+ - lib/ektoplayer/controllers/info.rb
104
+ - lib/ektoplayer/controllers/mainwindow.rb
105
+ - lib/ektoplayer/controllers/playlist.rb
106
+ - lib/ektoplayer/database.rb
107
+ - lib/ektoplayer/events.rb
108
+ - lib/ektoplayer/models/browser.rb
109
+ - lib/ektoplayer/models/database.rb
110
+ - lib/ektoplayer/models/model.rb
111
+ - lib/ektoplayer/models/player.rb
112
+ - lib/ektoplayer/models/playlist.rb
113
+ - lib/ektoplayer/models/search.rb
114
+ - lib/ektoplayer/models/trackloader.rb
115
+ - lib/ektoplayer/mp3player.rb
116
+ - lib/ektoplayer/operations/browser.rb
117
+ - lib/ektoplayer/operations/operations.rb
118
+ - lib/ektoplayer/operations/player.rb
119
+ - lib/ektoplayer/operations/playlist.rb
120
+ - lib/ektoplayer/theme.rb
121
+ - lib/ektoplayer/trackloader.rb
122
+ - lib/ektoplayer/ui.rb
123
+ - lib/ektoplayer/ui/colors.rb
124
+ - lib/ektoplayer/ui/widgets.rb
125
+ - lib/ektoplayer/ui/widgets/container.rb
126
+ - lib/ektoplayer/ui/widgets/labelwidget.rb
127
+ - lib/ektoplayer/ui/widgets/listwidget.rb
128
+ - lib/ektoplayer/ui/widgets/tabbedcontainer.rb
129
+ - lib/ektoplayer/updater.rb
130
+ - lib/ektoplayer/views/browser.rb
131
+ - lib/ektoplayer/views/help.rb
132
+ - lib/ektoplayer/views/info.rb
133
+ - lib/ektoplayer/views/mainwindow.rb
134
+ - lib/ektoplayer/views/playinginfo.rb
135
+ - lib/ektoplayer/views/playlist.rb
136
+ - lib/ektoplayer/views/progressbar.rb
137
+ - lib/ektoplayer/views/splash.rb
138
+ - lib/ektoplayer/views/trackrenderer.rb
139
+ - lib/ektoplayer/views/volumemeter.rb
140
+ homepage: http://www.github.com/braph/ektoplayer
141
+ licenses:
142
+ - GPL-3.0
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.6.8
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: play music from ektoplazm.com
164
+ test_files: []