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 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