mpd_client 0.0.6 → 0.1.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.
- checksums.yaml +5 -5
- data/.hound.yml +2 -0
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +11 -7
- data/Gemfile +8 -1
- data/MPD_COMMANDS.md +41 -45
- data/README.md +19 -15
- data/bin/console +4 -3
- data/examples/Gemfile +2 -0
- data/examples/idle.rb +27 -0
- data/examples/range.rb +6 -4
- data/examples/rangeid.rb +5 -3
- data/examples/search_and_replace_playlist.rb +7 -5
- data/examples/stickers.rb +10 -6
- data/lib/mpd_client.rb +394 -351
- data/lib/mpd_client/version.rb +6 -2
- data/mpd_client.gemspec +18 -11
- data/spec/mpd_client/mpd_client_spec.rb +17 -0
- data/spec/spec_helper.rb +8 -0
- metadata +30 -7
- data/Rakefile +0 -2
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mpd_client'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "mpd_client"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start
|
data/examples/Gemfile
CHANGED
data/examples/idle.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup :default
|
5
|
+
|
6
|
+
require 'logger'
|
7
|
+
require 'mpd_client'
|
8
|
+
|
9
|
+
client = MPD::Client.new
|
10
|
+
|
11
|
+
client = MPD::Client.new
|
12
|
+
client.log = Logger.new($stderr)
|
13
|
+
|
14
|
+
client.connect('localhost', 6600)
|
15
|
+
|
16
|
+
# Lists all changed systems:
|
17
|
+
# database, update, stored_playlist, playlist, player, mixer, output, options, sticker, subscription, message
|
18
|
+
#
|
19
|
+
subsystems = %w[player playlist]
|
20
|
+
|
21
|
+
loop do
|
22
|
+
resp = client.idle(*subsystems)
|
23
|
+
puts resp
|
24
|
+
end
|
25
|
+
|
26
|
+
client.close
|
27
|
+
client.disconnect
|
data/examples/range.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
Bundler.setup :default
|
3
5
|
|
@@ -5,16 +7,16 @@ require 'pp'
|
|
5
7
|
require 'logger'
|
6
8
|
require 'mpd_client'
|
7
9
|
|
8
|
-
|
10
|
+
MPD::Client.log = Logger.new($stderr)
|
9
11
|
|
10
|
-
client =
|
12
|
+
client = MPD::Client.new
|
11
13
|
client.connect('localhost', 6600)
|
12
14
|
|
13
15
|
# delete all songs from the current playlist, except for the firts ten
|
14
|
-
client.delete([10
|
16
|
+
client.delete([10])
|
15
17
|
|
16
18
|
# move the first three songs after the fifth number in the playlist
|
17
19
|
client.move([0, 3], 5)
|
18
20
|
|
19
21
|
# print songs form 5 to 10
|
20
|
-
client.playlistinfo([5, 10]).each{ |s| puts "#{s['artist']} - #{s['title']}"}
|
22
|
+
client.playlistinfo([5, 10]).each { |s| puts "#{s['artist']} - #{s['title']}" }
|
data/examples/rangeid.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
Bundler.setup :default
|
3
5
|
|
@@ -5,9 +7,9 @@ require 'pp'
|
|
5
7
|
require 'logger'
|
6
8
|
require 'mpd_client'
|
7
9
|
|
8
|
-
|
10
|
+
MPD::Client.log = Logger.new($stderr)
|
9
11
|
|
10
|
-
client =
|
12
|
+
client = MPD::Client.new
|
11
13
|
client.connect('localhost', 6600)
|
12
14
|
|
13
15
|
# Get id of the first song in the playllist
|
@@ -16,7 +18,7 @@ pp "#{song['artist']} - #{song['title']}"
|
|
16
18
|
song_id = song['id']
|
17
19
|
|
18
20
|
# Specifies the portion of the song that shall be played
|
19
|
-
client.rangeid(song_id, [60,70])
|
21
|
+
client.rangeid(song_id, [60, 70])
|
20
22
|
|
21
23
|
# Play the playlist at song 1
|
22
24
|
client.play(1)
|
@@ -1,24 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
Bundler.setup :default
|
3
5
|
|
4
6
|
require 'logger'
|
5
7
|
require 'mpd_client'
|
6
8
|
|
7
|
-
#
|
9
|
+
# MPD::Client.log = Logger.new($stderr)
|
8
10
|
|
9
|
-
client =
|
11
|
+
client = MPD::Client.new
|
10
12
|
|
11
13
|
type = ARGV[0]
|
12
14
|
what = ARGV[1]
|
13
15
|
|
14
|
-
client =
|
16
|
+
client = MPD::Client.new
|
15
17
|
client.log = Logger.new($stderr)
|
16
18
|
|
17
19
|
# Connecting to the server
|
18
20
|
client.connect('localhost', 6600)
|
19
21
|
|
20
22
|
puts "MPD version: #{client.mpd_version}"
|
21
|
-
puts "mpd_client version: #{
|
23
|
+
puts "mpd_client version: #{MPD::Client::VERSION}"
|
22
24
|
|
23
25
|
client.stop
|
24
26
|
client.clear # clear the current playlist
|
@@ -32,7 +34,7 @@ songs = client.search(type, what)
|
|
32
34
|
|
33
35
|
client.command_list_ok_begin # start a command list to speed up operations
|
34
36
|
songs.each do |song|
|
35
|
-
client.add(song['file']) if song.
|
37
|
+
client.add(song['file']) if song.key?('file')
|
36
38
|
end
|
37
39
|
client.command_list_end
|
38
40
|
|
data/examples/stickers.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
Bundler.setup :default
|
3
5
|
|
4
6
|
require 'logger'
|
5
7
|
require 'mpd_client'
|
6
8
|
|
7
|
-
|
9
|
+
MPD::Client.log = Logger.new($stderr)
|
8
10
|
|
9
11
|
# Stickers
|
10
12
|
# http://www.musicpd.org/doc/protocol/ch03s07.html
|
11
13
|
|
12
|
-
client =
|
14
|
+
client = MPD::Client.new
|
13
15
|
|
14
16
|
# Connecting to the server
|
15
17
|
client.connect('/run/mpd/socket')
|
16
18
|
|
17
19
|
puts "MPD version: #{client.mpd_version}"
|
18
|
-
puts "mpd_client version: #{
|
20
|
+
puts "mpd_client version: #{MPD::Client::VERSION}"
|
19
21
|
|
20
|
-
uri =
|
22
|
+
uri = 'world/j/Jane Air/2012.Иллюзия полёта/12. Любить любовь.ogg'
|
21
23
|
|
22
24
|
# sticker set {TYPE} {URI} {NAME} {VALUE}
|
23
|
-
# Adds a sticker value to the specified object.
|
25
|
+
# Adds a sticker value to the specified object.
|
26
|
+
# If a sticker item with that name already exists, it is replaced.
|
24
27
|
#
|
25
28
|
client.sticker_set('song', uri, 'rating', '1')
|
26
29
|
|
@@ -41,7 +44,8 @@ puts client.sticker_list('song', uri)
|
|
41
44
|
puts client.sticker_find('song', '/', 'rating')
|
42
45
|
|
43
46
|
# sticker delete {TYPE} {URI} [NAME]
|
44
|
-
# Deletes a sticker value from the specified object.
|
47
|
+
# Deletes a sticker value from the specified object.
|
48
|
+
# If you do not specify a sticker name, all sticker values are deleted.
|
45
49
|
#
|
46
50
|
client.sticker_delete('song', uri, 'rating')
|
47
51
|
|
data/lib/mpd_client.rb
CHANGED
@@ -1,423 +1,466 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'socket'
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# http://
|
11
|
-
# http://
|
12
|
-
#
|
13
|
-
COMMANDS = {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
}
|
128
|
-
|
129
|
-
# The
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
4
|
+
require 'mpd_client/version'
|
5
|
+
|
6
|
+
module MPD
|
7
|
+
HELLO_PREFIX = 'OK MPD '
|
8
|
+
ERROR_PREFIX = 'ACK '
|
9
|
+
SUCCESS = 'OK'
|
10
|
+
NEXT = 'list_OK'
|
11
|
+
|
12
|
+
# MPD changelog: http://git.musicpd.org/cgit/master/mpd.git/plain/NEWS
|
13
|
+
# http://www.musicpd.org/doc/protocol/command_reference.html
|
14
|
+
# http://git.musicpd.org/cgit/cirrus/mpd.git/plain/doc/protocol.xml
|
15
|
+
COMMANDS = {
|
16
|
+
# Status Commands
|
17
|
+
'clearerror' => 'fetch_nothing',
|
18
|
+
'currentsong' => 'fetch_object',
|
19
|
+
'idle' => 'fetch_list',
|
20
|
+
'noidle' => '',
|
21
|
+
'status' => 'fetch_object',
|
22
|
+
'stats' => 'fetch_object',
|
23
|
+
# Playback Option Commands
|
24
|
+
'consume' => 'fetch_nothing',
|
25
|
+
'crossfade' => 'fetch_nothing',
|
26
|
+
'mixrampdb' => 'fetch_nothing',
|
27
|
+
'mixrampdelay' => 'fetch_nothing',
|
28
|
+
'random' => 'fetch_nothing',
|
29
|
+
'repeat' => 'fetch_nothing',
|
30
|
+
'setvol' => 'fetch_nothing',
|
31
|
+
'single' => 'fetch_nothing',
|
32
|
+
'replay_gain_mode' => 'fetch_nothing',
|
33
|
+
'replay_gain_status' => 'fetch_item',
|
34
|
+
'volume' => 'fetch_nothing',
|
35
|
+
# Playback Control Commands
|
36
|
+
'next' => 'fetch_nothing',
|
37
|
+
'pause' => 'fetch_nothing',
|
38
|
+
'play' => 'fetch_nothing',
|
39
|
+
'playid' => 'fetch_nothing',
|
40
|
+
'previous' => 'fetch_nothing',
|
41
|
+
'seek' => 'fetch_nothing',
|
42
|
+
'seekid' => 'fetch_nothing',
|
43
|
+
'seekcur' => 'fetch_nothing',
|
44
|
+
'stop' => 'fetch_nothing',
|
45
|
+
# Playlist Commands
|
46
|
+
'add' => 'fetch_nothing',
|
47
|
+
'addid' => 'fetch_item',
|
48
|
+
'addtagid' => 'fetch_nothing',
|
49
|
+
'cleartagid' => 'fetch_nothing',
|
50
|
+
'clear' => 'fetch_nothing',
|
51
|
+
'delete' => 'fetch_nothing',
|
52
|
+
'deleteid' => 'fetch_nothing',
|
53
|
+
'move' => 'fetch_nothing',
|
54
|
+
'moveid' => 'fetch_nothing',
|
55
|
+
'playlist' => 'fetch_playlist',
|
56
|
+
'playlistfind' => 'fetch_songs',
|
57
|
+
'playlistid' => 'fetch_songs',
|
58
|
+
'playlistinfo' => 'fetch_songs',
|
59
|
+
'playlistsearch' => 'fetch_songs',
|
60
|
+
'plchanges' => 'fetch_songs',
|
61
|
+
'plchangesposid' => 'fetch_changes',
|
62
|
+
'prio' => 'fetch_nothing',
|
63
|
+
'prioid' => 'fetch_nothing',
|
64
|
+
'rangeid' => 'fetch_nothing',
|
65
|
+
'shuffle' => 'fetch_nothing',
|
66
|
+
'swap' => 'fetch_nothing',
|
67
|
+
'swapid' => 'fetch_nothing',
|
68
|
+
# Stored Playlist Commands
|
69
|
+
'listplaylist' => 'fetch_list',
|
70
|
+
'listplaylistinfo' => 'fetch_songs',
|
71
|
+
'listplaylists' => 'fetch_playlists',
|
72
|
+
'load' => 'fetch_nothing',
|
73
|
+
'playlistadd' => 'fetch_nothing',
|
74
|
+
'playlistclear' => 'fetch_nothing',
|
75
|
+
'playlistdelete' => 'fetch_nothing',
|
76
|
+
'playlistmove' => 'fetch_nothing',
|
77
|
+
'rename' => 'fetch_nothing',
|
78
|
+
'rm' => 'fetch_nothing',
|
79
|
+
'save' => 'fetch_nothing',
|
80
|
+
# Database Commands
|
81
|
+
'count' => 'fetch_object',
|
82
|
+
'find' => 'fetch_songs',
|
83
|
+
'findadd' => 'fetch_nothing',
|
84
|
+
'list' => 'fetch_list',
|
85
|
+
'listall' => 'fetch_database',
|
86
|
+
'listallinfo' => 'fetch_database',
|
87
|
+
'listfiles' => 'fetch_database',
|
88
|
+
'lsinfo' => 'fetch_database',
|
89
|
+
'search' => 'fetch_songs',
|
90
|
+
'searchadd' => 'fetch_nothing',
|
91
|
+
'searchaddp1' => 'fetch_nothing',
|
92
|
+
'update' => 'fetch_item',
|
93
|
+
'rescan' => 'fetch_item',
|
94
|
+
'readcomments' => 'fetch_object',
|
95
|
+
# Mounts and neighbors
|
96
|
+
'mount' => 'fetch_nothing',
|
97
|
+
'unmount' => 'fetch_nothing',
|
98
|
+
'listmounts' => 'fetch_mounts',
|
99
|
+
'listneighbors' => 'fetch_neighbors',
|
100
|
+
# Sticker Commands
|
101
|
+
'sticker get' => 'fetch_sticker',
|
102
|
+
'sticker set' => 'fetch_nothing',
|
103
|
+
'sticker delete' => 'fetch_nothing',
|
104
|
+
'sticker list' => 'fetch_stickers',
|
105
|
+
'sticker find' => 'fetch_songs',
|
106
|
+
# Connection Commands
|
107
|
+
'close' => '',
|
108
|
+
'kill' => '',
|
109
|
+
'password' => 'fetch_nothing',
|
110
|
+
'ping' => 'fetch_nothing',
|
111
|
+
# Audio Output Commands
|
112
|
+
'disableoutput' => 'fetch_nothing',
|
113
|
+
'enableoutput' => 'fetch_nothing',
|
114
|
+
'outputs' => 'fetch_outputs',
|
115
|
+
'toggleoutput' => 'fetch_nothing',
|
116
|
+
# Reflection Commands
|
117
|
+
'config' => 'fetch_item',
|
118
|
+
'commands' => 'fetch_list',
|
119
|
+
'notcommands' => 'fetch_list',
|
120
|
+
'tagtypes' => 'fetch_list',
|
121
|
+
'urlhandlers' => 'fetch_list',
|
122
|
+
'decoders' => 'fetch_plugins',
|
123
|
+
# Client To Client
|
124
|
+
'subscribe' => 'fetch_nothing',
|
125
|
+
'unsubscribe' => 'fetch_nothing',
|
126
|
+
'channels' => 'fetch_list',
|
127
|
+
'readmessages' => 'fetch_messages',
|
128
|
+
'sendmessage' => 'fetch_nothing'
|
129
|
+
}.freeze
|
130
|
+
|
131
|
+
# The `MPD::Client` is used for interactions with a MPD server.
|
132
|
+
#
|
133
|
+
# Example:
|
134
|
+
#
|
135
|
+
# ```ruby
|
136
|
+
# require 'mpd_client'
|
137
|
+
# require 'logger'
|
138
|
+
#
|
139
|
+
# client = MPD::Client.new
|
140
|
+
# client.log = Logger.new($stderr)
|
141
|
+
# client.connect('/var/run/mpd/socket')
|
142
|
+
# ```
|
143
|
+
class Client
|
144
|
+
attr_reader :mpd_version
|
145
|
+
|
146
|
+
class << self
|
147
|
+
# Default logger for all `MPD::Client`` instances
|
148
|
+
#
|
149
|
+
# ```ruby
|
150
|
+
# MPD::Client.log = Logger.new($stderr)
|
151
|
+
# ```
|
152
|
+
attr_accessor :log
|
153
|
+
|
154
|
+
def connect(host = 'localhost', port = 6600)
|
155
|
+
client = MPD::Client.new
|
156
|
+
client.connect(host, port)
|
157
|
+
|
158
|
+
client
|
159
|
+
end
|
160
|
+
|
161
|
+
def add_command(name, retval)
|
162
|
+
escaped_name = name.tr(' ', '_')
|
163
|
+
define_method escaped_name.to_sym do |*args|
|
164
|
+
ensure_connected
|
165
|
+
|
166
|
+
execute(name, *args, retval)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def remove_command(name)
|
171
|
+
raise "Can't remove not existent '#{name}' command" unless method_defined? name.to_sym
|
172
|
+
remove_method name.to_sym
|
154
173
|
end
|
155
174
|
end
|
156
175
|
|
157
|
-
def
|
158
|
-
|
159
|
-
|
176
|
+
def initialize
|
177
|
+
@mutex = Mutex.new
|
178
|
+
reset
|
160
179
|
end
|
161
|
-
end
|
162
180
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
181
|
+
def connect(host = 'localhost', port = 6600)
|
182
|
+
@host = host
|
183
|
+
@port = port
|
167
184
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
reconnect
|
172
|
-
|
185
|
+
reconnect
|
186
|
+
end
|
187
|
+
|
188
|
+
def reconnect
|
189
|
+
log&.info("MPD (re)connect #{@host}, #{@port}")
|
190
|
+
|
191
|
+
@socket = if @host.start_with?('/')
|
192
|
+
UNIXSocket.new(@host)
|
193
|
+
else
|
194
|
+
TCPSocket.new(@host, @port)
|
195
|
+
end
|
173
196
|
|
174
|
-
def reconnect
|
175
|
-
log.info("MPD (re)connect #{@host}, #{@port}") if log
|
176
|
-
if @host.start_with?('/')
|
177
|
-
@socket = UNIXSocket.new(@host)
|
178
|
-
hello
|
179
|
-
else
|
180
|
-
@socket = TCPSocket.new(@host, @port)
|
181
197
|
hello
|
198
|
+
@connected = true
|
182
199
|
end
|
183
|
-
end
|
184
200
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
201
|
+
def disconnect
|
202
|
+
log&.info('MPD disconnect')
|
203
|
+
@socket.close
|
204
|
+
reset
|
205
|
+
end
|
190
206
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
207
|
+
def reset
|
208
|
+
@mpd_version = nil
|
209
|
+
@command_list = nil
|
210
|
+
@socket = nil
|
211
|
+
@log = nil
|
212
|
+
@connected = false
|
213
|
+
end
|
214
|
+
|
215
|
+
def connected?
|
216
|
+
@connected
|
217
|
+
end
|
197
218
|
|
198
|
-
|
199
|
-
|
200
|
-
|
219
|
+
# http://www.musicpd.org/doc/protocol/ch01s04.html
|
220
|
+
def command_list_ok_begin
|
221
|
+
raise 'Already in command list' unless @command_list.nil?
|
222
|
+
write_command('command_list_ok_begin')
|
223
|
+
@command_list = []
|
224
|
+
end
|
201
225
|
|
202
|
-
|
203
|
-
|
226
|
+
def command_list_end
|
227
|
+
raise 'Not in command list' if @command_list.nil?
|
228
|
+
write_command('command_list_end')
|
204
229
|
|
205
|
-
|
206
|
-
|
207
|
-
def log
|
208
|
-
@log || MPDClient.log
|
209
|
-
end
|
230
|
+
fetch_command_list
|
231
|
+
end
|
210
232
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
end
|
233
|
+
# The current logger. If no logger has been set MPD::Client.log is used
|
234
|
+
def log
|
235
|
+
@log || MPD::Client.log
|
236
|
+
end
|
216
237
|
|
217
|
-
|
238
|
+
# Sets the +logger+ used by this instance of MPD::Client
|
239
|
+
attr_writer :log
|
218
240
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
241
|
+
private
|
242
|
+
|
243
|
+
def ensure_connected
|
244
|
+
raise 'Please connect to MPD server' unless connected?
|
245
|
+
end
|
246
|
+
|
247
|
+
def execute(command, *args, retval)
|
248
|
+
@mutex.synchronize do
|
225
249
|
write_command(command, *args)
|
226
|
-
|
250
|
+
|
251
|
+
if !@command_list.nil?
|
252
|
+
@command_list << retval
|
253
|
+
else
|
254
|
+
eval retval
|
255
|
+
end
|
227
256
|
end
|
228
257
|
end
|
229
|
-
end
|
230
258
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
259
|
+
def write_line(line)
|
260
|
+
begin
|
261
|
+
@socket.puts line
|
262
|
+
rescue Errno::EPIPE
|
263
|
+
reconnect
|
264
|
+
@socket.puts line
|
265
|
+
end
|
266
|
+
@socket.flush
|
237
267
|
end
|
238
|
-
@socket.flush
|
239
|
-
end
|
240
268
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
269
|
+
def write_command(command, *args)
|
270
|
+
parts = [command]
|
271
|
+
args.each do |arg|
|
272
|
+
line = if arg.is_a?(Array)
|
273
|
+
arg.size == 1 ? "\"#{arg[0].to_i}:\"" : "\"#{arg[0].to_i}:#{arg[1].to_i}\""
|
274
|
+
else
|
275
|
+
"\"#{escape(arg)}\""
|
276
|
+
end
|
277
|
+
|
278
|
+
parts << line
|
248
279
|
end
|
280
|
+
# log.debug("Calling MPD: #{command}#{args}") if log
|
281
|
+
log&.debug("Calling MPD: #{parts.join(' ')}")
|
282
|
+
write_line(parts.join(' '))
|
249
283
|
end
|
250
|
-
#log.debug("Calling MPD: #{command}#{args}") if log
|
251
|
-
log.debug("Calling MPD: #{parts.join(' ')}") if log
|
252
|
-
write_line(parts.join(' '))
|
253
|
-
end
|
254
284
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
285
|
+
def read_line
|
286
|
+
line = @socket.gets.force_encoding('utf-8')
|
287
|
+
raise 'Connection lost while reading line' unless line.end_with?("\n")
|
288
|
+
line.chomp!
|
289
|
+
if line.start_with?(ERROR_PREFIX)
|
290
|
+
error = line[/#{ERROR_PREFIX}(.*)/, 1].strip
|
291
|
+
raise error
|
292
|
+
end
|
263
293
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
294
|
+
if !@command_list.nil?
|
295
|
+
return if line == NEXT
|
296
|
+
raise "Got unexpected '#{SUCCESS}'" if line == SUCCESS
|
297
|
+
elsif line == SUCCESS
|
298
|
+
return
|
299
|
+
end
|
270
300
|
|
271
|
-
|
272
|
-
|
301
|
+
line
|
302
|
+
end
|
273
303
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
304
|
+
def read_pair(separator)
|
305
|
+
line = read_line
|
306
|
+
return if line.nil?
|
307
|
+
pair = line.split(separator, 2)
|
308
|
+
raise "Could now parse pair: '#{line}'" if pair.size < 2
|
279
309
|
|
280
|
-
|
281
|
-
|
310
|
+
pair # Array
|
311
|
+
end
|
282
312
|
|
283
|
-
|
284
|
-
|
285
|
-
pair = read_pair(separator)
|
286
|
-
while pair
|
287
|
-
result << pair
|
313
|
+
def read_pairs(separator = ': ')
|
314
|
+
result = []
|
288
315
|
pair = read_pair(separator)
|
289
|
-
|
316
|
+
while pair
|
317
|
+
result << pair
|
318
|
+
pair = read_pair(separator)
|
319
|
+
end
|
290
320
|
|
291
|
-
|
292
|
-
|
321
|
+
result
|
322
|
+
end
|
293
323
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
return pairs[0][1]
|
298
|
-
end
|
324
|
+
def fetch_item
|
325
|
+
pairs = read_pairs
|
326
|
+
return nil if pairs.size != 1
|
299
327
|
|
300
|
-
|
301
|
-
|
302
|
-
raise "Got unexpected return value: #{line}" unless line.nil?
|
303
|
-
end
|
328
|
+
pairs[0][1]
|
329
|
+
end
|
304
330
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
read_pairs.each do |key, value|
|
309
|
-
if key != seen
|
310
|
-
if seen != nil
|
311
|
-
raise "Expected key '#{seen}', got '#{key}'"
|
312
|
-
end
|
313
|
-
seen = key
|
314
|
-
end
|
315
|
-
result << value
|
331
|
+
def fetch_nothing
|
332
|
+
line = read_line
|
333
|
+
raise "Got unexpected value: #{line}" unless line.nil?
|
316
334
|
end
|
317
335
|
|
318
|
-
|
319
|
-
|
336
|
+
def fetch_list
|
337
|
+
result = []
|
338
|
+
seen = nil
|
320
339
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
result <<
|
328
|
-
obj = {}
|
329
|
-
elsif obj.include?(key)
|
330
|
-
obj[key] << value
|
340
|
+
read_pairs.each do |key, value|
|
341
|
+
if key != seen
|
342
|
+
raise "Expected key '#{seen}', got '#{key}'" unless seen.nil?
|
343
|
+
seen = key
|
344
|
+
end
|
345
|
+
|
346
|
+
result << value
|
331
347
|
end
|
332
|
-
obj[key] = value
|
333
|
-
end
|
334
348
|
|
335
|
-
|
349
|
+
result
|
350
|
+
end
|
336
351
|
|
337
|
-
|
338
|
-
|
352
|
+
def fetch_objects(delimeters = [])
|
353
|
+
result = []
|
354
|
+
obj = {}
|
355
|
+
read_pairs.each do |key, value|
|
356
|
+
key = key.downcase
|
357
|
+
if delimeters.include?(key)
|
358
|
+
result << obj unless obj.empty?
|
359
|
+
obj = {}
|
360
|
+
elsif obj.include?(key)
|
361
|
+
obj[key] << value
|
362
|
+
end
|
363
|
+
obj[key] = value
|
364
|
+
end
|
339
365
|
|
340
|
-
|
341
|
-
objs = fetch_objects
|
342
|
-
return objs ? objs[0] : {}
|
343
|
-
end
|
366
|
+
result << obj unless obj.empty?
|
344
367
|
|
345
|
-
|
368
|
+
result
|
369
|
+
end
|
346
370
|
|
347
|
-
|
371
|
+
def fetch_object
|
372
|
+
objs = fetch_objects
|
348
373
|
|
349
|
-
|
374
|
+
objs ? objs[0] : {}
|
375
|
+
end
|
350
376
|
|
351
|
-
|
377
|
+
def fetch_changes
|
378
|
+
fetch_objects(['cpos'])
|
379
|
+
end
|
352
380
|
|
353
|
-
|
381
|
+
def fetch_songs
|
382
|
+
fetch_objects(['file'])
|
383
|
+
end
|
354
384
|
|
355
|
-
|
385
|
+
def fetch_mounts
|
386
|
+
fetch_objects(['mount'])
|
387
|
+
end
|
356
388
|
|
357
|
-
|
389
|
+
def fetch_neighbors
|
390
|
+
fetch_objects(['neighbor'])
|
391
|
+
end
|
358
392
|
|
359
|
-
|
393
|
+
def fetch_messages
|
394
|
+
fetch_objects('channel')
|
395
|
+
end
|
360
396
|
|
361
|
-
|
397
|
+
def fetch_outputs
|
398
|
+
fetch_objects(['outputid'])
|
399
|
+
end
|
362
400
|
|
363
|
-
|
364
|
-
|
365
|
-
read_pairs(':').each do |key, value|
|
366
|
-
result << value
|
401
|
+
def fetch_plugins
|
402
|
+
fetch_objects(['plugin'])
|
367
403
|
end
|
368
404
|
|
369
|
-
|
370
|
-
|
405
|
+
def fetch_database
|
406
|
+
fetch_objects(%w[file directory playlist])
|
407
|
+
end
|
371
408
|
|
372
|
-
|
373
|
-
|
374
|
-
read_pairs.each do |key, sticker|
|
375
|
-
value = sticker.split('=', 2)
|
376
|
-
raise "Could now parse sticker: #{sticker}" if value.size < 2
|
377
|
-
result << Hash[*value]
|
409
|
+
def fetch_playlists
|
410
|
+
fetch_objects(['playlist'])
|
378
411
|
end
|
379
412
|
|
380
|
-
|
381
|
-
|
413
|
+
def fetch_playlist
|
414
|
+
result = []
|
415
|
+
read_pairs(':').each do |_key, value|
|
416
|
+
result << value
|
417
|
+
end
|
382
418
|
|
383
|
-
|
419
|
+
result
|
420
|
+
end
|
384
421
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
422
|
+
def fetch_stickers
|
423
|
+
result = []
|
424
|
+
read_pairs.each do |_key, sticker|
|
425
|
+
value = sticker.split('=', 2)
|
426
|
+
raise "Could now parse sticker: #{sticker}" if value.size < 2
|
427
|
+
result << Hash[*value]
|
390
428
|
end
|
391
|
-
|
392
|
-
|
429
|
+
|
430
|
+
result
|
393
431
|
end
|
394
432
|
|
395
|
-
|
396
|
-
|
433
|
+
def fetch_sticker
|
434
|
+
fetch_stickers[0]
|
435
|
+
end
|
397
436
|
|
437
|
+
def fetch_command_list
|
438
|
+
result = []
|
439
|
+
begin
|
440
|
+
@command_list.each do |retval|
|
441
|
+
result << (eval retval)
|
442
|
+
end
|
443
|
+
ensure
|
444
|
+
@command_list = nil
|
445
|
+
end
|
398
446
|
|
399
|
-
|
400
|
-
|
401
|
-
raise "Connection lost while reading MPD hello" unless line.end_with?("\n")
|
402
|
-
line.chomp!
|
403
|
-
raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
|
404
|
-
@mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
|
405
|
-
end
|
447
|
+
result
|
448
|
+
end
|
406
449
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
450
|
+
def hello
|
451
|
+
line = @socket.gets
|
452
|
+
raise 'Connection lost while reading MPD hello' unless line.end_with?("\n")
|
453
|
+
line.chomp!
|
454
|
+
raise "Got invalid MPD hello: #{line}" unless line.start_with?(HELLO_PREFIX)
|
455
|
+
@mpd_version = line[/#{HELLO_PREFIX}(.*)/, 1]
|
456
|
+
end
|
413
457
|
|
414
|
-
|
415
|
-
|
458
|
+
def escape(text)
|
459
|
+
text.to_s.gsub('\\', '\\\\').gsub('"', '\\"')
|
460
|
+
end
|
416
461
|
end
|
417
|
-
|
418
462
|
end
|
419
463
|
|
420
|
-
COMMANDS.each_pair do |name, callback|
|
421
|
-
|
464
|
+
MPD::COMMANDS.each_pair do |name, callback|
|
465
|
+
MPD::Client.add_command(name, callback)
|
422
466
|
end
|
423
|
-
|