cultome_player 2.0.0

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +325 -0
  9. data/Rakefile +8 -0
  10. data/bin/cultome_player +39 -0
  11. data/config/environment.yml +28 -0
  12. data/cultome_player.gemspec +35 -0
  13. data/db/001_create_schema.rb +58 -0
  14. data/lib/cultome_player.rb +107 -0
  15. data/lib/cultome_player/command.rb +11 -0
  16. data/lib/cultome_player/command/language.rb +61 -0
  17. data/lib/cultome_player/command/processor.rb +165 -0
  18. data/lib/cultome_player/command/reader.rb +86 -0
  19. data/lib/cultome_player/environment.rb +130 -0
  20. data/lib/cultome_player/events.rb +29 -0
  21. data/lib/cultome_player/media.rb +47 -0
  22. data/lib/cultome_player/objects.rb +15 -0
  23. data/lib/cultome_player/objects/album.rb +21 -0
  24. data/lib/cultome_player/objects/artist.rb +18 -0
  25. data/lib/cultome_player/objects/command.rb +37 -0
  26. data/lib/cultome_player/objects/drive.rb +26 -0
  27. data/lib/cultome_player/objects/genre.rb +16 -0
  28. data/lib/cultome_player/objects/parameter.rb +37 -0
  29. data/lib/cultome_player/objects/response.rb +42 -0
  30. data/lib/cultome_player/objects/song.rb +38 -0
  31. data/lib/cultome_player/player.rb +13 -0
  32. data/lib/cultome_player/player/adapter.rb +14 -0
  33. data/lib/cultome_player/player/adapter/mpg123.rb +143 -0
  34. data/lib/cultome_player/player/interactive.rb +56 -0
  35. data/lib/cultome_player/player/interface.rb +13 -0
  36. data/lib/cultome_player/player/interface/basic.rb +96 -0
  37. data/lib/cultome_player/player/interface/builtin_help.rb +368 -0
  38. data/lib/cultome_player/player/interface/extended.rb +199 -0
  39. data/lib/cultome_player/player/interface/helper.rb +300 -0
  40. data/lib/cultome_player/player/playlist.rb +280 -0
  41. data/lib/cultome_player/plugins.rb +23 -0
  42. data/lib/cultome_player/plugins/help.rb +58 -0
  43. data/lib/cultome_player/state_checker.rb +74 -0
  44. data/lib/cultome_player/utils.rb +95 -0
  45. data/lib/cultome_player/version.rb +3 -0
  46. data/spec/config.yml +0 -0
  47. data/spec/cultome_player/command/processor_spec.rb +168 -0
  48. data/spec/cultome_player/command/reader_spec.rb +45 -0
  49. data/spec/cultome_player/cultome_player_spec.rb +17 -0
  50. data/spec/cultome_player/environment_spec.rb +65 -0
  51. data/spec/cultome_player/events_spec.rb +22 -0
  52. data/spec/cultome_player/media_spec.rb +41 -0
  53. data/spec/cultome_player/player/adapter/mpg123_spec.rb +82 -0
  54. data/spec/cultome_player/player/interface/basic_spec.rb +168 -0
  55. data/spec/cultome_player/player/interface/extended/connect_spec.rb +117 -0
  56. data/spec/cultome_player/player/interface/extended/search_spec.rb +90 -0
  57. data/spec/cultome_player/player/interface/extended/show_spec.rb +36 -0
  58. data/spec/cultome_player/player/interface/extended/shuffle_spec.rb +26 -0
  59. data/spec/cultome_player/player/interface/extended_spec.rb +136 -0
  60. data/spec/cultome_player/player/interface/helper_spec.rb +63 -0
  61. data/spec/cultome_player/player/interface_spec.rb +17 -0
  62. data/spec/cultome_player/player/playlist_spec.rb +301 -0
  63. data/spec/cultome_player/plugins/help_spec.rb +21 -0
  64. data/spec/cultome_player/plugins_spec.rb +19 -0
  65. data/spec/cultome_player/utils_spec.rb +15 -0
  66. data/spec/spec_helper.rb +108 -0
  67. data/spec/test/uno/dos/dos.mp3 +0 -0
  68. data/spec/test/uno/dos/tres/tres.mp3 +0 -0
  69. data/spec/test/uno/uno.mp3 +0 -0
  70. data/tasks/console.rake +19 -0
  71. data/tasks/db.rake +19 -0
  72. data/tasks/run.rake +7 -0
  73. metadata +322 -0
@@ -0,0 +1,42 @@
1
+ module CultomePlayer
2
+ module Objects
3
+ class Response
4
+ attr_reader :data
5
+
6
+ def initialize(type, data)
7
+ @success = type == :success
8
+ @data = data
9
+
10
+ @data.each do |k,v|
11
+ self.class.send(:define_method, k) do
12
+ v
13
+ end
14
+ end
15
+ end
16
+
17
+ # Check if the success data associated to the response is false.
18
+ #
19
+ # @return [Boolean] True if success data is false, False otherwise.
20
+ def failure?
21
+ !@success
22
+ end
23
+
24
+ # Check if the success data associated to the response is true.
25
+ #
26
+ # @return [Boolean] True if success data is true, False otherwise.
27
+ def success?
28
+ @success
29
+ end
30
+
31
+ # Join two response together. The response type makes an OR and parameter response's data is merged into.
32
+ #
33
+ # @param response [Response] The response to join.
34
+ # @return [Response] The calculated new response.
35
+ def +(response)
36
+ type = success? && response.success? ? :success : :failure
37
+ data = @data.merge response.data
38
+ return Response.new(type, data)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_record'
2
+
3
+ module CultomePlayer
4
+ module Objects
5
+ # The ActiveRecord model for Song objects.
6
+ class Song < ActiveRecord::Base
7
+ belongs_to :artist
8
+ belongs_to :album
9
+ has_and_belongs_to_many :genres
10
+ belongs_to :drive
11
+ has_many :similars, as: :similar
12
+
13
+ scope :connected, -> {joins(:drive).where('drives.connected' => true)}
14
+ # Get the full path to the song file.
15
+ #
16
+ # @return [String] The full path to the song file.
17
+ def path
18
+ File.join(self.drive.path, self.relative_path)
19
+ end
20
+
21
+ def to_s
22
+ str = c4(":::: Song: ")
23
+ str += c10(self.name)
24
+
25
+ unless self.artist.nil?
26
+ str += c4(" \\ Artist: ")
27
+ str += c11(self.artist.name)
28
+ end
29
+
30
+ unless self.album.nil?
31
+ str += c4(" \\ Album: ")
32
+ str += c13(self.album.name)
33
+ end
34
+ str += c4(" ::::")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ require 'cultome_player/player/interface'
2
+ require 'cultome_player/player/interactive'
3
+ require 'cultome_player/player/playlist'
4
+ require 'cultome_player/player/adapter'
5
+
6
+ module CultomePlayer
7
+ module Player
8
+ include Interface
9
+ include Interactive
10
+ include Playlist
11
+ include Adapter
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'cultome_player/player/adapter/mpg123'
2
+
3
+ module CultomePlayer::Player::Adapter
4
+ include Mpg123
5
+
6
+ # Check if media player is running.
7
+ #
8
+ # @return [Boolean] True is player is running. False otherwise.
9
+ def player_running?
10
+ @is_player_running
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,143 @@
1
+ module CultomePlayer::Player::Adapter
2
+ module Mpg123
3
+
4
+ # Start a playback in the media player.
5
+ #
6
+ # @contract Adapter
7
+ # @param song [Song] The song to be played.
8
+ def play_in_player(song)
9
+ @current_song = song
10
+ unless player_running?
11
+ start_player
12
+ end
13
+
14
+ loadfile(song)
15
+ end
16
+
17
+ # Activate the pause in media player.
18
+ #
19
+ # @contract Adapter
20
+ def pause_in_player
21
+ toggle_pause
22
+ end
23
+
24
+ # Resume playback in media player. If is paused or stopped.
25
+ #
26
+ # @contract Adapter
27
+ def resume_in_player
28
+ if paused?
29
+ toggle_pause
30
+ else
31
+ play_in_player current_song
32
+ end
33
+ end
34
+
35
+ # Stop playback in media player.
36
+ #
37
+ # @contract Adapter
38
+ def stop_in_player
39
+ @user_stopped = true
40
+ send_to_player "stop"
41
+ end
42
+
43
+ # Fast forward the playback
44
+ #
45
+ # @contract Adapter
46
+ # @param secs [Integer] Number of seconds to fast forward.
47
+ def ff_in_player(secs)
48
+ send_to_player "jump +#{secs}s"
49
+ end
50
+
51
+ # Fast backward the playback
52
+ #
53
+ # @contract Adapter
54
+ # @param secs [Integer] Number of seconds to fast backward.
55
+ def fb_in_player(secs)
56
+ send_to_player "jump -#{secs}s"
57
+ end
58
+
59
+ # Turn off the media player
60
+ def quit_in_player
61
+ begin
62
+ send_to_player "quit"
63
+ rescue Exception => e
64
+ end
65
+ end
66
+
67
+ # Play from the begining the current playback.
68
+ def repeat_in_player
69
+ send_to_player "jump 0"
70
+ end
71
+
72
+ private
73
+
74
+ def toggle_pause
75
+ send_to_player "pause"
76
+ end
77
+
78
+ def loadfile(song)
79
+ send_to_player "load #{song.path}"
80
+ end
81
+
82
+ def send_to_player(cmd)
83
+ raise 'invalid state:player is not running' unless player_running?
84
+ control_pipe.puts cmd
85
+ control_pipe.flush
86
+ end
87
+
88
+ def control_pipe
89
+ unless pipe_alive?
90
+ @pipe = File.open(mplayer_pipe, 'a+')
91
+ end
92
+ @pipe
93
+ end
94
+
95
+ def pipe_alive?
96
+ return !(@pipe.nil? || @pipe.closed?)
97
+ end
98
+
99
+ def start_player
100
+ # creamos el thread que lea la salida del mpg123
101
+ Thread.new do
102
+ start_cmd = "mpg123 --fifo #{mplayer_pipe} -R"
103
+ IO.popen(start_cmd).each do |line|
104
+ case line
105
+ when /^@R MPG123/
106
+ @is_player_running = true
107
+ when /^@P ([\d])$/
108
+ case $1.to_i
109
+ when 0 # stopped
110
+ @playing = @paused = false
111
+ @stopped = true
112
+ emit_event(:playback_finish) unless @user_stopped
113
+ @user_stopped = false
114
+ when 1 # paused
115
+ @stopped = @playing = false
116
+ @paused = true
117
+ emit_event(:playback_paused)
118
+ when 2 # unpaused
119
+ @playing = true
120
+ @paused = @stopped = false
121
+ emit_event(:playback_resumed)
122
+ end
123
+ when /^@F ([\d]+) ([\d]+) ([\d.]+) ([\d.]+)$/
124
+ @playback_time_position = $3.to_f
125
+ @playback_time_length = @playback_time_position + $4.to_f
126
+ end # case line
127
+ end # IO
128
+ end # Thread
129
+
130
+ wait_player
131
+ end
132
+
133
+ def wait_player
134
+ count = 0
135
+ while !player_running?
136
+ sleep(0.1)
137
+ count += 1
138
+ return if count > 50 # 5 seg
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,56 @@
1
+ module CultomePlayer::Player
2
+ module Interactive
3
+
4
+ PROMPT = "cultome> "
5
+
6
+ # Begin a REP loop inside player.
7
+ def begin_session
8
+ @in_session = true
9
+ display "Cultome Player v#{CultomePlayer::VERSION}"
10
+
11
+ while in_session?
12
+ begin
13
+ r = execute read_command(PROMPT)
14
+ show_response(r)
15
+ rescue Exception => e
16
+ display e.message
17
+ end
18
+ end
19
+ end
20
+
21
+ # Check if there is an interactive session in progress.
22
+ #
23
+ # @return [Boolean] True if session in progress. False otherwise.
24
+ def in_session?
25
+ @in_session ||= false
26
+ end
27
+
28
+ # Terminates a interactive session.
29
+ def terminate_session
30
+ @in_session = false
31
+ end
32
+
33
+ private
34
+
35
+ def show_response(r)
36
+ if r.respond_to?(:response_type)
37
+ res_obj = r.send(r.response_type)
38
+ if res_obj.respond_to?(:each)
39
+ res_obj.each.with_index do |elem, idx|
40
+ display "#{(idx + 1).to_s.ljust(3)} | #{elem.to_s}"
41
+ end
42
+ elsif res_obj.class == String
43
+ display res_obj.to_s
44
+ else
45
+ display "(((#{res_obj.to_s})))"
46
+ end
47
+
48
+ elsif r.respond_to?(:message)
49
+ display r.message
50
+ else
51
+ display "!!!#{r}!!!"
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ require 'cultome_player/player/interface/basic'
2
+ require 'cultome_player/player/interface/extended'
3
+ require 'cultome_player/player/interface/helper'
4
+ require 'cultome_player/player/interface/builtin_help'
5
+
6
+ module CultomePlayer::Player
7
+ module Interface
8
+ include Basic
9
+ include Extended
10
+ include Helper
11
+ include BuiltinHelp
12
+ end
13
+ end
@@ -0,0 +1,96 @@
1
+ module CultomePlayer
2
+ module Player
3
+ module Interface
4
+ module Basic
5
+
6
+ # For more information on this command refer to user manual or inline help in interactive mode.
7
+ def play(cmd)
8
+ if cmd.params.empty?
9
+ # Estos los consideramos comportamientos inteligentes
10
+ # porque checan el contexto y toman una descision,
11
+ # por lo tanto la logica no aplica a el comando play normal
12
+
13
+ # tocar mientras ya estamos tocando algo??
14
+ return failure("What you mean? Im already playing!") if playing?
15
+ # quitamos la pausa
16
+ return execute "pause off" if paused?
17
+ # iniciamos ultima la reproduccion desde el principio
18
+ if stopped? && current_song
19
+ curr_song = player_object :song
20
+ return execute "play @song"
21
+ end
22
+ # tocamos toda la libreria
23
+ songs = whole_library
24
+ return failure("No music connected! You should try 'connect /home/yoo/music => main' first") if songs.empty?
25
+ playlists[:current, :focus] <= songs
26
+
27
+ else
28
+ songs = select_songs_with cmd
29
+ # checamos si el tipo de comando es para programar una
30
+ # nueva playlist o solo para tocar una cancion
31
+ if play_inline?(cmd)
32
+ playlists[:queue] << songs
33
+ else
34
+ playlists[:current] <= songs
35
+ end
36
+ end
37
+
38
+ return success(playlist: songs) + execute("next no_history")
39
+ end
40
+
41
+ # For more information on this command refer to user manual or inline help in interactive mode.
42
+ def pause(cmd)
43
+ if cmd.params.empty?
44
+ is_pause = !paused?
45
+ else
46
+ is_pause = cmd.params(:boolean).first.value
47
+ end
48
+
49
+ if is_pause
50
+ pause_in_player
51
+ else
52
+ resume_in_player
53
+ end
54
+
55
+ success(message: is_pause ? "Holding your horses" : "Letting it flow", paused: paused?, stopped: stopped?, playing: playing?)
56
+ end
57
+
58
+ # For more information on this command refer to user manual or inline help in interactive mode.
59
+ def stop(cmd)
60
+ stop_in_player
61
+ success(message: "Stoped it!", paused: paused?, stopped: stopped?, playing: playing?)
62
+ end
63
+
64
+ # For more information on this command refer to user manual or inline help in interactive mode.
65
+ def next(cmd)
66
+ if playlists[:queue].empty?
67
+ unless cmd.params(:literal).any?{|p| p.value == 'no_history'}
68
+ playlists[:history] << current_song
69
+ end
70
+ playlists[:queue] << playlists[:current].next
71
+ end
72
+
73
+ # aqui enviamos al reproductor externo a tocar
74
+ play_queue
75
+
76
+ return success(message: "Now playing #{current_song}", now_playing: current_song)
77
+ end
78
+
79
+ # For more information on this command refer to user manual or inline help in interactive mode.
80
+ def prev(cmd)
81
+ playlists[:queue] << playlists[:history].pop
82
+ playlists[:current].rewind_by 1
83
+ execute("next no_history")
84
+ end
85
+
86
+ # For more information on this command refer to user manual or inline help in interactive mode.
87
+ def quit(cmd)
88
+ quit_in_player
89
+ terminate_session
90
+ return success("See you next time!") unless in_session?
91
+ return failure("Oops! You should use Ctr-c or throw water to the CPU NOW!!!!")
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,368 @@
1
+ module CultomePlayer
2
+ module Player
3
+ module BuiltinHelp
4
+ def description_play
5
+ "Creates a playlist and start playing. Resumes playback."
6
+ end
7
+
8
+ def description_pause
9
+ "Toggle pause."
10
+ end
11
+
12
+ def description_stop
13
+ "Stops current playback."
14
+ end
15
+
16
+ def description_next
17
+ "Play the next song in current playlist."
18
+ end
19
+
20
+ def description_prev
21
+ "Play the last song in history playlist."
22
+ end
23
+
24
+ def description_quit
25
+ "Quits the playback and exit the player."
26
+ end
27
+
28
+ def description_search
29
+ "Search into the connected music drives."
30
+ end
31
+
32
+ def description_show
33
+ "Shows representations of diverse objects in the player."
34
+ end
35
+
36
+ def description_enqueue
37
+ "Append a playlist to the queue playlist."
38
+ end
39
+
40
+ def description_shuffle
41
+ "Check the state of shuffle. Can turn it on and off."
42
+ end
43
+
44
+ def description_connect
45
+ "Add or reconnect a drive to the music library."
46
+ end
47
+
48
+ def description_disconnect
49
+ "Disconnect a drive from the music library."
50
+ end
51
+
52
+ def description_ff
53
+ "Fast forward 10 seconds the current playback."
54
+ end
55
+
56
+ def description_fb
57
+ "Fast backward 10 seconds the current playback."
58
+ end
59
+
60
+ def description_repeat
61
+ "Repeat the current playback from the begining."
62
+ end
63
+
64
+ def usage_cultome_player
65
+ cmds_availables = methods.grep(/^description_/).collect do |method_name|
66
+ [method_name.to_s.gsub("description_", ""), send(method_name)]
67
+ end
68
+
69
+ border_width = 5
70
+ cmd_column_width = cmds_availables.reduce(0){|sum, arr| sum > arr[0].length ? sum : arr[0].length}
71
+ desc_column_width = 90 - border_width - cmd_column_width
72
+
73
+ cmds_availables_formatted = cmds_availables.collect do |arr|
74
+ " " + arrange_in_columns(arr, [cmd_column_width, desc_column_width], border_width)
75
+ end
76
+
77
+ return <<-HELP
78
+ usage: <command> [param param ...]
79
+
80
+ The following commands are availables:
81
+ #{cmds_availables_formatted.join("\n")}
82
+
83
+ The params can be of any of these types:
84
+ criterio A key:value pair. Only a,b,t are recognized.
85
+ literal Any valid string. If contains spaces quotes or double quotes are required.
86
+ object Identifiers preceded with an @.
87
+ number An integer number.
88
+ path A string representing a path in filesystem.
89
+ boolean Can be true, false. Accept some others.
90
+ ip An IP4 address.
91
+
92
+ See 'help <command>' for more information on a especific command.
93
+
94
+ Refer to the README file for a complete user guide.
95
+ HELP
96
+ end
97
+
98
+ def usage_play
99
+ return <<-USAGE
100
+ usage: play [literal|number|criteria|object]
101
+
102
+ This command is intelligent, so it reacts depending on its context.
103
+ Without parameters, if the current playlist is empty, play without parameters crate a playlist with all the music in your library and start playing, also if you stop or pause the player, this command resumes the playback where you left it.
104
+ With parameters works as if you make a search and then create a playlist from the results.
105
+ If you select songs from the *focus* playlist, this command dont replace the *current* playlist, just append the song to the *queue* playlist.
106
+
107
+ Examples:
108
+
109
+ Create a playlist with songs that contains "super" in its title, artist name o album name:
110
+ play super
111
+
112
+ Create a playlist with all the songs in the current album:
113
+ play @album
114
+
115
+ Create a playlist with songs whose artists contains the string "llica" or "gori":
116
+ play a:llica a:gori
117
+
118
+ If you use a command that modifies the *focus* playlist, you can play songs associated with their index in the list:
119
+ play 1 5 9
120
+
121
+ USAGE
122
+ end
123
+
124
+ def usage_pause
125
+ return <<-USAGE
126
+ usage: pause [boolean]
127
+
128
+ Without parameters, toggle pause. Whit a boolean parameter... well, just you what is been told.
129
+
130
+ Examples:
131
+
132
+ If there is an active playback, pause it:
133
+ pause
134
+
135
+ If is paused, resume:
136
+ pause
137
+
138
+ If you wanna make sure is paused:
139
+ pause on
140
+
141
+ USAGE
142
+ end
143
+
144
+ def usage_stop
145
+ return <<-USAGE
146
+ usage: stop
147
+
148
+ Stop the current playback. If *play* is called after *stop*, the playback will begin again.
149
+
150
+ Examples:
151
+
152
+ Stop current playback:
153
+ stop
154
+
155
+ USAGE
156
+ end
157
+
158
+ def usage_next
159
+ return <<-USAGE
160
+ usage: next
161
+
162
+ Select the next song in the *current* playlist and play it, if there is any or if *repeat* is on.
163
+
164
+ Examples:
165
+
166
+ You dont like the music and wanna try lunk with the next:
167
+ next
168
+
169
+ USAGE
170
+ end
171
+
172
+ def usage_prev
173
+ return <<-USAGE
174
+ usage: prev
175
+
176
+ Select the last song in the *history* playlist and plays it again.
177
+
178
+ Examples:
179
+
180
+ "OMG! I love that song, lets hear it again":
181
+ prev
182
+
183
+ USAGE
184
+ end
185
+
186
+ def usage_quit
187
+ return <<-USAGE
188
+ usage: quit
189
+
190
+ Stop playback and exit player.
191
+
192
+ Examples:
193
+
194
+ You wanna quit the player:
195
+ quit
196
+
197
+ USAGE
198
+ end
199
+
200
+ def usage_search
201
+ return <<-USAGE
202
+ usage: search (literal|criteria)
203
+
204
+ Search in the connected drives for song that fullfil the parameters criteria.
205
+ When a search is made, the results go to the *search* and *focus* playlist. From there, they can be manipulated.
206
+ To understand the results, I will explain the rules the search algorith uses:
207
+ 1. Similar criteria creates an OR filter and differents create AND filter.
208
+ Example:
209
+
210
+ search t:tres a:uno a:dos
211
+
212
+ This extract songs whose title contains "tres" and whose artist contains "uno" OR "dos" in their names
213
+
214
+ 2. Literal words form and OR
215
+
216
+ search uno dos tres
217
+
218
+ This extract songs whose title, artist name or album name contains "uno" OR "dos" OR "tres".
219
+
220
+ Examples:
221
+
222
+ Get all my Gorillaz's songs
223
+ search a:gorillaz
224
+
225
+ Im in love and I wanna hear love-related songs:
226
+ search love
227
+
228
+ USAGE
229
+ end
230
+
231
+ def usage_show
232
+ return <<-USAGE
233
+ usage: show [number|object]
234
+
235
+ Display a representation of player's objects.
236
+ Without parameters show the status of the current playback, if any.
237
+
238
+ Examples:
239
+
240
+ See how much time left of this song:
241
+ show
242
+
243
+ See what songs are in the *focus* playlist:
244
+ show @focus
245
+
246
+ USAGE
247
+ end
248
+
249
+ def usage_enqueue
250
+ return <<-USAGE
251
+ usage: enqueue [literal|number|criteria|object]
252
+
253
+ Search, pick and extract the song defined in the parameters and creates a playlist that is appended to the *queue* playlist.
254
+ Similar to a search with the literal and criteria parameters, but with number takes songs from the *focus* playlist, and with object extract the songs from the respective object.
255
+
256
+ Examples:
257
+
258
+ After the current song I want to play all Jugulator album:
259
+ enqueue b:Jugulator
260
+
261
+ Play te third song in the list after this:
262
+ enqueue 3
263
+
264
+ USAGE
265
+ end
266
+
267
+ def usage_shuffle
268
+ return <<-USAGE
269
+ usage: shuffle [boolean]
270
+
271
+ Without parameters, check the shuffle state. When parameters are provided, you can turn it on and off.
272
+
273
+ Examples:
274
+
275
+ Is shuffling?:
276
+ shuffle
277
+
278
+ Turn shuffle off:
279
+ shuffle off
280
+
281
+ USAGE
282
+ end
283
+
284
+ def usage_connect
285
+ return <<-USAGE
286
+ usage: connect (literal | path => literal)
287
+
288
+ This command allows you to create a new drive or reconnect an existing one.
289
+ A drive is a logical folder or container that groups songs by path. This player uses this concept to organize your music directories.
290
+ You can disconnect it to avoid the music search look in there.
291
+
292
+ Examples:
293
+
294
+ To connect a drive you use the following form:
295
+ connect path/to/my/music => my_library
296
+
297
+ To reconnect the same drive in the future (if you disconnect it for some reason):
298
+ connect my_library
299
+
300
+ USAGE
301
+ end
302
+
303
+ def usage_disconnect
304
+ return <<-USAGE
305
+ usage: disconnect literal
306
+
307
+ Disconnect a previously connected drive. With the drive disconnected the searches are not made in this drives.
308
+
309
+ Examples:
310
+
311
+ Disconnect a temporal download music drive:
312
+ disconnect my_downloads
313
+
314
+ USAGE
315
+ end
316
+
317
+ def usage_ff
318
+ return <<-USAGE
319
+ usage: ff [number]
320
+
321
+ Fast forward the current playback 10 seconds by default. This time can be customized passing a parameter with the seconds to fast forward.
322
+
323
+ Examples:
324
+
325
+ Fas forward 10 seconds:
326
+ ff
327
+
328
+ Fas forward 35 seconds:
329
+ ff 35
330
+
331
+ USAGE
332
+ end
333
+
334
+ def usage_fb
335
+ return <<-USAGE
336
+ usage: fb [number]
337
+
338
+ Fast backward the current playback 10 seconds by default. This time can be customized passing a parameter with the seconds to fast backward.
339
+
340
+ Examples:
341
+
342
+ Fas backward 10 seconds:
343
+ fb
344
+
345
+ Fas backward 5 seconds:
346
+ fb 5
347
+
348
+ USAGE
349
+ end
350
+
351
+ def usage_repeat
352
+ return <<-USAGE
353
+ usage: stop
354
+
355
+ Stop current playback.
356
+
357
+ Examples
358
+
359
+ Stop current playback:
360
+ stop
361
+
362
+ USAGE
363
+ end
364
+
365
+
366
+ end
367
+ end
368
+ end