class Plllayer
Plllayer
provides an interface to an external media player, such as mplayer. It contains a playlist of tracks, which may be as simple as an Array of paths to some audio files. You can then control the playback of this playlist by calling various command-like methods, like play, pause, seek, skip, shuffle, and so on.
Constants
- FileNotFoundError
Raise this exception when the file at the given track path doesn’t exist.
- InvalidAudioFileError
Raise this one when the file can’t be played for whatever reason
- SINGLE_PLAYERS
Attributes
Public Class Methods
Create a new Plllayer
. Optionally, you can pass in an initial playlist to be loaded (won’t start playing until you call play
). You can also pass the :external_player option to specify the preferred external player to use. Otherwise, it will try to figure out what external players are available, and attempt to use the best one available.
However, only mplayer is supported at the moment, so this option isn’t useful right now.
TODO: check if external player is available before trying to use it.
# File lib/plllayer.rb, line 36 def initialize(*args) options = {} options = args.pop if args.last.is_a? Hash options[:external_player] ||= :mplayer # Look up the single player class, raise error if it doesn't exist. single_player_class = SINGLE_PLAYERS[options[:external_player].to_sym] if single_player_class.nil? raise NotImplementedError, "external player #{options[:external_player]} not supported" end @single_player = single_player_class.new @playlist = [] append(args.first) unless args.empty? @index = nil @paused = false @playing = false @repeat_mode = nil @index_mutex = Mutex.new end
Public Instance Methods
Append tracks to the playlist. Can be done while the playlist is playing. A track is either a String containing the path to the audio file, or an object with a location method that returns the path to the audio file. An ArgumentError is raised when you try to pass a non-track.
This method is aliased as the << operator.
# File lib/plllayer.rb, line 64 def append(*tracks) tracks = tracks.flatten tracks.each do |track| if !track.is_a?(String) && !track.respond_to?(:location) raise ArgumentError, "a #{track.class} is not a track (try adding a #location method)" end end @playlist += tracks @playlist.dup end
Play the previous track. Pass a number to go back that many tracks. Treats the playlist like a circular array if the repeat mode is :all.
# File lib/plllayer.rb, line 270 def back(n = 1) change_track(-n) end
Stop playback and empty the playlist.
# File lib/plllayer.rb, line 170 def clear stop @playlist.clear true end
Returns the current position of the currently-playing track, as a String like “1:23”.
# File lib/plllayer.rb, line 392 def formatted_position(options = {}) Plllayer.format_time(position, options) end
Returns the length of the currently-playing track, as a String like “1:23”.
# File lib/plllayer.rb, line 406 def formatted_track_length(options = {}) if length = track_length Plllayer.format_time(length, options) end end
Insert one or more tracks in the playlist, right after the currently-playing track. If no track is playing, insert to the head of the playlist.
# File lib/plllayer.rb, line 79 def insert(*tracks) after = @index || -1 _unsynchronized_insert_at(after + 1, *tracks) @playlist.dup end
Insert one or more tracks anywhere in the playlist. A negative index may be given to refer to the end of the playlist.
# File lib/plllayer.rb, line 88 def insert_at(index, *tracks) index = @playlist.length + index + 1 if index < 0 unless (0..@playlist.length).include? index raise IndexError, "index is out of range" end @playlist.insert(index, *tracks) @index += tracks.length if @index && index < @index @playlist.dup end
Mute the volume.
# File lib/plllayer.rb, line 334 def mute @single_player.mute end
Check if volume is muted.
# File lib/plllayer.rb, line 344 def muted? @single_player.muted? end
Pause playback.
# File lib/plllayer.rb, line 218 def pause if playing? && !paused? @paused = true @single_player.pause else false end end
Check if playback is paused.
# File lib/plllayer.rb, line 177 def paused? @paused end
Start playing the playlist from beginning to end.
# File lib/plllayer.rb, line 190 def play if !@playlist.empty? && !playing? @playing = true @paused = false @index = 0 play_track true else false end end
Returns a copy of the playlist.
# File lib/plllayer.rb, line 143 def playlist @playlist.dup end
Returns the current position of the currently-playing track, in milliseconds.
# File lib/plllayer.rb, line 386 def position @single_player.position || 0 end
Remove one or more tracks from the playlist by value. If the currently-playing track is removed, it will start playing the next track in the playlist.
# File lib/plllayer.rb, line 101 def remove(*tracks) n = nil n = tracks.pop if tracks.last.is_a?(Fixnum) index = 0 current_track_removed = false @playlist = @playlist.inject([]) do |playlist, track| if tracks.include?(track) && (n.nil? || n > 0) n &&= n - 1 @index -= 1 if @index && index < @index current_track_removed = true if index == @index else playlist << track end index += 1 playlist end _unsynchronized_change_track(0) if current_track_removed @playlist.dup end
Remove one or more tracks from the playlist at a particular index. A negative index may be given to refer to the end of the playlist. If the currently-playing track is removed, it will start playing the next track in the playlist.
# File lib/plllayer.rb, line 125 def remove_at(index, n = 1) index = @playlist.length + index if index < 0 if @playlist.empty? || index < 0 || index + n > @playlist.length raise IndexError, "index is out of range" end @playlist.slice!(index, n) if @index && @index > index if @index < index + n _unsynchronized_change_track(index - @index) else @index -= n end end @playlist.dup end
Set the repeat behaviour of the playlist. There are three possible values:
:one repeat a single track over and over :all repeat the whole playlist, treating it like a circular array :off play songs consecutively, stop playback when done
The default, of course, is :off.
# File lib/plllayer.rb, line 249 def repeat(one_or_all_or_off) case one_or_all_or_off when :one @repeat_mode = :one when :all @repeat_mode = :all when :off @repeat_mode = nil else raise ArgumentError end true end
Play the currently-playing track from the beginning.
# File lib/plllayer.rb, line 264 def restart change_track(0) end
Resume playback.
# File lib/plllayer.rb, line 228 def resume if playing? && paused? @paused = false if @single_player.playing? @single_player.resume else play_track @track true end else false end end
Seek to a particular position within the currently-playing track. There are multiple ways to specify where to seek to:
seek 10000 # seek 10000 milliseconds forward, relative to the current position seek -5000 # seek 5000 milliseconds backward seek "3:45" # seek to the absolute position 3 minutes and 45 seconds seek "1:03:45.123" # seek to 1 hour, 3 minutes, 45 seconds, 123 milliseconds seek 3..45 # syntax sugar for seeking to "3:45" seek 3..45.123 # syntax sugar for seeking to "3:45.123" seek abs: 150000 # seek to the absolute position 150000 milliseconds seek percent: 80 # seek to 80% of the way through the track
# File lib/plllayer.rb, line 292 def seek(where) if paused? && !@single_player.playing? resume pause end case where when Integer @single_player.seek(where, :relative) when Range seconds = where.begin * 60 + where.end @single_player.seek(seconds * 1000, :absolute) when String @single_player.seek(Plllayer.parse_time(where), :absolute) when Hash if where[:abs] if where[:abs].is_a? Integer @single_player.seek(where[:abs], :absolute) else seek(where[:abs]) end elsif where[:percent] @single_player.seek(where[:percent], :percent) end else raise ArgumentError, "seek doesn't take a #{where.class}" end end
Shuffle the playlist. If this is done while the playlist is playing, the current song will go to the top of the playlist and the rest of the songs will be shuffled.
# File lib/plllayer.rb, line 361 def shuffle current_track = track @playlist.shuffle! if playing? index = @playlist.index(current_track) @playlist[0], @playlist[index] = @playlist[index], @playlist[0] @index = 0 end true end
Play the next track. Pass a number to go forward that many tracks. Treats the playlist like a circular array if the repeat mode is :all.
# File lib/plllayer.rb, line 276 def skip(n = 1) change_track(n) end
Sorts the playlist. Delegates to Array#sort, so a block may be passed to specify what the tracks should be sorted by.
This method is safe to call while the playlist is playing.
# File lib/plllayer.rb, line 376 def sort(&by) current_track = track @playlist.sort! &by if playing? @index = @playlist.index(current_track) end true end
Get the playback speed as a Float. 1.0 is normal speed, 2.0 is double speed, 0.5 is half-speed, and so on.
# File lib/plllayer.rb, line 323 def speed @single_player.speed || 1.0 end
Set the playback speed as a Float. 1.0 is normal speed, 2.0 is double speed, 0.5 is half-speed, and so on.
# File lib/plllayer.rb, line 329 def speed=(new_speed) @single_player.speed = new_speed end
Stop playback.
# File lib/plllayer.rb, line 203 def stop if playing? @single_player.stop @track = nil @index = nil @paused = false @playing = false true else false end end
Get the currently-playing track, or the track that’s about to play if you’re paused between tracks. Returns nil if playback is stopped.
# File lib/plllayer.rb, line 149 def track @index ? @playlist[@index] : nil end
Get the index of the currently-playing track, or of the track that’s about to play if you’re paused between tracks. Returns nil if playback is stopped.
# File lib/plllayer.rb, line 156 def track_index @index end
Returns the length of the currently-playing track, in milliseconds.
# File lib/plllayer.rb, line 397 def track_length if paused? && !@single_player.playing? resume pause end @single_player.track_length end
Get the path to the audio file of the currently-playing track.
# File lib/plllayer.rb, line 161 def track_path if track.respond_to? :location track.location else track end end
Unmute the volume.
# File lib/plllayer.rb, line 339 def unmute @single_player.unmute end
Get the volume, as a percentage.
# File lib/plllayer.rb, line 349 def volume @single_player.volume end
Set the volume, as a percentage.
# File lib/plllayer.rb, line 354 def volume=(new_volume) @single_player.volume = new_volume end
Private Instance Methods
# File lib/plllayer.rb, line 414 def change_track(by = 1, options = {}) if playing? @index += by if @repeat_mode case @repeat_mode when :one @index -= by if options[:auto] when :all @index %= @playlist.length end end if track && @index >= 0 @single_player.stop if paused? && @single_player.active? play_track if not paused? else _unsynchronized_stop end true else false end end
# File lib/plllayer.rb, line 438 def play_track @single_player.play(track_path) do change_track(1, auto: true) ActiveRecord::Base.connection.close if defined?(ActiveRecord) end @paused = false true end