ruby-mpd 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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 *args }
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 self.connected?
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 = @socket.gets
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!" if !@socket
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
- private
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,
@@ -94,7 +94,7 @@ class MPD
94
94
  def build_hash(string)
95
95
  return {} if string.nil?
96
96
 
97
- string.split("\n").each_with_object({}) do |line, hash|
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.split("\n").reject {|line| line =~ /(#{filter.join('|')}):/}.join("\n")
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
- chunks = string.split(/\n(?=#{first_key})/).map(&:strip)
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
@@ -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
- build_songs_list send_command(command, params)
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)
@@ -1,3 +1,3 @@
1
1
  class MPD
2
- VERSION = '0.3.1'
3
- end
2
+ VERSION = '0.3.2'
3
+ end
data/ruby-mpd.gemspec CHANGED
@@ -1,20 +1,26 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.unshift File.expand_path("../lib", __FILE__)
3
- require "ruby-mpd/version"
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 |s|
6
- s.platform = Gem::Platform::RUBY
7
- s.name = 'ruby-mpd'
8
- s.version = MPD::VERSION
9
- s.homepage = 'https://github.com/archSeer/ruby-mpd'
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
- s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
- s.require_paths = ["lib"]
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.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blaž Hrastnik
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2014-01-27 00:00:00.000000000 Z
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
- - README.rdoc
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.0
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
- - test/test_parser.rb
126
+ test_files: []
127
+ has_rdoc: