Class: JimmyJukebox::Song

Inherits:
Object show all
Includes:
DisplayOptions
Defined in:
lib/jimmy_jukebox/song.rb

Defined Under Namespace

Classes: CannotSpawnProcessException, InvalidSongFormatException, NoPlayingPidException, SongTerminatedPrematurelyException, UnsupportedSongFormatException

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DisplayOptions

#display_options, #display_options_after_delay

Constructor Details

#initialize(in_music_file) ⇒ Song

Returns a new instance of Song.



19
20
21
22
23
# File 'lib/jimmy_jukebox/song.rb', line 19

def initialize(in_music_file)
  self.music_file = in_music_file
  self.paused = false
  self.playing_pid = nil
end

Instance Attribute Details

#music_fileObject

Returns the value of attribute music_file.



15
16
17
# File 'lib/jimmy_jukebox/song.rb', line 15

def music_file
  @music_file
end

#paused=(value) ⇒ Object (writeonly)

Sets the attribute paused

Parameters:

  • value

    the value to set the attribute paused to.



16
17
18
# File 'lib/jimmy_jukebox/song.rb', line 16

def paused=(value)
  @paused = value
end

#playerObject

Returns the value of attribute player.



17
18
19
# File 'lib/jimmy_jukebox/song.rb', line 17

def player
  @player
end

#playing_pidObject

Returns the value of attribute playing_pid.



17
18
19
# File 'lib/jimmy_jukebox/song.rb', line 17

def playing_pid
  @playing_pid
end

Instance Method Details

#<=>(other) ⇒ Object



29
30
31
# File 'lib/jimmy_jukebox/song.rb', line 29

def <=>(other)
  music_file <=> other.music_file
end

#grandchild_pidObject



45
46
47
48
49
50
# File 'lib/jimmy_jukebox/song.rb', line 45

def grandchild_pid
  # returns grandchild's pid if the child process spawns a grandchild
  # if so, the child is probably "/bin/sh" and the grandchild is "mpg123" or similar
  gpid = JimmyJukebox::RUNNING_LINUX && `ps --ppid #{playing_pid} -o pid`.strip.to_i
  gpid == 0 ? nil : gpid
end

#kill_playing_pid_and_childrenObject



82
83
84
85
86
87
# File 'lib/jimmy_jukebox/song.rb', line 82

def kill_playing_pid_and_children
  return nil unless playpid = playing_pid
  grandpid = grandchild_pid
  `kill #{grandpid}` if grandpid
  `kill #{playpid}`
end

#pauseObject



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/jimmy_jukebox/song.rb', line 56

def pause
  self.paused = true
  if playing_pid
    if grandchild_pid
      `kill -s STOP #{grandchild_pid}`
    else
      `kill -s STOP #{playing_pid}`
    end
  else
    raise NoPlayingPidException, "*** Can't pause song because can't find playing_pid #{playing_pid} ***"
  end
end

#paused?Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/jimmy_jukebox/song.rb', line 25

def paused?
  @paused
end

#play(user_config, jukebox) ⇒ Object



125
126
127
128
129
# File 'lib/jimmy_jukebox/song.rb', line 125

def play(user_config, jukebox)
  set_player(user_config)
  process_status = play_with_player
  process_status.exitstatus.to_i == 0 ? (self.playing_pid = nil) : (raise SongTerminatedPrematurelyException, "Experienced a problem playing a song")
end

#play_with_playerObject



131
132
133
134
135
136
137
138
# File 'lib/jimmy_jukebox/song.rb', line 131

def play_with_player
  escaped_music_file = escape_characters_in_string(music_file)
  run_command(player, escaped_music_file)
  puts "Now playing '#{music_file}'"
  display_options_after_delay
  Process.waitpid(playing_pid) # Waits for a child process to exit, returns its process id, and sets $? to a Process::Status object
  $? # return Process::Status object with instance methods .stopped?, .exited?, .exitstatus
end

#process_group_idObject



52
53
54
# File 'lib/jimmy_jukebox/song.rb', line 52

def process_group_id
  Process.getpgid(playing_pid)
end

#set_player(user_config) ⇒ Object



100
101
102
103
104
105
106
# File 'lib/jimmy_jukebox/song.rb', line 100

def set_player(user_config)
  if regex = AUDIO_FORMATS.keys.detect { |re| music_file =~ re }
    self.player = user_config.send(AUDIO_FORMATS[regex] + '_player')
  else
    raise UnsupportedSongFormatException, "Attempted to play a file format this program cannot play"
  end
end

#spawn_methodObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/jimmy_jukebox/song.rb', line 108

def spawn_method
  if JimmyJukebox::RUNNING_JRUBY
    lambda { |command, arg| Spoon.spawnp(command, arg) }
  else
    begin
      lambda { |command, arg| POSIX::Spawn::spawn(command + ' ' + arg) }

      # posix/spawn is much faster than fork-exec
      #pid = Process.fork do
      #  exec(command + ' ' + arg)
      #end
    rescue NotImplementedError
      raise CannotSpawnProcessException, "*** Cannot play music because we found neither Spoon.spawnp (for JRuby) nor Process.fork (for MRI) ***"
    end
  end
end

#terminateObject



89
90
91
92
93
94
95
96
97
98
# File 'lib/jimmy_jukebox/song.rb', line 89

def terminate
  self.paused = false
  self.player = nil
  if playing_pid
    kill_playing_pid_and_children
    self.playing_pid = nil
  else
    raise NoPlayingPidException, "*** Can't terminate song because can't find playing_pid #{playing_pid} ***"
  end
end

#unpauseObject



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/jimmy_jukebox/song.rb', line 69

def unpause
  self.paused = false
  if playing_pid
    if grandchild_pid
      `kill -s CONT #{grandchild_pid}`
    else playing_pid
      `kill -s CONT #{playing_pid}`
    end
  else
    raise NoPlayingPidException, "*** Can't unpause song because can't find playing_pid #{playing_pid} ***"
  end
end

#valid_audio_format?(music_file) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/jimmy_jukebox/song.rb', line 33

def valid_audio_format?(music_file)
  AUDIO_FORMATS.keys.any? { |re| re =~ music_file }
end