jimmy_jukebox 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/play_jukebox +5 -3
- data/lib/jimmy_jukebox/artists.rb +1 -1
- data/lib/jimmy_jukebox/jukebox.rb +31 -20
- data/lib/jimmy_jukebox/song.rb +10 -58
- data/lib/jimmy_jukebox/user_interface.rb +1 -1
- data/lib/jimmy_jukebox/version.rb +1 -1
- metadata +1 -1
data/bin/play_jukebox
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
$running_jruby = defined?(JRUBY_VERSION) || (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby') || RUBY_PLATFORM == 'java'
|
4
|
+
|
5
|
+
if $running_jruby
|
4
6
|
begin
|
5
7
|
require 'spoon'
|
6
8
|
require 'readline'
|
7
9
|
rescue LoadError => e
|
8
10
|
if e.message =~ /spoon/
|
9
|
-
p "*** You must install
|
11
|
+
p "*** You must run 'gem install spoon' before using JimmyJukebox on JRuby ***"
|
10
12
|
exit
|
11
13
|
elsif e.message =~ /readline/
|
12
14
|
p "*** You must install 'readline' to use JimmyJukebox on JRuby ***"
|
@@ -24,7 +26,7 @@ else
|
|
24
26
|
p "*** You must install 'readline' or the 'rb-readline' gem to use JimmyJukebox in Ruby ***"
|
25
27
|
exit
|
26
28
|
elsif e.message =~ /posix/ || e.message =~ /spawn/
|
27
|
-
p "*** You must install
|
29
|
+
p "*** You must run 'gem install posix-spawn' before using JimmyJukebox in Ruby ***"
|
28
30
|
exit
|
29
31
|
else
|
30
32
|
raise
|
@@ -26,6 +26,7 @@ module Artists
|
|
26
26
|
:lh => "lionel_hampton",
|
27
27
|
:md => "miles_davis",
|
28
28
|
:odjb => "original_dixieland_jazz_band",
|
29
|
+
:rt => "ragtime",
|
29
30
|
:rn => "red_norvo",
|
30
31
|
:sb => "sidney_bechet"
|
31
32
|
}
|
@@ -44,5 +45,4 @@ module Artists
|
|
44
45
|
value.to_s.split("_").map! { |name_component| name_component.capitalize }.join("") + '.yml'
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
48
|
end
|
@@ -8,8 +8,8 @@ module JimmyJukebox
|
|
8
8
|
class NoCurrentSongException < Exception; end
|
9
9
|
class NoPreviousSongException < Exception; end
|
10
10
|
|
11
|
-
attr_accessor :current_song, :continuous_play
|
12
|
-
attr_writer :user_config, :
|
11
|
+
attr_accessor :current_song, :continuous_play, :songs_played
|
12
|
+
attr_writer :user_config, :next_song, :playing
|
13
13
|
|
14
14
|
def initialize(new_user_config = UserConfig.new, continuous_play = true)
|
15
15
|
self.user_config = new_user_config
|
@@ -19,7 +19,7 @@ module JimmyJukebox
|
|
19
19
|
def play_loop
|
20
20
|
loop do
|
21
21
|
if continuous_play && !playing?
|
22
|
-
p "Playing
|
22
|
+
p "Playing next song"
|
23
23
|
play_next_song
|
24
24
|
else
|
25
25
|
sleep 0.1
|
@@ -28,7 +28,10 @@ module JimmyJukebox
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def next_song
|
31
|
-
|
31
|
+
# reset @next_song each time it's accessed
|
32
|
+
current_next_song = @next_song ? @next_song : random_song
|
33
|
+
@next_song = random_song
|
34
|
+
current_next_song
|
32
35
|
end
|
33
36
|
|
34
37
|
def play_next_song
|
@@ -45,27 +48,37 @@ module JimmyJukebox
|
|
45
48
|
end
|
46
49
|
|
47
50
|
def previous_song
|
48
|
-
|
51
|
+
if songs_played.length >= 2
|
52
|
+
songs_played[songs_played.length-2]
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def songs_played
|
59
|
+
@songs_played ||= []
|
49
60
|
end
|
50
61
|
|
51
62
|
def replay_previous_song
|
52
|
-
if previous_song
|
63
|
+
if previous_song && current_song
|
64
|
+
enable_continuous_play
|
65
|
+
self.next_song = previous_song
|
53
66
|
p "Replaying #{previous_song.music_file}"
|
54
|
-
|
55
|
-
|
67
|
+
current_song.terminate
|
68
|
+
self.current_song = nil
|
69
|
+
self.playing = false
|
56
70
|
else
|
57
71
|
raise NoPreviousSongException, "No previous song"
|
58
72
|
end
|
59
73
|
end
|
60
74
|
|
61
75
|
def skip_song
|
62
|
-
enable_continuous_play
|
63
76
|
if current_song
|
77
|
+
enable_continuous_play
|
64
78
|
p "Skipping #{current_song.music_file}"
|
65
79
|
#play_random_song
|
66
|
-
|
80
|
+
current_song.terminate
|
67
81
|
self.current_song = nil
|
68
|
-
previous_song.terminate
|
69
82
|
self.playing = false
|
70
83
|
else
|
71
84
|
raise NoCurrentSongException, "No current_song"
|
@@ -90,41 +103,39 @@ module JimmyJukebox
|
|
90
103
|
end
|
91
104
|
|
92
105
|
def play_random_song
|
93
|
-
p "Inside play_random_song"
|
94
106
|
play_song(random_song)
|
95
107
|
end
|
96
108
|
|
97
109
|
def enable_continuous_play
|
98
110
|
self.continuous_play = true
|
99
|
-
p "Enabled continuous_play"
|
100
111
|
end
|
101
112
|
|
102
113
|
def disable_continuous_play
|
103
114
|
self.continuous_play = false
|
104
|
-
p "Disabled continuous_play"
|
105
115
|
end
|
106
116
|
|
107
117
|
def play_song(song)
|
108
|
-
terminate_current_song if current_song
|
109
118
|
self.playing = true
|
110
|
-
|
119
|
+
terminate_current_song(play_another: false) if current_song
|
111
120
|
self.current_song = song
|
121
|
+
self.songs_played << song
|
112
122
|
current_song.play(user_config, self)
|
113
123
|
p "Finished playing"
|
114
|
-
|
124
|
+
p "Songs played: " + songs_played.to_s
|
115
125
|
self.current_song = nil
|
116
126
|
self.playing = false
|
117
127
|
rescue Song::SongTerminatedPrematurelyException
|
118
128
|
p "Song ended prematurely"
|
119
129
|
end
|
120
130
|
|
121
|
-
def terminate_current_song
|
131
|
+
def terminate_current_song(opts=nil)
|
132
|
+
# By default, stops song and lets a new song play automatically
|
133
|
+
# To prevent another song from playing automatically, pass "play_another: false"
|
122
134
|
if current_song
|
123
135
|
p "Terminating #{current_song.music_file}"
|
124
136
|
current_song.terminate
|
125
|
-
self.previous_song = current_song
|
126
137
|
self.current_song = nil
|
127
|
-
self.playing = false
|
138
|
+
self.playing = (opts && opts[:play_another]) ? !play_another : false
|
128
139
|
else
|
129
140
|
raise NoCurrentSongException, "No current_song"
|
130
141
|
end
|
data/lib/jimmy_jukebox/song.rb
CHANGED
@@ -37,16 +37,15 @@ module JimmyJukebox
|
|
37
37
|
gpid == 0 ? nil : gpid
|
38
38
|
end
|
39
39
|
|
40
|
+
def process_group_id
|
41
|
+
Process.getpgid(playing_pid)
|
42
|
+
end
|
43
|
+
|
40
44
|
def pause
|
41
45
|
self.paused = true
|
42
|
-
# jruby doesn't seem to handle system() correctly
|
43
|
-
# trying backticks
|
44
|
-
# system("kill -s STOP #{playing_pid}") if playing_pid
|
45
46
|
if grandchild_pid
|
46
|
-
p "Pausing"
|
47
47
|
`kill -s STOP #{grandchild_pid}`
|
48
48
|
elsif playing_pid
|
49
|
-
p "Pausing"
|
50
49
|
`kill -s STOP #{playing_pid}`
|
51
50
|
else
|
52
51
|
raise NoPlayingPidException, "*** Can't pause song because can't find playing_pid #{playing_pid} ***"
|
@@ -55,14 +54,9 @@ module JimmyJukebox
|
|
55
54
|
|
56
55
|
def unpause
|
57
56
|
self.paused = false
|
58
|
-
# jruby doesn't seem to handle system() correctly
|
59
|
-
# trying backticks
|
60
|
-
#system("kill -s CONT #{playing_pid}") if playing_pid
|
61
57
|
if grandchild_pid
|
62
|
-
p "Unpausing"
|
63
58
|
`kill -s CONT #{grandchild_pid}`
|
64
59
|
elsif playing_pid
|
65
|
-
p "Unpausing"
|
66
60
|
`kill -s CONT #{playing_pid}`
|
67
61
|
else
|
68
62
|
raise NoPlayingPidException, "*** Can't unpause song because can't find playing_pid #{playing_pid} ***"
|
@@ -77,17 +71,11 @@ module JimmyJukebox
|
|
77
71
|
p "killed #{grandpid}"
|
78
72
|
end
|
79
73
|
`kill #{playpid}`
|
80
|
-
p "killed #{playpid}"
|
81
74
|
end
|
82
75
|
|
83
76
|
def terminate
|
84
77
|
self.paused = false
|
85
|
-
#`killall #{player}`
|
86
78
|
self.player = nil
|
87
|
-
# killing processes seems problematic in JRuby
|
88
|
-
# I've tried several approaches, and nothing seems reliable
|
89
|
-
#Process.kill("SIGKILL",playing_pid) if playing_pid
|
90
|
-
#Process.kill("SIGTERM",playing_pid) if playing_pid
|
91
79
|
if playing_pid
|
92
80
|
kill_playing_pid_and_children
|
93
81
|
self.playing_pid = nil
|
@@ -118,38 +106,24 @@ module JimmyJukebox
|
|
118
106
|
music_file_path = File.expand_path(music_file)
|
119
107
|
run_command(player, music_file_path)
|
120
108
|
p "playing_pid = " + playing_pid.to_s
|
121
|
-
#
|
122
|
-
# self.playing_pid = pid
|
123
|
-
#end
|
124
|
-
#if running_jruby?
|
125
|
-
Process.waitpid(playing_pid) # Waits for a child process to exit, returns its process id, and sets $? to a Process::Status object
|
126
|
-
#else
|
127
|
-
# Process::waitpid(playing_pid)
|
128
|
-
#end
|
129
|
-
p "Stopped waiting"
|
109
|
+
Process.waitpid(playing_pid) # Waits for a child process to exit, returns its process id, and sets $? to a Process::Status object
|
130
110
|
$? # return Process::Status object with instance methods .stopped?, .exited?, .exitstatus
|
131
111
|
end
|
132
112
|
|
133
113
|
end
|
134
114
|
|
135
|
-
def running_jruby?
|
136
|
-
defined?(JRUBY_VERSION) || RUBY_ENGINE == 'jruby' || RUBY_PLATFORM == 'java'
|
137
|
-
end
|
138
|
-
|
139
115
|
def run_command(command, arg)
|
140
|
-
|
116
|
+
# make system call and get pid so you can pause/terminate process
|
117
|
+
if $running_jruby
|
141
118
|
pid = Spoon.spawnp(command,arg)
|
142
119
|
else
|
143
120
|
begin
|
144
121
|
pid = POSIX::Spawn::spawn(command + ' ' + arg)
|
145
|
-
#pgid = Process.getpgid(pid)
|
146
|
-
#child = POSIX::Spawn::Child.new(command + ' ' + arg)
|
147
|
-
#pid = child.status.pid
|
148
122
|
|
149
|
-
#
|
123
|
+
# posix/spawn is much faster than fork-exec
|
150
124
|
#pid = Process.fork do
|
151
|
-
# exec(command + ' ' + arg)
|
152
|
-
# exit! 127 #
|
125
|
+
# exec(command + ' ' + arg)
|
126
|
+
# exit! 127 # should never be reached
|
153
127
|
#end
|
154
128
|
rescue NotImplementedError
|
155
129
|
raise CannotSpawnProcessException, "*** Cannot play music because we found neither Spoon.spawnp (for JRuby) nor Process.fork (for MRI) ***"
|
@@ -158,26 +132,4 @@ module JimmyJukebox
|
|
158
132
|
self.playing_pid = pid
|
159
133
|
end
|
160
134
|
|
161
|
-
# make system call and get pid so you can terminate process
|
162
|
-
def system_yield_pid(command,arg)
|
163
|
-
# would like to use Process.respond_to?(:fork) but JRuby mistakenly returns true
|
164
|
-
if running_jruby?
|
165
|
-
pid = Spoon.spawnp(command,arg)
|
166
|
-
else
|
167
|
-
begin
|
168
|
-
#spawn(command + ' ' + arg)
|
169
|
-
#pid = POSIX::Spawn::spawn(command + ' ' + arg)
|
170
|
-
|
171
|
-
# create and run block in subprocess (which will terminate with status 0), capture subprocess pid
|
172
|
-
pid = Process.fork do
|
173
|
-
exec(command + ' ' + arg) # replace new process with system call
|
174
|
-
exit! 127 # exit process and return exit status 127; should never be reached
|
175
|
-
end
|
176
|
-
rescue NotImplementedError
|
177
|
-
raise CannotSpawnProcessException, "*** Cannot play music because we found neither Spoon.spawnp (for JRuby) nor Process.fork (for MRI) ***"
|
178
|
-
end
|
179
|
-
end
|
180
|
-
yield pid if block_given? # call block, passing in the subprocess pid
|
181
|
-
end
|
182
|
-
|
183
135
|
end
|
@@ -51,7 +51,7 @@ user_input_thread = Thread.new do
|
|
51
51
|
rescue Interrupt, SystemExit => e
|
52
52
|
p "JimmyJukebox closed by user request. Bye!"
|
53
53
|
jj.quit
|
54
|
-
system('stty', stty_save) unless
|
54
|
+
system('stty', stty_save) unless $running_jruby # Restore original terminal state
|
55
55
|
exit
|
56
56
|
end
|
57
57
|
end
|