listlace 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +0 -0
- data/Gemfile.lock +9 -33
- data/LICENSE +1 -1
- data/README.md +10 -32
- data/bin/listlace +12 -13
- data/lib/listlace/commands.rb +18 -0
- data/lib/listlace.rb +12 -28
- data/listlace.gemspec +7 -8
- metadata +22 -127
- data/README.old +0 -86
- data/lib/listlace/commands/library_commands.rb +0 -90
- data/lib/listlace/commands/player_commands.rb +0 -194
- data/lib/listlace/core_ext/array.rb +0 -58
- data/lib/listlace/library/database.rb +0 -84
- data/lib/listlace/library/selectors.rb +0 -199
- data/lib/listlace/library.rb +0 -160
- data/lib/listlace/models/playlist.rb +0 -6
- data/lib/listlace/models/playlist_item.rb +0 -6
- data/lib/listlace/models/track.rb +0 -21
- data/lib/listlace/player.rb +0 -249
- data/lib/listlace/simple_track.rb +0 -13
- data/lib/listlace/single_player.rb +0 -129
- data/lib/listlace/single_players/mplayer.rb +0 -255
- data/lib/listlace/time_helper.rb +0 -34
@@ -1,21 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
class Track < ActiveRecord::Base
|
3
|
-
has_many :playlist_items
|
4
|
-
has_many :playlists, through: :playlist_items
|
5
|
-
|
6
|
-
validates :location, presence: true, uniqueness: true
|
7
|
-
|
8
|
-
before_create { |track| track.date_added = Time.now }
|
9
|
-
before_save { |track| track.date_modified = Time.now }
|
10
|
-
|
11
|
-
def increment_skip_count
|
12
|
-
increment! :skip_count
|
13
|
-
update_column :skip_date, Time.now
|
14
|
-
end
|
15
|
-
|
16
|
-
def increment_play_count
|
17
|
-
increment! :play_count
|
18
|
-
update_column :play_date, Time.now
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/listlace/player.rb
DELETED
@@ -1,249 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
# This is the music box. It plays playlists. It contains a queue, which is
|
3
|
-
# just a playlist. To tell it what to play, you add one or more playlists to
|
4
|
-
# the queue, then start playing using the start method.
|
5
|
-
#
|
6
|
-
# Playback commands like pause, resume, seek, and so on are delegated to the
|
7
|
-
# SinglePlayer, which takes care of playing each individual song.
|
8
|
-
#
|
9
|
-
# Each method that performs an action is like a button on a physical media
|
10
|
-
# player: you can press the buttons even if they aren't applicable to the
|
11
|
-
# current state of the player. If that's the case, the methods wil return
|
12
|
-
# false. Otherwise, they'll return a truthy value.
|
13
|
-
class Player
|
14
|
-
DEFAULT_SINGLE_PLAYER = SinglePlayers::MPlayer
|
15
|
-
|
16
|
-
attr_reader :current_track, :current_track_index, :repeat_mode
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
@single_player = DEFAULT_SINGLE_PLAYER.new
|
20
|
-
@queue = []
|
21
|
-
@current_track = nil
|
22
|
-
@current_track_index = nil
|
23
|
-
@playlist_paused = false
|
24
|
-
@started = false
|
25
|
-
@repeat_mode = false
|
26
|
-
end
|
27
|
-
|
28
|
-
def queue(playlist = nil)
|
29
|
-
if playlist.is_a? Array
|
30
|
-
playlist = playlist.dup
|
31
|
-
playlist.map! { |track| track.is_a?(String) ? SimpleTrack.new(track) : track }
|
32
|
-
playlist.select! { |track| track.respond_to? :location }
|
33
|
-
if @queue.empty?
|
34
|
-
@queue = playlist
|
35
|
-
else
|
36
|
-
@queue += playlist
|
37
|
-
end
|
38
|
-
end
|
39
|
-
@queue.dup
|
40
|
-
end
|
41
|
-
|
42
|
-
def clear
|
43
|
-
stop
|
44
|
-
@queue.clear
|
45
|
-
true
|
46
|
-
end
|
47
|
-
|
48
|
-
def empty?
|
49
|
-
@queue.empty?
|
50
|
-
end
|
51
|
-
|
52
|
-
def playlist_paused?
|
53
|
-
@playlist_paused
|
54
|
-
end
|
55
|
-
|
56
|
-
def paused?
|
57
|
-
playlist_paused? or @single_player.paused?
|
58
|
-
end
|
59
|
-
|
60
|
-
def started?
|
61
|
-
@started
|
62
|
-
end
|
63
|
-
|
64
|
-
def start
|
65
|
-
unless empty?
|
66
|
-
@started = true
|
67
|
-
@playlist_paused = false
|
68
|
-
@current_track = @queue.first
|
69
|
-
@current_track_index = 0
|
70
|
-
play_track @current_track
|
71
|
-
true
|
72
|
-
else
|
73
|
-
false
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def stop
|
78
|
-
@single_player.stop
|
79
|
-
@current_track = nil
|
80
|
-
@current_track_index = nil
|
81
|
-
@playlist_paused = false
|
82
|
-
@started = false
|
83
|
-
true
|
84
|
-
end
|
85
|
-
|
86
|
-
def pause
|
87
|
-
@single_player.pause
|
88
|
-
end
|
89
|
-
|
90
|
-
def resume
|
91
|
-
if playlist_paused?
|
92
|
-
play_track @current_track
|
93
|
-
@playlist_paused = false
|
94
|
-
true
|
95
|
-
else
|
96
|
-
@single_player.resume
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def repeat(one_or_all_or_off)
|
101
|
-
case one_or_all_or_off
|
102
|
-
when :one
|
103
|
-
@repeat_mode = :one
|
104
|
-
when :all
|
105
|
-
@repeat_mode = :all
|
106
|
-
when :off
|
107
|
-
@repeat_mode = false
|
108
|
-
end
|
109
|
-
true
|
110
|
-
end
|
111
|
-
|
112
|
-
def restart
|
113
|
-
change_track(0)
|
114
|
-
end
|
115
|
-
|
116
|
-
def back(n = 1)
|
117
|
-
change_track(-n)
|
118
|
-
end
|
119
|
-
|
120
|
-
def skip(n = 1)
|
121
|
-
if @current_track.respond_to?(:increment_skip_count)
|
122
|
-
@current_track.increment_skip_count
|
123
|
-
end
|
124
|
-
change_track(n)
|
125
|
-
end
|
126
|
-
|
127
|
-
def seek(where)
|
128
|
-
if playlist_paused?
|
129
|
-
resume
|
130
|
-
pause
|
131
|
-
seek where
|
132
|
-
else
|
133
|
-
case where
|
134
|
-
when Integer
|
135
|
-
@single_player.seek(where, :relative)
|
136
|
-
when Range
|
137
|
-
seconds = where.begin * 60 + where.end
|
138
|
-
@single_player.seek(seconds * 1000, :absolute)
|
139
|
-
when String
|
140
|
-
@single_player.seek(Listlace.parse_time(where), :absolute)
|
141
|
-
when Hash
|
142
|
-
if where[:abs]
|
143
|
-
if where[:abs].is_a? Integer
|
144
|
-
@single_player.seek(where[:abs], :absolute)
|
145
|
-
else
|
146
|
-
seek(where[:abs])
|
147
|
-
end
|
148
|
-
elsif where[:percent]
|
149
|
-
@single_player.seek(where[:percent], :percent)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def speed
|
156
|
-
@single_player.speed || 1.0
|
157
|
-
end
|
158
|
-
|
159
|
-
def speed=(new_speed)
|
160
|
-
@single_player.speed = new_speed
|
161
|
-
end
|
162
|
-
|
163
|
-
def mute
|
164
|
-
@single_player.mute
|
165
|
-
end
|
166
|
-
|
167
|
-
def unmute
|
168
|
-
@single_player.unmute
|
169
|
-
end
|
170
|
-
|
171
|
-
def volume
|
172
|
-
@single_player.volume
|
173
|
-
end
|
174
|
-
|
175
|
-
def volume=(new_volume)
|
176
|
-
@single_player.volume = new_volume
|
177
|
-
end
|
178
|
-
|
179
|
-
def shuffle
|
180
|
-
if started?
|
181
|
-
@queue.shuffle_except! @current_track
|
182
|
-
@current_track_index = 0
|
183
|
-
else
|
184
|
-
@queue.shuffle!
|
185
|
-
end
|
186
|
-
true
|
187
|
-
end
|
188
|
-
|
189
|
-
def sort(&by)
|
190
|
-
@queue.sort! &by
|
191
|
-
if started?
|
192
|
-
@current_track_index = @queue.index(@current_track)
|
193
|
-
end
|
194
|
-
true
|
195
|
-
end
|
196
|
-
|
197
|
-
def current_time
|
198
|
-
@single_player.current_time || 0
|
199
|
-
end
|
200
|
-
|
201
|
-
def total_time
|
202
|
-
@single_player.total_time
|
203
|
-
end
|
204
|
-
|
205
|
-
private
|
206
|
-
|
207
|
-
def change_track(by = 1, options = {})
|
208
|
-
if started?
|
209
|
-
if options[:auto] && @current_track.respond_to?(:increment_play_count)
|
210
|
-
@current_track.increment_play_count
|
211
|
-
end
|
212
|
-
@current_track_index += by
|
213
|
-
if options[:auto] && @repeat_mode
|
214
|
-
case @repeat_mode
|
215
|
-
when :one
|
216
|
-
@current_track_index -= by
|
217
|
-
when :all
|
218
|
-
if @current_track_index >= @queue.length
|
219
|
-
@current_track_index = 0
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
223
|
-
@current_track = @queue[@current_track_index]
|
224
|
-
if @current_track && @current_track_index >= 0
|
225
|
-
if @single_player.paused?
|
226
|
-
@single_player.stop
|
227
|
-
@playlist_paused = true
|
228
|
-
elsif not playlist_paused?
|
229
|
-
play_track @current_track
|
230
|
-
end
|
231
|
-
else
|
232
|
-
stop
|
233
|
-
end
|
234
|
-
true
|
235
|
-
else
|
236
|
-
false
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def play_track(track)
|
241
|
-
@single_player.play(track) do
|
242
|
-
change_track(1, auto: true)
|
243
|
-
ActiveRecord::Base.connection.close if defined?(ActiveRecord)
|
244
|
-
end
|
245
|
-
@playlist_paused = false
|
246
|
-
true
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
# The bare minimum needed to represent a track. This is used by the Player and
|
3
|
-
# SinglePlayer, in case they get passed a String containing a path to an audio
|
4
|
-
# file. That way, users don't have to worry about creating track objects, if
|
5
|
-
# they want.
|
6
|
-
class SimpleTrack
|
7
|
-
attr_accessor :location
|
8
|
-
|
9
|
-
def initialize(location = nil)
|
10
|
-
@location = location
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
# A SinglePlayer takes care of playing a single track, and controlling the
|
3
|
-
# playback with commands like pause, resume, seek, and so on. It typically
|
4
|
-
# starts an external audio player process to do this job. This class is an
|
5
|
-
# interface that is to be implemented for different audio players. Then the
|
6
|
-
# user can choose which SinglePlayer to use based on what audio players they
|
7
|
-
# have installed.
|
8
|
-
#
|
9
|
-
# All methods that perform an action should return false if the action isn't
|
10
|
-
# applicable, and return a truthy value otherwise.
|
11
|
-
class SinglePlayer
|
12
|
-
# Returns true if a track is currently loaded, i.e. either playing or
|
13
|
-
# paused.
|
14
|
-
def active?
|
15
|
-
raise NotImplementedError
|
16
|
-
end
|
17
|
-
|
18
|
-
# Returns true if a track is loaded and is paused.
|
19
|
-
def paused?
|
20
|
-
raise NotImplementedError
|
21
|
-
end
|
22
|
-
|
23
|
-
# Get the current track object which was passed to the play method.
|
24
|
-
def track
|
25
|
-
raise NotImplementedError
|
26
|
-
end
|
27
|
-
|
28
|
-
# Get the title of the current track. First tries to call the title method on
|
29
|
-
# the current track, if that doesn't work it tries to get the title from the
|
30
|
-
# metadata of the audio file, and if that doesn't work it uses the filename.
|
31
|
-
def track_title
|
32
|
-
raise NotImplementedError
|
33
|
-
end
|
34
|
-
|
35
|
-
# Begin playing a track. The track should be either a String representing a
|
36
|
-
# path to an audio file, or an object responding to #location. The &on_end
|
37
|
-
# callback will be called when the track is finished playing.
|
38
|
-
def play(track, &on_end)
|
39
|
-
raise NotImplementedError
|
40
|
-
end
|
41
|
-
|
42
|
-
# Stop playback. Typically quits the external audio player process.
|
43
|
-
def stop
|
44
|
-
raise NotImplementedError
|
45
|
-
end
|
46
|
-
|
47
|
-
# Pauses playback.
|
48
|
-
def pause
|
49
|
-
raise NotImplementedError
|
50
|
-
end
|
51
|
-
|
52
|
-
# Resumes playback.
|
53
|
-
def resume
|
54
|
-
raise NotImplementedError
|
55
|
-
end
|
56
|
-
|
57
|
-
# Seek to a particular position in the track. Different types can be
|
58
|
-
# supported, such as absolute, relative, or percent. All times are specified
|
59
|
-
# in milliseconds. A NotImplementedError is raised when a certain type isn't
|
60
|
-
# supported.
|
61
|
-
def seek(where, type = :absolute)
|
62
|
-
case type
|
63
|
-
when :absolute
|
64
|
-
raise NotImplementedError
|
65
|
-
when :relative
|
66
|
-
raise NotImplementedError
|
67
|
-
when :percent
|
68
|
-
raise NotImplementedError
|
69
|
-
else
|
70
|
-
raise NotImplementedError
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Gets the current playback speed. The speed is a multiplier. For example,
|
75
|
-
# double speed is 2 and half-speed is 0.5. Normal speed is 1.
|
76
|
-
def speed
|
77
|
-
raise NotImplementedError
|
78
|
-
end
|
79
|
-
|
80
|
-
# Sets the playback speed. The speed is a multiplier. For example, for
|
81
|
-
# double speed you'd set it to 2 and for half-speed you'd set it to 0.5. And
|
82
|
-
# for normal speed: 1.
|
83
|
-
def speed=(new_speed)
|
84
|
-
raise NotImplementedError
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns true if audio is muted.
|
88
|
-
def muted?
|
89
|
-
raise NotImplementedError
|
90
|
-
end
|
91
|
-
|
92
|
-
# Mutes the audio player.
|
93
|
-
def mute
|
94
|
-
raise NotImplementedError
|
95
|
-
end
|
96
|
-
|
97
|
-
# Unmutes the audio player.
|
98
|
-
def unmute
|
99
|
-
raise NotImplementedError
|
100
|
-
end
|
101
|
-
|
102
|
-
# Get the current volume as a percentage.
|
103
|
-
def volume
|
104
|
-
raise NotImplementedError
|
105
|
-
end
|
106
|
-
|
107
|
-
# Set the volume as a percentage. The player is automatically unmuted.
|
108
|
-
def volume=(new_volume)
|
109
|
-
raise NotImplementedError
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns the current time into the song, in milliseconds.
|
113
|
-
def current_time
|
114
|
-
raise NotImplementedError
|
115
|
-
end
|
116
|
-
|
117
|
-
# Returns the length of the current track, in milliseconds.
|
118
|
-
def total_time
|
119
|
-
raise NotImplementedError
|
120
|
-
end
|
121
|
-
|
122
|
-
# Get metadata for the current track from the audio player. Returns a Hash
|
123
|
-
# with keys like :artist and :album. Values should be Strings, or nil if the
|
124
|
-
# value is blank.
|
125
|
-
def metadata
|
126
|
-
raise NotImplementedError
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
@@ -1,255 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
module SinglePlayers
|
3
|
-
# This is the SinglePlayer implementation for mplayer. It requires mplayer
|
4
|
-
# to be in your $PATH. It uses open4 to start up and communicate with the
|
5
|
-
# mplayer process, and mplayer's slave protocol to issue commands to mplayer.
|
6
|
-
class MPlayer < SinglePlayer
|
7
|
-
# Create a new MPlayer. The mplayer process is only started when the #play
|
8
|
-
# method is called to start playing a song. The process quits when the
|
9
|
-
# song ends. Even though a new process is started for each song, the
|
10
|
-
# MPlayer object keeps track of the volume, speed, and mute properties and
|
11
|
-
# sets these properties when a new song is played.
|
12
|
-
def initialize
|
13
|
-
@paused = false
|
14
|
-
@muted = false
|
15
|
-
@volume = 50
|
16
|
-
@speed = 1.0
|
17
|
-
@track = nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def active?
|
21
|
-
not @track.nil?
|
22
|
-
end
|
23
|
-
|
24
|
-
def paused?
|
25
|
-
@paused
|
26
|
-
end
|
27
|
-
|
28
|
-
def track
|
29
|
-
@track
|
30
|
-
end
|
31
|
-
|
32
|
-
def track_title
|
33
|
-
if active?
|
34
|
-
if @track.respond_to? :title
|
35
|
-
@track.title
|
36
|
-
elsif title = metadata[:title]
|
37
|
-
title
|
38
|
-
else
|
39
|
-
_command "get_file_name", expect_answer: /^ANS_FILENAME='(.+)'$/
|
40
|
-
end
|
41
|
-
else
|
42
|
-
false
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def play(track, &on_end)
|
47
|
-
# Make sure we're only playing one song at any one time.
|
48
|
-
_quit
|
49
|
-
|
50
|
-
# If a path to an audio file passed as the track, wrap it in a SimpleTrack.
|
51
|
-
track = SimpleTrack.new(track) if track.is_a? String
|
52
|
-
|
53
|
-
# The track object must respond to #location to be a track.
|
54
|
-
if not track.respond_to? :location
|
55
|
-
raise ArgumentError, "got a #{track.class} instead of a track"
|
56
|
-
end
|
57
|
-
|
58
|
-
if File.exists? track.location
|
59
|
-
# Run the mplayer process in slave mode, passing it the location of
|
60
|
-
# the track's audio file.
|
61
|
-
cmd = ["mplayer", "-slave", "-quiet", track.location]
|
62
|
-
@pid, @stdin, @stdout, @stderr = Open4.popen4(*cmd)
|
63
|
-
|
64
|
-
# This should skip past mplayer's initial lines of output so we can
|
65
|
-
# start reading its replies to our commands.
|
66
|
-
until @stdout.gets["playback"]
|
67
|
-
end
|
68
|
-
|
69
|
-
@paused = false
|
70
|
-
@track = track
|
71
|
-
|
72
|
-
# Persist the previous speed, volume, and mute properties into this
|
73
|
-
# process.
|
74
|
-
self.speed = @speed
|
75
|
-
self.volume = @volume
|
76
|
-
mute if @muted
|
77
|
-
|
78
|
-
# Start a thread that waits for the mplayer process to end, then calls
|
79
|
-
# the end of song callback. If the #quit method is called, this thread
|
80
|
-
# will be killed if it's still waiting for the process to end.
|
81
|
-
@quit_hook_active = false
|
82
|
-
@quit_hook = Thread.new do
|
83
|
-
Process.wait(@pid)
|
84
|
-
@quit_hook_active = true
|
85
|
-
@paused = false
|
86
|
-
@track = nil
|
87
|
-
on_end.call
|
88
|
-
end
|
89
|
-
|
90
|
-
true
|
91
|
-
else
|
92
|
-
false
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def stop
|
97
|
-
_quit
|
98
|
-
end
|
99
|
-
|
100
|
-
def pause
|
101
|
-
if not @paused
|
102
|
-
@paused = true
|
103
|
-
_command "pause"
|
104
|
-
else
|
105
|
-
false
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def resume
|
110
|
-
if @paused
|
111
|
-
@paused = false
|
112
|
-
_command "pause"
|
113
|
-
else
|
114
|
-
false
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def seek(where, type = :absolute)
|
119
|
-
# mplayer talks seconds, not milliseconds.
|
120
|
-
seconds = where.to_f / 1_000
|
121
|
-
case type
|
122
|
-
when :absolute
|
123
|
-
_command "seek #{seconds} 2", expect_answer: true
|
124
|
-
when :relative
|
125
|
-
_command "seek #{seconds} 0", expect_answer: true
|
126
|
-
when :percent
|
127
|
-
_command "seek #{where} 1", expect_answer: true
|
128
|
-
else
|
129
|
-
raise NotImplementedError
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def speed
|
134
|
-
@speed
|
135
|
-
end
|
136
|
-
|
137
|
-
def speed=(new_speed)
|
138
|
-
@speed = new_speed.to_f
|
139
|
-
answer = _command "speed_set #{@speed}", expect_answer: true
|
140
|
-
!!answer
|
141
|
-
end
|
142
|
-
|
143
|
-
def muted?
|
144
|
-
@muted
|
145
|
-
end
|
146
|
-
|
147
|
-
def mute
|
148
|
-
@muted = true
|
149
|
-
answer = _command "mute 1", expect_answer: true
|
150
|
-
!!answer
|
151
|
-
end
|
152
|
-
|
153
|
-
def unmute
|
154
|
-
@muted = false
|
155
|
-
answer = _command "mute 0", expect_answer: true
|
156
|
-
!!answer
|
157
|
-
end
|
158
|
-
|
159
|
-
def volume
|
160
|
-
@volume
|
161
|
-
end
|
162
|
-
|
163
|
-
def volume=(new_volume)
|
164
|
-
@muted = false
|
165
|
-
@volume = new_volume.to_f
|
166
|
-
answer = _command "volume #{@volume} 1", expect_answer: true
|
167
|
-
!!answer
|
168
|
-
end
|
169
|
-
|
170
|
-
def current_time
|
171
|
-
answer = _command "get_time_pos", expect_answer: /^ANS_TIME_POSITION=([0-9.]+)$/
|
172
|
-
answer ? (answer.to_f * 1000).to_i : false
|
173
|
-
end
|
174
|
-
|
175
|
-
def total_time
|
176
|
-
answer = _command "get_time_length", expect_answer: /^ANS_LENGTH=([0-9.]+)$/
|
177
|
-
answer ? (answer.to_f * 1000).to_i : false
|
178
|
-
end
|
179
|
-
|
180
|
-
def metadata
|
181
|
-
properties = %w(album artist comment genre title track year)
|
182
|
-
properties.inject({}) do |hash, property|
|
183
|
-
answer = _command "get_meta_#{property}", expect_answer: /^ANS_META_#{property.upcase}='(.+)'$/
|
184
|
-
hash[property.to_sym] = answer || nil
|
185
|
-
hash
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
private
|
190
|
-
|
191
|
-
# Issue a command to mplayer through the slave protocol. False is returned
|
192
|
-
# if the process is dead (not playing anything).
|
193
|
-
#
|
194
|
-
# If :expect_answer option is set to true, this will wait for a legible
|
195
|
-
# answer back from mplayer, and send it as a return value. If :expect_answer
|
196
|
-
# is set to a Regexp, the answer mplayer gives back will be matched to that
|
197
|
-
# Regexp and the first match will be returned. If there are no matches, nil
|
198
|
-
# will be returned.
|
199
|
-
def _command(cmd, options = {})
|
200
|
-
if _alive? and active?
|
201
|
-
# If the player is paused, prefix the command with "pausing ".
|
202
|
-
# Otherwise it unpauses when it runs a command. The only exception to
|
203
|
-
# this is when the "pause" command itself is issued.
|
204
|
-
if paused? and cmd != "pause"
|
205
|
-
cmd = "pausing #{cmd}"
|
206
|
-
end
|
207
|
-
|
208
|
-
# Send the command to mplayer.
|
209
|
-
@stdin.puts cmd
|
210
|
-
|
211
|
-
if options[:expect_answer]
|
212
|
-
# Read lines of output from mplayer until we get an actual message.
|
213
|
-
answer = "\n"
|
214
|
-
while answer == "\n"
|
215
|
-
answer = @stdout.gets.sub("\e[A\r\e[K", "")
|
216
|
-
answer = "\n" if options[:expect_answer].is_a?(Regexp) && answer !~ options[:expect_answer]
|
217
|
-
end
|
218
|
-
|
219
|
-
if options[:expect_answer].is_a? Regexp
|
220
|
-
matches = answer.match(options[:expect_answer])
|
221
|
-
answer = matches && matches[1]
|
222
|
-
end
|
223
|
-
|
224
|
-
answer
|
225
|
-
else
|
226
|
-
true
|
227
|
-
end
|
228
|
-
else
|
229
|
-
false
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
# Quit the mplayer process, stopping playback. The end of song callback
|
234
|
-
# will not be called if this method is called.
|
235
|
-
def _quit
|
236
|
-
if _alive?
|
237
|
-
@quit_hook.kill unless @quit_hook_active
|
238
|
-
_command "quit"
|
239
|
-
@paused = false
|
240
|
-
@track = nil
|
241
|
-
end
|
242
|
-
true
|
243
|
-
end
|
244
|
-
|
245
|
-
# Check if the mplayer process is still around.
|
246
|
-
def _alive?
|
247
|
-
return false if @pid.nil?
|
248
|
-
Process.getpgid(@pid)
|
249
|
-
true
|
250
|
-
rescue Errno::ESRCH
|
251
|
-
false
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
data/lib/listlace/time_helper.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module Listlace
|
2
|
-
# Helper method to format a number of milliseconds as a string like
|
3
|
-
# "1:03:56.555". The only option is :include_milliseconds, true by default. If
|
4
|
-
# false, milliseconds won't be included in the formatted string.
|
5
|
-
def self.format_time(milliseconds, options = {})
|
6
|
-
ms = milliseconds % 1000
|
7
|
-
seconds = (milliseconds / 1000) % 60
|
8
|
-
minutes = (milliseconds / 60000) % 60
|
9
|
-
hours = milliseconds / 3600000
|
10
|
-
|
11
|
-
if ms.zero? || options[:include_milliseconds] == false
|
12
|
-
ms_string = ""
|
13
|
-
else
|
14
|
-
ms_string = ".%03d" % [ms]
|
15
|
-
end
|
16
|
-
|
17
|
-
if hours > 0
|
18
|
-
"%d:%02d:%02d%s" % [hours, minutes, seconds, ms_string]
|
19
|
-
else
|
20
|
-
"%d:%02d%s" % [minutes, seconds, ms_string]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Helper method to parse a string like "1:03:56.555" and return the number of
|
25
|
-
# milliseconds that time length represents.
|
26
|
-
def self.parse_time(string)
|
27
|
-
parts = string.split(":").map(&:to_f)
|
28
|
-
parts = [0] + parts if parts.length == 2
|
29
|
-
hours, minutes, seconds = parts
|
30
|
-
seconds = hours * 3600 + minutes * 60 + seconds
|
31
|
-
milliseconds = seconds * 1000
|
32
|
-
milliseconds.to_i
|
33
|
-
end
|
34
|
-
end
|