listlace 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +6 -2
- data/lib/listlace/commands/playback.rb +78 -48
- data/lib/listlace/commands/queue.rb +35 -0
- data/lib/listlace/models/track.rb +10 -6
- data/lib/listlace/mplayer.rb +21 -2
- data/lib/listlace/player.rb +65 -5
- data/lib/listlace.rb +1 -0
- data/listlace.gemspec +2 -2
- metadata +3 -2
data/README
CHANGED
@@ -17,7 +17,7 @@ appropriate, don't you think?
|
|
17
17
|
|
18
18
|
It's a gem. So do this:
|
19
19
|
|
20
|
-
gem install listlace
|
20
|
+
$ gem install listlace
|
21
21
|
|
22
22
|
But you also need mplayer. It has to be
|
23
23
|
at /usr/bin/mplayer, for now.
|
@@ -67,7 +67,11 @@ itself will start playing the queue.
|
|
67
67
|
- restart: seek back to the beginning of the current track
|
68
68
|
- back: go back a song
|
69
69
|
- skip: go to the next song
|
70
|
+
- seek: move to a different part of the track
|
71
|
+
- ff: fast-forward, or slow down
|
72
|
+
- repeat: choose the repeat mode, either :one or :all
|
73
|
+
- norepeat: turn off the repeat mode
|
70
74
|
- status: show what's currently playing, as well as other information
|
71
|
-
*
|
75
|
+
* queue
|
72
76
|
- q: append some tracks to the queue, or get the queue
|
73
77
|
- clear: clear the queue
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module Listlace
|
2
|
+
REPEAT_SYMBOL = "\u221E"
|
3
|
+
TIMES_SYMBOL = "\u00D7"
|
4
|
+
|
2
5
|
# The play command. With no arguments, it either resumes playback or starts
|
3
6
|
# playing the queue. With arguments, it replaces the queue with the given
|
4
7
|
# tracks and starts playing.
|
@@ -6,22 +9,26 @@ module Listlace
|
|
6
9
|
if tracks.empty?
|
7
10
|
if $player.paused?
|
8
11
|
$player.resume
|
9
|
-
status
|
12
|
+
status
|
10
13
|
elsif $player.started?
|
11
|
-
$player.
|
12
|
-
|
14
|
+
if $player.speed == 1
|
15
|
+
$player.pause
|
16
|
+
status
|
17
|
+
else
|
18
|
+
$player.set_speed 1
|
19
|
+
status
|
20
|
+
end
|
13
21
|
else
|
14
22
|
if $player.empty?
|
15
23
|
puts "Nothing to play."
|
16
24
|
else
|
17
25
|
$player.start
|
18
|
-
|
19
|
-
status :playing
|
26
|
+
status
|
20
27
|
end
|
21
28
|
end
|
22
29
|
else
|
23
|
-
stop
|
24
|
-
clear
|
30
|
+
$player.stop
|
31
|
+
$player.clear
|
25
32
|
q *tracks
|
26
33
|
p
|
27
34
|
end
|
@@ -37,13 +44,13 @@ module Listlace
|
|
37
44
|
# Start the current track from the beginning.
|
38
45
|
def restart
|
39
46
|
$player.restart
|
40
|
-
status
|
47
|
+
status
|
41
48
|
end
|
42
49
|
|
43
50
|
# Go back one song in the queue.
|
44
51
|
def back
|
45
52
|
if $player.back
|
46
|
-
status
|
53
|
+
status
|
47
54
|
else
|
48
55
|
puts "End of queue."
|
49
56
|
end
|
@@ -52,54 +59,77 @@ module Listlace
|
|
52
59
|
# Go directly to the next song in the queue.
|
53
60
|
def skip
|
54
61
|
if $player.skip
|
55
|
-
status
|
62
|
+
status
|
56
63
|
else
|
57
64
|
puts "End of queue."
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
q *playlist_or_track.tracks
|
72
|
-
when Array
|
73
|
-
q *playlist_or_track
|
74
|
-
when ActiveRecord::Relation
|
75
|
-
q *playlist_or_track.all
|
76
|
-
end
|
77
|
-
end
|
78
|
-
$player.queue
|
68
|
+
# Seek to a particular position in the current track. If given an integer, it
|
69
|
+
# will seek that many seconds forward or backward. If given a Range, it will
|
70
|
+
# seek to that specific time, the first number in the Range representing the
|
71
|
+
# minutes, the second number representing the seconds. You can also pass a
|
72
|
+
# String like "1:23:45" to do the same thing. To seek to an absolute time in
|
73
|
+
# seconds, do it like "seek(abs: 40)". To seek to a percentage, do something
|
74
|
+
# like "seek(percent: 75)".
|
75
|
+
def seek(where)
|
76
|
+
$player.seek(where)
|
77
|
+
status
|
79
78
|
end
|
80
79
|
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
# Fast-forward at a particular speed. Induces the chipmunk effect, which I
|
81
|
+
# find agreeable. Call p to go back to normal. You can also pass a value
|
82
|
+
# smaller than one to slow down.
|
83
|
+
def ff(speed = 2)
|
84
|
+
$player.set_speed(speed)
|
85
|
+
status
|
85
86
|
end
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
88
|
+
# Pass :all to start playing from the top of the queue when it gets to the
|
89
|
+
# end. Pass :one to repeat the current track.
|
90
|
+
def repeat(one_or_all = :all)
|
91
|
+
$player.repeat one_or_all
|
92
|
+
status
|
93
|
+
end
|
94
|
+
|
95
|
+
# Turn off the repeat mode set by the repeat command.
|
96
|
+
def norepeat
|
97
|
+
$player.repeat :off
|
98
|
+
status
|
99
|
+
end
|
100
|
+
|
101
|
+
# Show various information about the status of the player. The information it
|
102
|
+
# shows depends on what status types you pass:
|
103
|
+
#
|
104
|
+
# :playlist - Shows the playlist that is currently playing
|
105
|
+
# :playing - Shows the current track
|
106
|
+
#
|
107
|
+
def status(*types)
|
108
|
+
types = [:playlist, :playing] if types.empty?
|
109
|
+
types.each do |type|
|
110
|
+
case type
|
111
|
+
when :playlist
|
112
|
+
if $player.started?
|
113
|
+
track_number = $player.current_track_index + 1
|
114
|
+
num_tracks = q.length
|
115
|
+
repeat_one = $player.repeat_mode == :one ? REPEAT_SYMBOL : ""
|
116
|
+
repeat_all = $player.repeat_mode == :all ? REPEAT_SYMBOL : ""
|
117
|
+
puts "Playlist: queue (%d%s / %d%s)" % [track_number, repeat_one, num_tracks, repeat_all]
|
118
|
+
else
|
119
|
+
puts "Playlist: queue (%d songs)" % [q.length]
|
120
|
+
end
|
121
|
+
when :playing
|
122
|
+
if $player.started?
|
123
|
+
name = $player.current_track.name
|
124
|
+
artist = $player.current_track.artist
|
125
|
+
time = $player.formatted_current_time
|
126
|
+
total_time = $player.current_track.formatted_total_time
|
127
|
+
paused = $player.paused? ? "|| " : ""
|
128
|
+
speed = $player.speed != 1 ? "#{TIMES_SYMBOL}#{$player.speed} " : ""
|
129
|
+
puts "%s - %s (%s / %s) %s%s" % [name, artist, time, total_time, paused, speed]
|
130
|
+
else
|
131
|
+
puts "Stopped."
|
132
|
+
end
|
103
133
|
end
|
104
134
|
end
|
105
135
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Listlace
|
2
|
+
# The queue command. Simply appends tracks to the queue. Tracks can be
|
3
|
+
# specified by a single Track, a Playlist, an ActiveRecord::Relation, or an
|
4
|
+
# Array containing any of the above. With or without arguments, it returns the
|
5
|
+
# queue as an Array of Tracks, so this can be used as an accessor method.
|
6
|
+
def q(*tracks)
|
7
|
+
tracks.each do |playlist_or_track|
|
8
|
+
case playlist_or_track
|
9
|
+
when Track
|
10
|
+
$player.queue playlist_or_track
|
11
|
+
when Playlist
|
12
|
+
q *playlist_or_track.tracks
|
13
|
+
when Array
|
14
|
+
q *playlist_or_track
|
15
|
+
when ActiveRecord::Relation
|
16
|
+
q *playlist_or_track.all
|
17
|
+
end
|
18
|
+
end
|
19
|
+
$player.queue
|
20
|
+
end
|
21
|
+
|
22
|
+
# Clears the queue.
|
23
|
+
def clear
|
24
|
+
$player.clear
|
25
|
+
puts "Queue cleared."
|
26
|
+
end
|
27
|
+
|
28
|
+
def shuffle
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def sort(by = :artist_asc_album_asc_track_number_asc)
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -4,7 +4,16 @@ module Listlace
|
|
4
4
|
has_many :playlists, through: :playlist_items
|
5
5
|
|
6
6
|
def formatted_total_time
|
7
|
-
|
7
|
+
Track.format_time(total_time)
|
8
|
+
end
|
9
|
+
|
10
|
+
def play
|
11
|
+
$player.queue = [self]
|
12
|
+
Listlace.play
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.format_time(milliseconds)
|
16
|
+
total_seconds = milliseconds / 1000
|
8
17
|
|
9
18
|
seconds = total_seconds % 60
|
10
19
|
minutes = (total_seconds / 60) % 60
|
@@ -16,10 +25,5 @@ module Listlace
|
|
16
25
|
"%d:%02d" % [minutes, seconds]
|
17
26
|
end
|
18
27
|
end
|
19
|
-
|
20
|
-
def play
|
21
|
-
$player.queue = [self]
|
22
|
-
Listlace.play
|
23
|
-
end
|
24
28
|
end
|
25
29
|
end
|
data/lib/listlace/mplayer.rb
CHANGED
@@ -6,19 +6,38 @@ module Listlace
|
|
6
6
|
def initialize(track, &on_quit)
|
7
7
|
cmd = "/usr/bin/mplayer -slave -quiet #{Shellwords.shellescape(track.location)}"
|
8
8
|
@pid, @stdin, @stdout, @stderr = Open4.popen4(cmd)
|
9
|
+
@paused = false
|
10
|
+
@extra_lines = 0
|
9
11
|
|
12
|
+
until @stdout.gets["playback"]
|
13
|
+
end
|
14
|
+
|
15
|
+
@quit_hook_active = false
|
10
16
|
@quit_hook = Thread.new do
|
11
17
|
Process.wait(@pid)
|
18
|
+
@quit_hook_active = true
|
12
19
|
on_quit.call
|
13
20
|
end
|
14
21
|
end
|
15
22
|
|
16
|
-
def command(cmd)
|
23
|
+
def command(cmd, options = {})
|
24
|
+
if cmd == "pause"
|
25
|
+
@paused = !@paused
|
26
|
+
elsif @paused
|
27
|
+
cmd = "pausing #{cmd}"
|
28
|
+
end
|
29
|
+
|
17
30
|
@stdin.puts cmd
|
31
|
+
|
32
|
+
if options[:expect_answer]
|
33
|
+
answer = "\n"
|
34
|
+
answer = @stdout.gets.sub("\e[A\r\e[K", "") while answer == "\n"
|
35
|
+
answer
|
36
|
+
end
|
18
37
|
end
|
19
38
|
|
20
39
|
def quit
|
21
|
-
@quit_hook.kill
|
40
|
+
@quit_hook.kill unless @quit_hook_active
|
22
41
|
command "quit" if alive?
|
23
42
|
end
|
24
43
|
|
data/lib/listlace/player.rb
CHANGED
@@ -3,7 +3,7 @@ module Listlace
|
|
3
3
|
# then plays these tracks sequentially. The buttons for play, pause, next,
|
4
4
|
# previous, etc. are all located here.
|
5
5
|
class Player
|
6
|
-
|
6
|
+
attr_reader :current_track, :current_track_index, :repeat_mode
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@mplayer = nil
|
@@ -12,6 +12,7 @@ module Listlace
|
|
12
12
|
@current_track_index = nil
|
13
13
|
@paused = false
|
14
14
|
@started = false
|
15
|
+
@repeat_mode = false
|
15
16
|
end
|
16
17
|
|
17
18
|
def queue(track = nil)
|
@@ -72,6 +73,17 @@ module Listlace
|
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
76
|
+
def repeat(one_or_all_or_off)
|
77
|
+
case one_or_all_or_off
|
78
|
+
when :one
|
79
|
+
@repeat_mode = :one
|
80
|
+
when :all
|
81
|
+
@repeat_mode = :all
|
82
|
+
when :off
|
83
|
+
@repeat_mode = false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
75
87
|
def restart
|
76
88
|
change_track(0)
|
77
89
|
end
|
@@ -84,18 +96,66 @@ module Listlace
|
|
84
96
|
change_track(1)
|
85
97
|
end
|
86
98
|
|
99
|
+
def seek(where)
|
100
|
+
case where
|
101
|
+
when Integer
|
102
|
+
@mplayer.command("seek %d 0" % [where], expect_answer: true)
|
103
|
+
when Range
|
104
|
+
@mplayer.command("seek %d 2" % [where.begin * 60 + where.end], expect_answer: true)
|
105
|
+
when String
|
106
|
+
parts = where.split(":").map(&:to_i)
|
107
|
+
parts = [0] + parts if parts.length == 2
|
108
|
+
hours, minutes, seconds = parts
|
109
|
+
@mplayer.command("seek %d 2" % [hours * 3600 + minutes * 60 + seconds], expect_answer: true)
|
110
|
+
when Hash
|
111
|
+
if where[:abs]
|
112
|
+
if where[:abs].is_a? Integer
|
113
|
+
@mplayer.command("seek %d 2" % [where[:abs]], expect_answer: true)
|
114
|
+
else
|
115
|
+
seek(where[:abs])
|
116
|
+
end
|
117
|
+
elsif where[:percent]
|
118
|
+
@mplayer.command("seek %d 1" % [where[:percent]], expect_answer: true)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def speed
|
124
|
+
answer = @mplayer.command("get_property speed", expect_answer: true)
|
125
|
+
if answer =~ /^ANS_speed=([0-9.]+)$/
|
126
|
+
$1.to_f
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def set_speed(speed)
|
131
|
+
@mplayer.command("speed_set %f" % [speed], expect_answer: true)
|
132
|
+
end
|
133
|
+
|
87
134
|
def current_time
|
88
|
-
|
135
|
+
answer = @mplayer.command "get_time_pos", expect_answer: true
|
136
|
+
if answer =~ /^ANS_TIME_POSITION=([0-9.]+)$/
|
137
|
+
($1.to_f * 1000).to_i
|
138
|
+
end
|
89
139
|
end
|
90
140
|
|
91
141
|
def formatted_current_time
|
92
|
-
|
142
|
+
Track.format_time(current_time)
|
93
143
|
end
|
94
144
|
|
95
145
|
private
|
96
146
|
|
97
|
-
def change_track(by = 1)
|
147
|
+
def change_track(by = 1, options = {})
|
98
148
|
@current_track_index += by
|
149
|
+
if options[:auto] && @repeat_mode
|
150
|
+
case @repeat_mode
|
151
|
+
when :one
|
152
|
+
@current_track_index -= by
|
153
|
+
when :all
|
154
|
+
if @current_track_index >= @queue.length
|
155
|
+
@current_track_index = 0
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
99
159
|
@current_track = @queue[@current_track_index]
|
100
160
|
if @current_track && @current_track_index >= 0
|
101
161
|
if paused?
|
@@ -112,7 +172,7 @@ module Listlace
|
|
112
172
|
|
113
173
|
def load_track(track)
|
114
174
|
@mplayer.quit if @mplayer
|
115
|
-
@mplayer = MPlayer.new(track) { send
|
175
|
+
@mplayer = MPlayer.new(track) { send(:change_track, 1, auto: true) }
|
116
176
|
@paused = false
|
117
177
|
end
|
118
178
|
end
|
data/lib/listlace.rb
CHANGED
@@ -21,6 +21,7 @@ require "listlace/commands/library"
|
|
21
21
|
require "listlace/commands/playback"
|
22
22
|
require "listlace/commands/selectors"
|
23
23
|
require "listlace/commands/volume"
|
24
|
+
require "listlace/commands/queue"
|
24
25
|
|
25
26
|
# gotta ged rid of this global sometime
|
26
27
|
$player = Listlace::Player.new
|
data/listlace.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "listlace"
|
3
|
-
s.version = "0.0.
|
4
|
-
s.date = "2012-08-
|
3
|
+
s.version = "0.0.3"
|
4
|
+
s.date = "2012-08-24"
|
5
5
|
s.summary = "A music player in a REPL."
|
6
6
|
s.description = "Listlace is a music player which is interacted with through a Ruby REPL."
|
7
7
|
s.author = "Jeremy Ruten"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: listlace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pry
|
@@ -138,6 +138,7 @@ files:
|
|
138
138
|
- bin/listlace
|
139
139
|
- lib/listlace/commands/library.rb
|
140
140
|
- lib/listlace/commands/playback.rb
|
141
|
+
- lib/listlace/commands/queue.rb
|
141
142
|
- lib/listlace/commands/selectors.rb
|
142
143
|
- lib/listlace/commands/volume.rb
|
143
144
|
- lib/listlace/database.rb
|