mikeplayer 1.0.2 → 1.0.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44e5e59f36ed8b6c41f8d2c36e65bb1b997b920a2dd3ad59eba2fff0c2dc5141
4
- data.tar.gz: 59efb825846abc56475a6b5a562a3a897ea68f4b985ae4bb880c968492160bec
3
+ metadata.gz: 4629aa37d245f2c061c144700a0d23aba06a851085baed86b54a8514c7919146
4
+ data.tar.gz: 1554111b1ae2568d736a248f9310d79ec6198c4f1cb0d672814f287a463d7ddb
5
5
  SHA512:
6
- metadata.gz: ddf8ec65bf9ba7486cf48cc4e27c4507d55fbc233aa2c3c9bb4afc74a6d0778a77a6817bc66070ab7adaa90248bed5ced766b4bf203e9afd905789800fc546a8
7
- data.tar.gz: e3655f5176eb292128264151db69987dcd3b3fd3fcc6379c6c80028198e1b0da9833591ddd798cfb53d9395ee30a04bd77d9c6911eabd19608a13784c3614687
6
+ metadata.gz: cbaec3a72786231a7c327259539a1aef222b1a8e548e9254ac09ac5a3c65c2a6b39d992f9590450ee382e8f521c6e61031a8b798617517a867f4eebbf7411356
7
+ data.tar.gz: 3bd515e08a89b88cf3e52c21f547d2b056a5472b9ef6db95da378ff7c1b612e79cbbd824623e067d18ee7db218627d67a8244c6db60f1f5c34f0bf06752f2303
data/bin/MikePlayer.rb CHANGED
@@ -21,6 +21,7 @@ OptionParser.new do |opt|
21
21
  opt.on('-l', '--list', 'List songs in playlist.') { |o| options[:list] = true; }
22
22
  opt.on('-d', '--directory name', 'Directory to find mp3s.') { |o| options[:directory] = o }
23
23
  opt.on('-t', '--time minutes', 'Limit time to number of minutes.') { |o| options[:minutes] = o }
24
+ opt.on('-x', '--debug', 'Turn on debug.') { |o| options[:debug] = true }
24
25
  end.parse!
25
26
 
26
27
  MikePlayer::Player.new(options, ARGV).play
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'json'
5
+ require 'mikeplayer'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start
data/lib/mikeplayer.rb CHANGED
@@ -4,28 +4,29 @@ require 'open3'
4
4
  require 'io/console'
5
5
  require 'mp3info'
6
6
  require 'mikeplayer/version'
7
- require 'mikeplayer/settings'
7
+ require 'mikeplayer/display'
8
8
  require 'mikeplayer/playlist'
9
+ require 'mikeplayer/play_thread'
10
+ require 'mikeplayer/settings'
9
11
  require 'mikeplayer/song'
10
12
 
11
13
  module MikePlayer
12
14
  class Player
13
- PAUSE_INDICATOR = " ||".freeze
14
- SLEEP_SETTING = 0.5
15
+ PLAY_SLEEP = 0.5
16
+ PAUSE_SLEEP = 1.0
15
17
  STOPPED = :stopped
16
18
  PLAYING = :playing
17
19
  PAUSED = :paused
20
+ SONG_CHANGE = :song_change
18
21
 
19
22
  def initialize(options, *args)
20
23
  @settings = Settings.new(options)
21
24
  @playlist = Playlist.new(@settings.playlist)
22
25
  @minutes = @settings.minutes
23
26
  @command = ''
24
- @pid = nil
25
- @timer_start = nil
27
+ @timer_start = Time.now if (@minutes > 0)
26
28
  @state = STOPPED
27
-
28
- check_system
29
+ @player = PlayThread.new(volume: @settings.volume)
29
30
 
30
31
  if (true == @settings.list?)
31
32
  @songs.map { |song| File.basename(song) }.sort.each {|song| puts "#{File.basename(song)}"}
@@ -50,101 +51,54 @@ module MikePlayer
50
51
  puts "Mike Player v#{MikePlayer::VERSION}"
51
52
  puts "Playlist #{@playlist.info}\n"
52
53
 
53
- if @playlist.finished?
54
+ if (0 == @playlist.size)
54
55
  puts "No songs in playlist."
55
56
 
56
57
  exit 1
57
58
  end
58
59
 
59
60
  @thread = Thread.new do
60
- display_width = 0
61
+ @display = Display.new
62
+ @song_i = 0
61
63
 
62
- while (false == @playlist.finished?)
63
- song = @playlist.current
64
- @song_start = Time.now
65
- info_prefix = "\r#{@playlist.current_song_info}".freeze
64
+ while (@song_i < @playlist.size)
65
+ @display.song_info = @playlist.song_info(@song_i)
66
66
 
67
- stdin, stdother, thread_info = Open3.popen2e('play', '--no-show-progress', '--volume', @settings.volume, song.filename)
67
+ @player.play(song.filename)
68
68
 
69
69
  @state = PLAYING
70
- @pid = thread_info.pid
71
- indicator = ''
72
- info_changed = false
73
70
 
74
- while (true == pid_alive?)
71
+ while @player.playing?
75
72
  pause_if_over_time_limit
76
73
 
77
- if (true == playing?)
78
- indicator = "#{'>' * (playing_time % 4)}"
79
- info_changed = true
80
- elsif (true == paused?) && (PAUSE_INDICATOR != indicator)
81
- indicator = PAUSE_INDICATOR
82
- info_changed = true
83
- end
84
-
85
- if (true == info_changed)
86
- mindicator = ""
87
-
88
- if (0 < minutes_remaining)
89
- mindicator = "(#{minutes_remaining}↓) "
90
- end
91
-
92
- info = "#{info_prefix} #{song.length_str(playing_time)} #{mindicator}#{indicator}".ljust(display_width)
74
+ @display.elapsed = @player.elapsed if playing?
93
75
 
94
- display_width = info.size
76
+ @display.display!(song.length_str(@player.elapsed), minutes_remaining)
95
77
 
96
- print(info)
97
-
98
- info_changed = false
99
- $stdout.flush
100
- end
101
-
102
- sleep SLEEP_SETTING
78
+ sleep(sleep_time)
103
79
  end
104
80
 
105
- stdin.close
106
- stdother.close
107
-
108
- @pid = nil
109
-
110
- if (true == playing?)
81
+ if playing? && @player.stopped?
111
82
  next_song
83
+ elsif paused?
84
+ while paused?
85
+ sleep(sleep_time)
86
+ end
112
87
  end
113
88
  end
114
89
 
115
- @pid = nil
90
+ @player.stop
91
+ print("\r\n")
116
92
  exit
117
93
  end
118
94
 
119
95
  wait_on_user
120
96
 
121
- puts ""
122
- end
123
-
124
- def cmd_exist?(cmd)
125
- if (true != system('command'))
126
- raise "Missing 'command' command, which is used to test compatibility."
127
- end
128
-
129
- if (true != system("command -v #{cmd} >/dev/null 2>&1"))
130
- return false
131
- end
132
-
133
- return true
97
+ print("\r\n")
134
98
  end
135
99
 
136
100
  private
137
101
 
138
- def check_system
139
- %w(play).each do |cmd|
140
- if (false == cmd_exist?(cmd))
141
- raise "#{cmd} failed, do you have sox installed?"
142
- end
143
- end
144
-
145
- return nil
146
- end
147
-
148
102
  def wait_on_user
149
103
  while ('q' != @command)
150
104
  @command = STDIN.getch
@@ -155,9 +109,8 @@ module MikePlayer
155
109
  next_song
156
110
  elsif ('z' == @command)
157
111
  previous_song
158
- elsif ('q' == @command) && (false == @pid.nil?)
159
- stop_song
160
- @thread.kill
112
+ elsif ('q' == @command)
113
+ press_stop
161
114
  elsif ('t' == @command)
162
115
  @timer_start = Time.now
163
116
  elsif (false == @timer_start.nil?) && ("#{@command.to_i}" == @command)
@@ -179,66 +132,58 @@ module MikePlayer
179
132
  return (PAUSED == @state)
180
133
  end
181
134
 
135
+ def changing?
136
+ return (SONG_CHANGE == @state)
137
+ end
138
+
139
+ def press_stop
140
+ @player.stop
141
+ @state = STOPPED
142
+ end
143
+
182
144
  def press_pause
183
- if (true == playing?)
184
- kill("STOP")
185
- @pause_time = Time.now
145
+ debug('|')
146
+
147
+ if playing?
148
+ debug('>')
186
149
  @state = PAUSED
187
- elsif (true == paused?)
188
- kill("CONT")
189
- @song_start += (Time.now - @pause_time)
190
- @pause_time = nil
150
+ @display.paused
151
+ @player.pause
152
+ elsif paused?
153
+ debug('|')
191
154
  @state = PLAYING
192
155
  else
193
156
  print("Confused state #{@state}.")
194
157
  end
195
158
  end
196
159
 
197
- def stop_song
198
- if (true == paused?)
199
- kill("CONT")
200
- end
201
-
202
- kill("INT")
203
-
204
- sleep 0.2
205
-
206
- if (true == pid_alive?)
207
- kill("KILL")
208
- end
209
-
210
- @state = STOPPED
211
- end
212
-
213
- def pid_alive?(pid = @pid)
214
- if (false == pid.nil?)
215
- return system("ps -p #{pid} > /dev/null")
216
- end
160
+ def next_song
161
+ debug('n')
217
162
 
218
- return false
219
- end
163
+ @state = SONG_CHANGE
220
164
 
221
- def next_song
222
- stop_song
165
+ @player.stop
223
166
 
224
- @playlist.next
167
+ @song_i += 1
225
168
  end
226
169
 
227
170
  def previous_song
228
- stop_song
171
+ debug('p')
229
172
 
230
- @playlist.previous
231
- end
173
+ @state = SONG_CHANGE
232
174
 
233
- def kill(signal)
234
- if (false == @pid.nil?)
235
- Process.kill(signal, @pid)
175
+ if (@player.elapsed < 10)
176
+ @song_i -= 1 if @song_i.positive?
177
+ else
178
+ debug('x')
236
179
  end
180
+
181
+ @player.stop
237
182
  end
238
183
 
239
184
  def pause_if_over_time_limit
240
185
  if (false == @timer_start.nil?) && (0 < @minutes) && (true == playing?)
241
- if (0 > minutes_remaining)
186
+ if (minutes_remaining && 0 >= minutes_remaining)
242
187
  press_pause
243
188
  @timer_start = nil
244
189
  @minutes = 0
@@ -246,24 +191,24 @@ module MikePlayer
246
191
  end
247
192
  end
248
193
 
249
- def playing_time
250
- return (Time.now - @song_start).to_i - pause_time
194
+ def minutes_remaining
195
+ return if ((0 == @minutes) || (@timer_start.nil?))
196
+
197
+ (@minutes - ((Time.now - @timer_start).to_i / 60).to_i)
251
198
  end
252
199
 
253
- def pause_time
254
- if (@pause_time.nil?)
255
- return 0
256
- else
257
- return (Time.now - @pause_time).to_i
258
- end
200
+ def sleep_time
201
+ return PLAY_SLEEP if playing?
202
+
203
+ PAUSE_SLEEP
259
204
  end
260
205
 
261
- def minutes_remaining
262
- if ((0 == @minutes) || (@timer_start.nil?))
263
- return -1
264
- else
265
- return (@minutes - ((Time.now - @timer_start).to_i / 60).to_i)
266
- end
206
+ def song
207
+ @playlist.get(@song_i)
208
+ end
209
+
210
+ def debug(str)
211
+ print(str) if @settings.debug?
267
212
  end
268
213
  end
269
214
  end
@@ -0,0 +1,49 @@
1
+ module MikePlayer
2
+ class Display
3
+ PAUSE_INDICATOR = '||'.freeze
4
+ INDICATOR_SIZE = 4
5
+
6
+ def initialize
7
+ @width = 0
8
+ @indicator = ''
9
+ @changed = false
10
+ end
11
+
12
+ def song_info=(v)
13
+ @info_prefix = "\r#{v}".freeze
14
+ end
15
+
16
+ def elapsed=(v)
17
+ @indicator = "#{'>' * (v % INDICATOR_SIZE)}".ljust(INDICATOR_SIZE)
18
+ @changed = true
19
+ end
20
+
21
+ def paused
22
+ if (false == @indicator.include?(PAUSE_INDICATOR))
23
+ @indicator = PAUSE_INDICATOR.ljust(INDICATOR_SIZE)
24
+ @changed = true
25
+ end
26
+ end
27
+
28
+ def display!(elapsed_info, countdown = nil)
29
+ return unless changed?
30
+
31
+ mindicator = "(#{countdown}↓) " if countdown
32
+
33
+ print("\r" << ' '.ljust(@width))
34
+
35
+ info = "#{@info_prefix} #{elapsed_info} #{mindicator}#{@indicator}"
36
+
37
+ print(info)
38
+
39
+ @width = info.size
40
+ @changed = false
41
+
42
+ $stdout.flush
43
+ end
44
+
45
+ def changed?
46
+ true == @changed
47
+ end
48
+ end
49
+ end
@@ -1,13 +1,121 @@
1
1
  module MikePlayer
2
2
  class PlayThread
3
- def self.run(file, volume)
3
+ def initialize(volume: 1.0)
4
+ check_system
5
+
6
+ @pid = nil
7
+ @volume = volume
8
+ @start_t = 0
9
+ @elapsed = 0
10
+ @paused = false
11
+ end
12
+
13
+ def play(file)
14
+ start_position = 0
15
+
16
+ if paused?
17
+ start_position = @elapsed
18
+ else
19
+ @elapsed = 0
20
+ end
21
+
22
+ start_thread(file: file, start_position: start_position)
23
+
24
+ @start_t = Time.now.to_i
25
+ end
26
+
27
+ def stop
28
+ pause
29
+
30
+ @elapsed = 0
31
+ @start_t = 0
32
+ @paused = false
33
+ end
34
+
35
+ def pause
36
+ kill('INT')
37
+
38
+ @elapsed += Time.now.to_i - @start_t
39
+ @start_t = 0
40
+
41
+ sleep 0.2
42
+
43
+ kill('KILL')
44
+
45
+ @paused = true
46
+ end
47
+
48
+ def kill(signal)
49
+ Process.kill(signal, @pid) if alive?
50
+ end
51
+
52
+ def alive?
53
+ MikePlayer::PlayThread.alive?(@pid)
54
+ end
55
+
56
+ def stopped?
57
+ false == alive?
58
+ end
59
+
60
+ def paused?
61
+ @paused && stopped?
62
+ end
63
+
64
+ def playing?
65
+ alive?
66
+ end
67
+
68
+ def elapsed
69
+ return (@elapsed + (Time.now.to_i - @start_t)) if @start_t.positive?
70
+
71
+ @elapsed
72
+ end
73
+
74
+ def self.alive?(pid)
75
+ return system("ps -p #{pid} > /dev/null") unless pid.nil?
76
+
77
+ false
78
+ end
79
+
80
+ def self.cmd_exist?(cmd)
81
+ if (true != system('command'))
82
+ raise "Missing 'command' command, which is used to test compatibility."
83
+ end
84
+
85
+ if (true != system("command -v #{cmd} >/dev/null 2>&1"))
86
+ return false
87
+ end
88
+
89
+ return true
90
+ end
91
+
92
+ private
93
+ def start_thread(file:, start_position: )
4
94
  args = [
5
- :play,
95
+ 'play',
6
96
  '--no-show-progress',
7
- '--volume', volume,
8
- song
97
+ '--volume', @volume.to_s,
98
+ file,
99
+ 'trim', start_position.to_s,
9
100
  ]
10
- Open3.popen2e(*args) do |stdin, out, wait_thr|
101
+
102
+ stdin, stdother, thread_info = Open3.popen2e(*args)
103
+
104
+ @pid = thread_info.pid
105
+
106
+ sleep 0.2
107
+
108
+ raise "Failed to play #{stdother.read}" unless alive?
109
+
110
+ stdin.close
111
+ stdother.close
112
+
113
+ self
114
+ end
115
+
116
+ def check_system
117
+ %w[play].each do |cmd|
118
+ raise "#{cmd} failed, do you have sox installed?" unless MikePlayer::PlayThread.cmd_exist?(cmd)
11
119
  end
12
120
  end
13
121
  end
@@ -5,12 +5,11 @@ module MikePlayer
5
5
  def initialize(filename)
6
6
  @filename = filename
7
7
  @songs = []
8
- @song_i = 0
9
8
  @length = 0
10
9
 
11
10
  load_songs
12
11
 
13
- @loaded_song_count = @songs.size
12
+ @loaded_song_count = self.size
14
13
  end
15
14
 
16
15
  def <<(song)
@@ -44,20 +43,8 @@ module MikePlayer
44
43
  return self
45
44
  end
46
45
 
47
- def current
48
- return @songs[@song_i]
49
- end
50
-
51
- def next
52
- @song_i += 1
53
-
54
- return self.current
55
- end
56
-
57
- def previous
58
- @song_i -= 1 unless @song_i <= 0
59
-
60
- return self.current
46
+ def get(i)
47
+ return @songs[i]
61
48
  end
62
49
 
63
50
  def shuffle!
@@ -77,17 +64,17 @@ module MikePlayer
77
64
  end
78
65
 
79
66
  def info
80
- return "#{self.name} loaded #{@loaded_song_count} songs with length #{Song.as_duration_str(@length)}, added #{@songs.size - @loaded_song_count}"
67
+ return "#{self.name} loaded #{@loaded_song_count} and added #{self.size - @loaded_song_count} songs with length #{Song.as_duration_str(@length)}"
81
68
  end
82
69
 
83
- def current_song_info
84
- song_i_str = "#{@song_i + 1}".rjust(@songs.size.to_s.size)
70
+ def song_info(i)
71
+ song_i_str = "#{i + 1}".rjust(self.size.to_s.size)
85
72
 
86
- return "Playing (#{song_i_str}/#{@songs.size}): #{current.info}".freeze
73
+ return "Playing (#{song_i_str}/#{self.size}): #{get(i).info}".freeze
87
74
  end
88
75
 
89
- def finished?
90
- return @song_i == @songs.size
76
+ def size
77
+ return @songs.size
91
78
  end
92
79
 
93
80
  private
@@ -16,6 +16,7 @@ module MikePlayer
16
16
  @music_dir = options[:directory] || File.join(@home, DEFAULT_DIRECTORY)
17
17
  @settings_dir = options[:settings] || File.join(@home, SETTINGS_DIRECTORY)
18
18
  @minutes = options[:minutes].to_i
19
+ @debug = options[:debug]
19
20
  @random = options[:random].to_i
20
21
 
21
22
  if (false == Dir.exist?(@settings_dir))
@@ -35,6 +36,10 @@ module MikePlayer
35
36
  return 0 < @random
36
37
  end
37
38
 
39
+ def debug?
40
+ return (true == @debug)
41
+ end
42
+
38
43
  def overwrite?
39
44
  return true == @overwrite
40
45
  end
@@ -23,7 +23,7 @@ module MikePlayer
23
23
  end
24
24
 
25
25
  def length
26
- return @mp3info.length
26
+ return @mp3info.length || 0
27
27
  end
28
28
 
29
29
  def length_str(elapsed_time)
@@ -1,3 +1,3 @@
1
1
  module MikePlayer
2
- VERSION = '1.0.2'.freeze
2
+ VERSION = '1.0.7'.freeze
3
3
  end
data/mikeplayer.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |gem|
28
28
  gem.require_paths = ['lib']
29
29
  gem.licenses = ['MIT']
30
30
 
31
- gem.add_dependency 'json', '~> 1.8'
31
+ gem.add_dependency 'json', '~> 2.5'
32
32
  gem.add_dependency 'ruby-mp3info', '~> 0.8'
33
33
  gem.add_dependency 'minitest', '~> 5'
34
34
  gem.add_development_dependency 'rake', '~> 12'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mikeplayer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Crockett
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-28 00:00:00.000000000 Z
11
+ date: 2021-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.8'
19
+ version: '2.5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.8'
26
+ version: '2.5'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: ruby-mp3info
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +92,7 @@ email:
92
92
  - rubygems@mmcrockett.com
93
93
  executables:
94
94
  - MikePlayer.rb
95
+ - console
95
96
  extensions: []
96
97
  extra_rdoc_files: []
97
98
  files:
@@ -101,7 +102,9 @@ files:
101
102
  - README.md
102
103
  - Rakefile
103
104
  - bin/MikePlayer.rb
105
+ - bin/console
104
106
  - lib/mikeplayer.rb
107
+ - lib/mikeplayer/display.rb
105
108
  - lib/mikeplayer/executable.rb
106
109
  - lib/mikeplayer/play_thread.rb
107
110
  - lib/mikeplayer/playlist.rb
@@ -116,7 +119,7 @@ homepage: https://github.com/mmcrockett/mikeplayer
116
119
  licenses:
117
120
  - MIT
118
121
  metadata: {}
119
- post_install_message:
122
+ post_install_message:
120
123
  rdoc_options: []
121
124
  require_paths:
122
125
  - lib
@@ -131,9 +134,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
134
  - !ruby/object:Gem::Version
132
135
  version: '0'
133
136
  requirements: []
134
- rubyforge_project:
135
- rubygems_version: 2.7.6
136
- signing_key:
137
+ rubygems_version: 3.1.2
138
+ signing_key:
137
139
  specification_version: 4
138
140
  summary: Wraps Sox's `play` command, allowing playslists, find, random and time limit.
139
141
  test_files: