listlace 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
- * queueing
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 :playing
12
+ status
10
13
  elsif $player.started?
11
- $player.pause
12
- status :playing
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
- track = $player.current_track
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 :playing
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 :playing
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 :playing
62
+ status
56
63
  else
57
64
  puts "End of queue."
58
65
  end
59
66
  end
60
67
 
61
- # The queue command. Simply appends tracks to the queue. Tracks can be
62
- # specified by a single Track, a Playlist, an ActiveRecord::Relation, or an
63
- # Array containing any of the above. With or without arguments, it returns the
64
- # queue as an Array of Tracks, so this can be used as an accessor method.
65
- def q(*tracks)
66
- tracks.each do |playlist_or_track|
67
- case playlist_or_track
68
- when Track
69
- $player.queue playlist_or_track
70
- when Playlist
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
- # Clears the queue.
82
- def clear
83
- $player.clear
84
- puts "Queue cleared."
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
- def status(type = [:playlist, :playing])
88
- case type
89
- when Array
90
- type.each { |t| status t }
91
- when :playlist
92
- #nop
93
- when :playing
94
- if $player.started?
95
- s = $player.paused? ? "Paused" : "Now Playing"
96
- name = $player.current_track.name
97
- artist = $player.current_track.artist
98
- time = $player.formatted_current_time
99
- total_time = $player.current_track.formatted_total_time
100
- puts "%s: %s - %s (%s / %s)" % [s, name, artist, time, total_time]
101
- else
102
- puts "Stopped."
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
- total_seconds = total_time / 1000
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
@@ -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
 
@@ -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
- attr_accessor :current_track
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
- 0
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
- "0:00"
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 :change_track }
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.2"
4
- s.date = "2012-08-22"
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.2
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-22 00:00:00.000000000 Z
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