ruby-mpd 0.3.1 → 0.3.2
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 +4 -4
- data/.gitignore +13 -14
- data/Gemfile +6 -0
- data/LICENSE.txt +339 -0
- data/README.md +276 -0
- data/Rakefile +1 -1
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/ruby-mpd.rb +58 -50
- data/lib/ruby-mpd/parser.rb +4 -4
- data/lib/ruby-mpd/playlist.rb +3 -3
- data/lib/ruby-mpd/plugins/database.rb +8 -3
- data/lib/ruby-mpd/plugins/information.rb +1 -1
- data/lib/ruby-mpd/plugins/outputs.rb +7 -0
- data/lib/ruby-mpd/plugins/queue.rb +1 -1
- data/lib/ruby-mpd/song.rb +10 -1
- data/lib/ruby-mpd/version.rb +2 -2
- data/ruby-mpd.gemspec +23 -17
- metadata +69 -10
- data/README.rdoc +0 -296
- data/test/test_parser.rb +0 -52
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
1
2
|
require 'rake/testtask'
|
2
3
|
|
3
4
|
Rake::TestTask.new(:test) do |test|
|
@@ -6,7 +7,6 @@ end
|
|
6
7
|
|
7
8
|
desc "Open an irb session preloaded with this API"
|
8
9
|
task :console do
|
9
|
-
$:.unshift(File.expand_path('../lib', __FILE__))
|
10
10
|
require 'ruby-mpd'
|
11
11
|
require 'irb'
|
12
12
|
ARGV.clear
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby/mpd"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/ruby-mpd.rb
CHANGED
@@ -54,20 +54,13 @@ class MPD
|
|
54
54
|
@hostname = hostname
|
55
55
|
@port = port
|
56
56
|
@options = {callbacks: false}.merge(opts)
|
57
|
+
@password = opts.delete(:password) || nil
|
57
58
|
reset_vars
|
58
59
|
|
59
60
|
@mutex = Mutex.new
|
60
61
|
@callbacks = {}
|
61
62
|
end
|
62
63
|
|
63
|
-
# Initialize instance variables on new object, or on disconnect.
|
64
|
-
def reset_vars
|
65
|
-
@socket = nil
|
66
|
-
@version = nil
|
67
|
-
@tags = nil
|
68
|
-
end
|
69
|
-
private :reset_vars
|
70
|
-
|
71
64
|
# This will register a block callback that will trigger whenever
|
72
65
|
# that specific event happens.
|
73
66
|
#
|
@@ -93,44 +86,9 @@ class MPD
|
|
93
86
|
# @return [void]
|
94
87
|
def emit(event, *args)
|
95
88
|
return unless @callbacks[event]
|
96
|
-
@callbacks[event].each { |handle| handle.call
|
89
|
+
@callbacks[event].each { |handle| handle.call(*args) }
|
97
90
|
end
|
98
91
|
|
99
|
-
# Constructs a callback loop thread and/or resumes it.
|
100
|
-
# @return [Thread]
|
101
|
-
def callback_thread
|
102
|
-
@cb_thread ||= Thread.new(self) do |mpd|
|
103
|
-
old_status = {}
|
104
|
-
while true
|
105
|
-
status = mpd.status rescue {}
|
106
|
-
|
107
|
-
status[:connection] = mpd.connected?
|
108
|
-
|
109
|
-
status[:time] ||= [nil, nil] # elapsed, total
|
110
|
-
status[:audio] ||= [nil, nil, nil] # samp, bits, chans
|
111
|
-
status[:song] = mpd.current_song
|
112
|
-
|
113
|
-
status.each do |key, val|
|
114
|
-
next if val == old_status[key] # skip unchanged keys
|
115
|
-
emit key, *val # splat arrays
|
116
|
-
end
|
117
|
-
|
118
|
-
old_status = status
|
119
|
-
sleep 0.1
|
120
|
-
|
121
|
-
unless status[:connection] || Thread.current[:stop]
|
122
|
-
sleep 2
|
123
|
-
mpd.connect rescue nil
|
124
|
-
end
|
125
|
-
|
126
|
-
Thread.stop if Thread.current[:stop]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
@cb_thread[:stop] = false
|
130
|
-
@cb_thread.run if @cb_thread.stop?
|
131
|
-
end
|
132
|
-
private :callback_thread
|
133
|
-
|
134
92
|
# Connect to the daemon.
|
135
93
|
#
|
136
94
|
# When called without any arguments, this will just connect to the server
|
@@ -139,16 +97,16 @@ class MPD
|
|
139
97
|
# @return [true] Successfully connected.
|
140
98
|
# @raise [MPDError] If connect is called on an already connected instance.
|
141
99
|
def connect(callbacks = nil)
|
142
|
-
raise ConnectionError, 'Already connected!' if
|
143
|
-
|
144
|
-
@socket = File.exists?(@hostname) ? UNIXSocket.new(@hostname) : TCPSocket.new(@hostname, @port)
|
100
|
+
raise ConnectionError, 'Already connected!' if connected?
|
145
101
|
|
146
102
|
# by protocol, we need to get a 'OK MPD <version>' reply
|
147
103
|
# should we fail to do so, the connection was unsuccessful
|
148
|
-
unless response =
|
104
|
+
unless response = socket.gets
|
149
105
|
reset_vars
|
150
106
|
raise ConnectionError, 'Unable to connect (possibly too many connections open)'
|
151
107
|
end
|
108
|
+
|
109
|
+
authenticate
|
152
110
|
@version = response.chomp.gsub('OK MPD ', '') # Read the version
|
153
111
|
|
154
112
|
if callbacks
|
@@ -208,6 +166,10 @@ class MPD
|
|
208
166
|
send_command :password, pass
|
209
167
|
end
|
210
168
|
|
169
|
+
def authenticate
|
170
|
+
send_command(:password, @password) if @password
|
171
|
+
end
|
172
|
+
|
211
173
|
# Ping the server.
|
212
174
|
# @macro returnraise
|
213
175
|
def ping
|
@@ -224,7 +186,7 @@ class MPD
|
|
224
186
|
# @return (see #handle_server_response)
|
225
187
|
# @raise [MPDError] if the command failed.
|
226
188
|
def send_command(command, *args)
|
227
|
-
raise ConnectionError, "Not connected to the server!"
|
189
|
+
raise ConnectionError, "Not connected to the server!" unless @socket
|
228
190
|
|
229
191
|
@mutex.synchronize do
|
230
192
|
begin
|
@@ -238,7 +200,49 @@ class MPD
|
|
238
200
|
end
|
239
201
|
end
|
240
202
|
|
241
|
-
|
203
|
+
private
|
204
|
+
|
205
|
+
# Initialize instance variables on new object, or on disconnect.
|
206
|
+
def reset_vars
|
207
|
+
@socket = nil
|
208
|
+
@version = nil
|
209
|
+
@tags = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
# Constructs a callback loop thread and/or resumes it.
|
213
|
+
# @return [Thread]
|
214
|
+
def callback_thread
|
215
|
+
@cb_thread ||= Thread.new(self) do |mpd|
|
216
|
+
old_status = {}
|
217
|
+
while true
|
218
|
+
status = mpd.status rescue {}
|
219
|
+
|
220
|
+
status[:connection] = mpd.connected?
|
221
|
+
|
222
|
+
status[:time] ||= [nil, nil] # elapsed, total
|
223
|
+
status[:audio] ||= [nil, nil, nil] # samp, bits, chans
|
224
|
+
status[:song] = mpd.current_song rescue nil
|
225
|
+
status[:updating_db] ||= nil
|
226
|
+
|
227
|
+
status.each do |key, val|
|
228
|
+
next if val == old_status[key] # skip unchanged keys
|
229
|
+
emit key, *val # splat arrays
|
230
|
+
end
|
231
|
+
|
232
|
+
old_status = status
|
233
|
+
sleep 0.1
|
234
|
+
|
235
|
+
unless status[:connection] || Thread.current[:stop]
|
236
|
+
sleep 2
|
237
|
+
mpd.connect rescue nil
|
238
|
+
end
|
239
|
+
|
240
|
+
Thread.stop if Thread.current[:stop]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
@cb_thread[:stop] = false
|
244
|
+
@cb_thread.run if @cb_thread.stop?
|
245
|
+
end
|
242
246
|
|
243
247
|
# Handles the server's response (called inside {#send_command}).
|
244
248
|
# Repeatedly reads the server's response from the socket.
|
@@ -265,6 +269,10 @@ class MPD
|
|
265
269
|
raise SERVER_ERRORS[err[:code].to_i], "[#{err[:command]}] #{err[:message]}"
|
266
270
|
end
|
267
271
|
|
272
|
+
def socket
|
273
|
+
@socket ||= File.exists?(@hostname) ? UNIXSocket.new(@hostname) : TCPSocket.new(@hostname, @port)
|
274
|
+
end
|
275
|
+
|
268
276
|
SERVER_ERRORS = {
|
269
277
|
1 => NotListError,
|
270
278
|
2 => ServerArgumentError,
|
data/lib/ruby-mpd/parser.rb
CHANGED
@@ -94,7 +94,7 @@ class MPD
|
|
94
94
|
def build_hash(string)
|
95
95
|
return {} if string.nil?
|
96
96
|
|
97
|
-
string.
|
97
|
+
string.lines.each_with_object({}) do |line, hash|
|
98
98
|
key, object = parse_line(line)
|
99
99
|
|
100
100
|
# if val appears more than once, make an array of vals.
|
@@ -109,19 +109,19 @@ class MPD
|
|
109
109
|
# Converts the response to MPD::Song objects.
|
110
110
|
# @return [Array<MPD::Song>] An array of songs.
|
111
111
|
def build_songs_list(array)
|
112
|
-
return array.map { |hash| Song.new(hash) }
|
112
|
+
return array.map { |hash| Song.new(self, hash) }
|
113
113
|
end
|
114
114
|
|
115
115
|
# Remove lines which we don't want.
|
116
116
|
def filter_lines(string, filter)
|
117
|
-
string.
|
117
|
+
string.lines.reject {|line| line =~ /(#{filter.join('|')}):/}.join
|
118
118
|
end
|
119
119
|
|
120
120
|
# Make chunks from string.
|
121
121
|
# @return [Array<String>]
|
122
122
|
def make_chunks(string)
|
123
123
|
first_key = string.match(/\A(.+?):\s?/)[1]
|
124
|
-
|
124
|
+
string.split(/\n(?=#{first_key})/).map(&:strip)
|
125
125
|
end
|
126
126
|
|
127
127
|
# Parses the response, determining per-command on what parsing logic
|
data/lib/ruby-mpd/playlist.rb
CHANGED
@@ -26,9 +26,9 @@ class MPD
|
|
26
26
|
def songs
|
27
27
|
result = @mpd.send_command(:listplaylistinfo, @name)
|
28
28
|
if result.to_s =~ URI::regexp
|
29
|
-
Song.new({:file => result, :time => 0})
|
29
|
+
Song.new(@mpd, {:file => result, :time => 0})
|
30
30
|
else
|
31
|
-
result.map {|hash| Song.new(hash) }
|
31
|
+
result.map {|hash| Song.new(@mpd, hash) }
|
32
32
|
end
|
33
33
|
rescue TypeError
|
34
34
|
puts "Files inside Playlist '#{@name}' do not exist!"
|
@@ -78,7 +78,7 @@ class MPD
|
|
78
78
|
# Moves song with SONGID in the playlist to the position SONGPOS.
|
79
79
|
# @macro returnraise
|
80
80
|
def move(songid, songpos)
|
81
|
-
@mpd.send_command :playlistmove, @name
|
81
|
+
@mpd.send_command :playlistmove, @name, songid, songpos
|
82
82
|
end
|
83
83
|
|
84
84
|
# Renames the playlist to +new_name+.
|
@@ -91,8 +91,13 @@ class MPD
|
|
91
91
|
else
|
92
92
|
command = options[:strict] ? :find : :search
|
93
93
|
end
|
94
|
-
|
95
|
-
|
94
|
+
|
95
|
+
response = send_command(command, params)
|
96
|
+
if response == true
|
97
|
+
return true
|
98
|
+
else
|
99
|
+
build_songs_list response
|
100
|
+
end
|
96
101
|
end
|
97
102
|
|
98
103
|
# Tell the server to update the database. Optionally,
|
@@ -117,7 +122,7 @@ class MPD
|
|
117
122
|
#
|
118
123
|
# @return [Array<String>] Array of directory names
|
119
124
|
def directories(path = nil)
|
120
|
-
return files[:directory]
|
125
|
+
return files(path)[:directory]
|
121
126
|
end
|
122
127
|
|
123
128
|
# Lists all of the albums in the database.
|
@@ -18,7 +18,7 @@ class MPD
|
|
18
18
|
def current_song
|
19
19
|
hash = send_command :currentsong
|
20
20
|
# if there is no current song (we get true, then return nil)
|
21
|
-
hash.is_a?(TrueClass) ? nil : Song.new(hash)
|
21
|
+
hash.is_a?(TrueClass) ? nil : Song.new(self, hash)
|
22
22
|
end
|
23
23
|
|
24
24
|
# Waits until there is a noteworthy change in one or more of MPD's subsystems.
|
@@ -21,6 +21,13 @@ class MPD
|
|
21
21
|
def disableoutput(num)
|
22
22
|
send_command :disableoutput, num
|
23
23
|
end
|
24
|
+
|
25
|
+
# Toggles specified output.
|
26
|
+
# @param [Integer] num Number of the output to enable.
|
27
|
+
# @macro returnraise
|
28
|
+
def toggleoutput(num)
|
29
|
+
send_command :toggleoutput, num
|
30
|
+
end
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
@@ -78,7 +78,7 @@ class MPD
|
|
78
78
|
# Returns the song with the +songid+ in the playlist,
|
79
79
|
# @return [MPD::Song]
|
80
80
|
def song_with_id(songid)
|
81
|
-
Song.new send_command(:playlistid, songid)
|
81
|
+
Song.new(self, send_command(:playlistid, songid))
|
82
82
|
end
|
83
83
|
|
84
84
|
# Searches for songs in the queue matched by the what
|
data/lib/ruby-mpd/song.rb
CHANGED
@@ -7,7 +7,8 @@ class MPD::Song
|
|
7
7
|
# length in seconds
|
8
8
|
attr_reader :file, :title, :time, :artist, :album, :albumartist
|
9
9
|
|
10
|
-
def initialize(options)
|
10
|
+
def initialize(mpd, options)
|
11
|
+
@mpd = mpd
|
11
12
|
@data = {} # allowed fields are @types + :file
|
12
13
|
@time = options.delete(:time) { [nil] }.first # HAXX for array return
|
13
14
|
@file = options.delete(:file)
|
@@ -28,6 +29,14 @@ class MPD::Song
|
|
28
29
|
return '--:--' if @time.nil?
|
29
30
|
"#{@time / 60}:#{"%02d" % (@time % 60)}"
|
30
31
|
end
|
32
|
+
|
33
|
+
# Retrieve "comments" metadata from a file and cache it in the object.
|
34
|
+
#
|
35
|
+
# @return [Hash] Key value pairs from "comments" metadata on a file.
|
36
|
+
# @return [Boolean] True if comments are empty
|
37
|
+
def comments
|
38
|
+
@comments ||= @mpd.send_command :readcomments, @file
|
39
|
+
end
|
31
40
|
|
32
41
|
# Pass any unknown calls over to the data hash.
|
33
42
|
def method_missing(m, *a)
|
data/lib/ruby-mpd/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
class MPD
|
2
|
-
VERSION = '0.3.
|
3
|
-
end
|
2
|
+
VERSION = '0.3.2'
|
3
|
+
end
|
data/ruby-mpd.gemspec
CHANGED
@@ -1,20 +1,26 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ruby-mpd/version'
|
4
5
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
s.license = 'GPL-2'
|
11
|
-
s.authors = ["Blaž Hrastnik"]
|
12
|
-
s.email = ['speed.the.bboy@gmail.com']
|
13
|
-
s.summary = "Modern client library for MPD"
|
14
|
-
s.description = "A powerful, modern and feature complete library for the Music Player Daemon."
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ruby-mpd"
|
8
|
+
spec.version = MPD::VERSION
|
9
|
+
spec.authors = ["Blaž Hrastnik"]
|
10
|
+
spec.email = ["speed.the.bboy@gmail.com"]
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
spec.summary = "Modern client library for MPD"
|
13
|
+
spec.description = "A powerful, modern and feature complete library for the Music Player Daemon"
|
14
|
+
spec.homepage = "https://github.com/archSeer/ruby-mpd"
|
15
|
+
spec.license = "GPL-2"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.5"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
20
26
|
end
|
metadata
CHANGED
@@ -1,17 +1,73 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-mpd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blaž Hrastnik
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2015-05-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.1'
|
13
69
|
description: A powerful, modern and feature complete library for the Music Player
|
14
|
-
Daemon
|
70
|
+
Daemon
|
15
71
|
email:
|
16
72
|
- speed.the.bboy@gmail.com
|
17
73
|
executables: []
|
@@ -20,8 +76,12 @@ extra_rdoc_files: []
|
|
20
76
|
files:
|
21
77
|
- ".gitignore"
|
22
78
|
- COPYING
|
23
|
-
-
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
24
82
|
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
25
85
|
- lib/ruby-mpd.rb
|
26
86
|
- lib/ruby-mpd/exceptions.rb
|
27
87
|
- lib/ruby-mpd/parser.rb
|
@@ -39,7 +99,6 @@ files:
|
|
39
99
|
- lib/ruby-mpd/song.rb
|
40
100
|
- lib/ruby-mpd/version.rb
|
41
101
|
- ruby-mpd.gemspec
|
42
|
-
- test/test_parser.rb
|
43
102
|
homepage: https://github.com/archSeer/ruby-mpd
|
44
103
|
licenses:
|
45
104
|
- GPL-2
|
@@ -60,9 +119,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
119
|
version: '0'
|
61
120
|
requirements: []
|
62
121
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.2.
|
122
|
+
rubygems_version: 2.2.2
|
64
123
|
signing_key:
|
65
124
|
specification_version: 4
|
66
125
|
summary: Modern client library for MPD
|
67
|
-
test_files:
|
68
|
-
|
126
|
+
test_files: []
|
127
|
+
has_rdoc:
|