mikeplayer 1.0.2 → 1.0.7

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
  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: