nezetic-ruby-xbmc 0.1.1

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 (3) hide show
  1. data/README +26 -0
  2. data/lib/ruby-xbmc.rb +576 -0
  3. metadata +63 -0
data/README ADDED
@@ -0,0 +1,26 @@
1
+ ruby-xbmc
2
+ a remote access library for XBMC
3
+ =============================
4
+
5
+ ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API.
6
+
7
+ It provides a remote access to any XBMC running on the local network,
8
+ in a simple way (methods follow the ones from XBMC API).
9
+
10
+ Example:
11
+
12
+ irb(main):001:0> require 'ruby-xbmc'
13
+ irb(main):002:0> xbmc = XBMC::XBMC.new(hostname, port, user, pass)
14
+ => #<XBMC::XBMC:0x1128e7c>
15
+
16
+ irb(main):003:0> pp xbmc.GetMediaLocation("music")
17
+ [{"name"=>"Jazz", "type"=>"1", "path"=>"/mnt/data/Music/Jazz/"},
18
+ {"name"=>"Electro", "type"=>"1", "path"=>"/mnt/data/Music/Electro/"},
19
+ {"name"=>"Metal", "type"=>"1", "path"=>"/mnt/data/Music/Metal/"},
20
+ {"name"=>"Last.fm", "type"=>"1", "path"=>"lastfm://"}]
21
+
22
+ References
23
+ =========
24
+
25
+ http://xbmc.org/wiki/?title=Web_Server_HTTP_API
26
+
data/lib/ruby-xbmc.rb ADDED
@@ -0,0 +1,576 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+ ruby-xbmc
5
+
6
+ ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API.
7
+
8
+ It provides a remote access to any XBMC running on the local network,
9
+ in a simple way (methods follow the ones from XBMC API).
10
+
11
+
12
+ Example:
13
+
14
+ irb(main):001:0> xbmc = XBMC::XBMC.new(hostname, port, user, pass)
15
+ => #<XBMC::XBMC:0x1128e7c>
16
+
17
+ irb(main):002:0> pp xbmc.GetMediaLocation("music")
18
+ [{"name"=>"Jazz", "type"=>"1", "path"=>"/mnt/data/Music/Jazz/"},
19
+ {"name"=>"Electro", "type"=>"1", "path"=>"/mnt/data/Music/Electro/"},
20
+ {"name"=>"Metal", "type"=>"1", "path"=>"/mnt/data/Music/Metal/"},
21
+ {"name"=>"Last.fm", "type"=>"1", "path"=>"lastfm://"}]
22
+
23
+
24
+
25
+ Copyright (C) 2009 Cedric TESSIER
26
+
27
+ Contact: nezetic.info
28
+
29
+ This program is free software: you can redistribute it and/or modify
30
+ it under the terms of the GNU General Public License as published by
31
+ the Free Software Foundation, either version 3 of the License, or
32
+ (at your option) any later version.
33
+
34
+ This program is distributed in the hope that it will be useful,
35
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
+ GNU General Public License for more details.
38
+
39
+ You should have received a copy of the GNU General Public License
40
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
41
+ =end
42
+
43
+ begin
44
+ require 'rubygems'
45
+ rescue LoadError
46
+ end
47
+
48
+ require 'iconv'
49
+ require 'uri'
50
+ require 'open-uri'
51
+ require 'hpricot'
52
+
53
+ class Object
54
+ def caller_method
55
+ caller[1] =~ /`([^']*)'/ and $1
56
+ end
57
+ end
58
+
59
+ class Array
60
+ def to_hash(keys)
61
+ return nil if(keys.length != self.length)
62
+ Hash[*keys.zip(self).flatten]
63
+ end
64
+ end
65
+
66
+ class String
67
+ def transliterate
68
+ Iconv.iconv('ASCII//IGNORE//TRANSLIT', 'UTF-8', self).to_s
69
+ rescue
70
+ self
71
+ end
72
+ end
73
+
74
+ # Module for the library
75
+ #
76
+ #=Usage example
77
+ # First, we need to create the link between us and XBMC:
78
+ #
79
+ # xbmc = XBMC::XBMC.new(hostname, port, user, pass)
80
+ #
81
+ # then, we can do a lot of things, following XBMC API.
82
+ #
83
+ #==Set current XBMC Volume
84
+ # xbmc.SetVolume(95)
85
+ #==Get currently playing file data
86
+ # xbmc.GetCurrentlyPlaying
87
+ #==Get a files list from music library, with insertion dates
88
+ # xbmc.GetMediaLocation("music", "/mnt/data/Music/Jazz/", true)
89
+ #==Retrieve playlist contents
90
+ # xbmc.GetPlaylistContents
91
+ #==Get some system information
92
+ # xbmc.GetSystemInfoByName("weather.location","weather.temperature")
93
+ #
94
+ module XBMC
95
+ #User Agent Name
96
+ NAME = 'Ruby/XBMC'
97
+ #Library Version
98
+ VERSION = '0.1'
99
+ #Complete User-Agent
100
+ USER_AGENT = "%s %s" % [NAME, VERSION]
101
+ # scheme used in HTTP transfers (http/https)
102
+ SCHEME = "http"
103
+ # XBMC Command API
104
+ CMDPATH = "/xbmcCmds/xbmcHttp?command="
105
+
106
+ MUSIC_PLAYLIST = 0
107
+ VIDEO_PLAYLIST = 1
108
+
109
+ TYPE_DIRECTORY = 1
110
+ TYPE_FILE = 0
111
+
112
+ #Error class when not authenticated
113
+ class XBMC::UnauthenticatedError < StandardError
114
+ def initialize(m=nil)
115
+ super(m.nil? ? "You are not connected (no user/password)": m)
116
+ end
117
+ end
118
+
119
+ #Error class when password and user mismatched
120
+ class XBMC::WrongCredentialsError < XBMC::UnauthenticatedError
121
+ def initialize()
122
+ super("User / password mismatched")
123
+ end
124
+ end
125
+
126
+ #Connection class used to specify credentials and proxy settings
127
+ class Connection
128
+ #network hostname
129
+ attr_accessor :host
130
+ #network port
131
+ attr_accessor :port
132
+ #username
133
+ attr_accessor :user
134
+ #password
135
+ attr_accessor :password
136
+
137
+ #proxy url eg. http://user:pass@proxy:8080
138
+ attr_writer :proxy
139
+
140
+ def initialize(host, port=nil, user=nil, pass=nil)
141
+ @host=host
142
+ @port=port
143
+ @user=user
144
+ @password=pass
145
+ end
146
+
147
+ #proxy getter
148
+ def proxy
149
+ return @proxy unless @proxy.nil?
150
+ ENV["proxy"]
151
+ end
152
+
153
+ def exec(command) #:nodoc:
154
+ command.rstrip!
155
+ command += "()" if command[-1,1] != ")"
156
+ url = SCHEME + "://" + host + ":" + port + CMDPATH + command
157
+
158
+ begin
159
+ return open(url,"r",http_opts)
160
+ rescue OpenURI::HTTPError => e
161
+ if e.io.status.first.to_i == 401
162
+ raise (user.nil? ? UnauthenticatedError.new : WrongCredentialsError.new )
163
+ end
164
+ raise e
165
+ end
166
+ end
167
+
168
+ protected
169
+ def http_opts #:nodoc:
170
+ ret={}
171
+ ret[:http_basic_authentication]=[user,password] unless user.nil?
172
+ ret[:proxy]=proxy
173
+ ret["User-Agent"]=USER_AGENT
174
+ return ret
175
+ end
176
+
177
+ end
178
+
179
+ class XBMC
180
+ attr_accessor :error
181
+
182
+ ##### XBMC API
183
+ # Commands that Retrieve Information
184
+ def GetMediaLocation(type, path=nil, showdate=false)
185
+ if(showdate)
186
+ parse_asdictlist(cmd(with_args(type, path, "showdate")), ["name", "path", "type", "date"])
187
+ else
188
+ parse_asdictlist(cmd(with_args(type, path)), ["name", "path", "type"])
189
+ end
190
+ end
191
+
192
+ def GetShares(type)
193
+ parse_asdictlist(cmd(with_args(type)), ["name", "path"])
194
+ end
195
+
196
+ def GetCurrentPlaylist
197
+ parse(cmd())
198
+ end
199
+
200
+ def GetCurrentlyPlaying
201
+ song = parse_asdict(cmd())
202
+ return nil if(song.length <= 1)
203
+ song
204
+ end
205
+
206
+ def GetCurrentSlide
207
+ parse(cmd())
208
+ end
209
+
210
+ def GetDirectory(directory, mask=" ", showdate=false)
211
+ if(showdate)
212
+ parse_asdictlist(cmd(with_args(directory, mask, 1)), ["path", "date"])
213
+ else
214
+ parse_aslist(cmd(with_args(directory, mask)))
215
+ end
216
+ end
217
+
218
+ def GetGuiDescription
219
+ parse_asdict(cmd())
220
+ end
221
+
222
+ def GetGuiSetting(type, name)
223
+ parse(cmd(with_args(type, name)))
224
+ end
225
+
226
+ def GetGuiStatus
227
+ parse_asdict(cmd())
228
+ end
229
+
230
+ def GetMovieDetails
231
+ puts "Not implemented"
232
+ end
233
+
234
+ def GetPercentage
235
+ parse(cmd())
236
+ end
237
+
238
+ def GetPlaylistContents(playlist=nil)
239
+ parse_aslist(cmd(with_args(playlist)))
240
+ end
241
+
242
+ def GetPlaylistLength(playlist=nil)
243
+ parse(cmd(with_args(playlist)))
244
+ end
245
+
246
+ def GetPlaylistSong(position=nil)
247
+ parse(cmd(with_args(position)))
248
+ end
249
+
250
+ def GetPlaySpeed
251
+ parse(cmd())
252
+ end
253
+
254
+ def GetMusicLabel
255
+ puts "Not implemented"
256
+ end
257
+
258
+ def GetRecordStatus
259
+ puts "Not implemented"
260
+ end
261
+
262
+ def GetVideoLabel
263
+ puts "Not implemented"
264
+ end
265
+
266
+ def GetSlideshowContents
267
+ parse_aslist(cmd())
268
+ end
269
+
270
+ def GetSystemInfo(*args)
271
+ parse_aslist(cmd(with_args(args)))
272
+ end
273
+
274
+ def GetSystemInfoByName(*args)
275
+ parse_aslist(cmd(with_args(args)))
276
+ end
277
+
278
+ def GetTagFromFilename(fullpath)
279
+ parse_asdict(cmd(with_args(fullpath)))
280
+ end
281
+
282
+ def GetThumbFilename
283
+ puts "Not implemented"
284
+ end
285
+
286
+ def GetVolume
287
+ parse(cmd())
288
+ end
289
+
290
+ def GetLogLevel
291
+ parse(cmd())
292
+ end
293
+
294
+ def QueryMusicDatabase
295
+ puts "Not implemented"
296
+ end
297
+
298
+ def QueryVideoDatabase
299
+ puts "Not implemented"
300
+ end
301
+
302
+ # Commands that Modify Settings
303
+
304
+ def AddToPlayList(media, playlist=nil, mask=nil, recursive=true)
305
+ if(recursive == true)
306
+ recursive = 1
307
+ else
308
+ recursive = 0
309
+ end
310
+ success?(parse(cmd(with_args(media, playlist, mask, recursive))))
311
+ end
312
+
313
+ def AddToSlideshow(media, mask="[pictures]", recursive=true)
314
+ if(recursive == true)
315
+ recursive = 1
316
+ else
317
+ recursive = 0
318
+ end
319
+ success?(parse(cmd(with_args(media, mask, recursive))))
320
+ end
321
+
322
+ def ClearPlayList(playlist=nil)
323
+ success?(parse(cmd(with_args(playlist))))
324
+ end
325
+
326
+ def ClearSlideshow
327
+ success?(parse(cmd()))
328
+ end
329
+
330
+ def Mute
331
+ success?(parse(cmd()))
332
+ end
333
+
334
+ def RemoveFromPlaylist(item, playlist=nil)
335
+ success?(parse(cmd(with_args(item, playlist))))
336
+ end
337
+
338
+ def SeekPercentage(percentage)
339
+ success?(parse(cmd(with_args(percentage))))
340
+ end
341
+
342
+ def SeekPercentageRelative(percentage)
343
+ success?(parse(cmd(with_args(percentage))))
344
+ end
345
+
346
+ def SetCurrentPlaylist(playlist)
347
+ success?(parse(cmd(with_args(playlist))))
348
+ end
349
+
350
+ def SetGUISetting(type, name, value)
351
+ success?(parse(cmd(with_args(type, name, value))))
352
+ end
353
+
354
+ def SetPlaylistSong(position)
355
+ success?(parse(cmd(with_args(position))))
356
+ end
357
+
358
+ def SetPlaySpeed(speed)
359
+ success?(parse(cmd(with_args(speed))))
360
+ end
361
+
362
+ def SlideshowSelect(filename)
363
+ success?(parse(cmd(with_args(filename))))
364
+ end
365
+
366
+ def SetVolume(volume)
367
+ success?(parse(cmd(with_args(volume))))
368
+ end
369
+
370
+ def SetLogLevel(level)
371
+ success?(parse(cmd(with_args(level))))
372
+ end
373
+
374
+ def SetAutoGetPictureThumbs(boolean=true)
375
+ success?(parse(cmd(with_args(boolean))))
376
+ end
377
+
378
+ # Commands that Generate Actions
379
+
380
+ def Action(code) # Sends a raw Action ID (see key.h)
381
+ success?(parse(cmd(with_args(code))))
382
+ end
383
+
384
+ def Exit
385
+ success?(parse(cmd()))
386
+ end
387
+
388
+ def KeyRepeat(rate)
389
+ success?(parse(cmd(with_args(rate))))
390
+ end
391
+
392
+ def Move(deltaX, deltaY)
393
+ success?(parse(cmd(with_args(deltaX, deltaY))))
394
+ end
395
+
396
+ def Pause
397
+ success?(parse(cmd()))
398
+ end
399
+
400
+ def PlayListNext
401
+ success?(parse(cmd()))
402
+ end
403
+
404
+ def PlayListPrev
405
+ success?(parse(cmd()))
406
+ end
407
+
408
+ def PlayNext
409
+ success?(parse(cmd()))
410
+ end
411
+
412
+ def PlayPrev
413
+ success?(parse(cmd()))
414
+ end
415
+
416
+ def PlaySlideshow(directory=nil, recursive=true)
417
+ success?(parse(cmd(with_args(directory, recursive))))
418
+ end
419
+
420
+ def PlayFile(filename, playlist=nil)
421
+ success?(parse(cmd(with_args(filename, playlist))))
422
+ end
423
+
424
+ def Reset
425
+ success?(parse(cmd()))
426
+ end
427
+
428
+ def Restart
429
+ success?(parse(cmd()))
430
+ end
431
+
432
+ def RestartApp
433
+ success?(parse(cmd()))
434
+ end
435
+
436
+ def Rotate
437
+ success?(parse(cmd()))
438
+ end
439
+
440
+ def SendKey(buttoncode)
441
+ success?(parse(cmd(with_args(buttoncode))))
442
+ end
443
+
444
+ def ShowPicture(filename)
445
+ success?(parse(cmd(with_args(filename))))
446
+ end
447
+
448
+ def Shutdown
449
+ success?(parse(cmd()))
450
+ end
451
+
452
+ def SpinDownHardDisk
453
+ success?(parse(cmd()))
454
+ end
455
+
456
+ def Stop
457
+ success?(parse(cmd()))
458
+ end
459
+
460
+ def TakeScreenshot(width=300, height=200, quality=90, rotation=0, download=true, filename="xbmc-screen.jpg", flash=true, imgtag=false)
461
+ if(imgtag)
462
+ imgtag = "imgtag"
463
+ else
464
+ imgtag = nil
465
+ end
466
+ parse(cmd(with_args(filename, flash, rotation, width, height, quality, download, imgtag)))
467
+ end
468
+
469
+ def Zoom(zoom)
470
+ success?(parse(cmd(with_args(zoom))))
471
+ end
472
+
473
+ ##### END XBMC API
474
+
475
+ def initialize(host, port=nil, user=nil, pass=nil)
476
+ @@connection = Connection.new(host, port, user, pass)
477
+ end
478
+
479
+ def error?
480
+ not error.nil?
481
+ end
482
+
483
+ def print_error
484
+ $stderr.puts "Error: " + @error if @error != nil
485
+ end
486
+
487
+ def host
488
+ @@connection.host
489
+ end
490
+
491
+ def port
492
+ @@connection.port
493
+ end
494
+
495
+ # private members
496
+ private
497
+
498
+ def with_args(*args)
499
+ args.flatten! if args.length == 1
500
+ command = self.caller_method
501
+ command += "("
502
+ cmdargs = ""
503
+ args.each {|arg| cmdargs += (";" + (URI.escape(arg.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")))) unless arg.nil? }
504
+ cmdargs = cmdargs[1..-1]
505
+ command += cmdargs unless cmdargs.nil?
506
+ command += ")"
507
+ end
508
+
509
+ def cmd(command=nil)
510
+ command = self.caller_method if command.nil?
511
+ response = @@connection.exec(command)
512
+ end
513
+
514
+ def success?(res)
515
+ (not error? and not res.nil? and res == "OK")
516
+ end
517
+
518
+ def parse_resp(response)
519
+ doc = Hpricot(response)
520
+ list = (doc/"li")
521
+ if(list.length > 0)
522
+ text = list.inner_text
523
+ if(text[0,5] == "Error")
524
+ idx = text.index(':')
525
+ if(idx != nil)
526
+ @error = text[(idx+2)..-1]
527
+ else
528
+ @error = "Unknown error"
529
+ end
530
+ else
531
+ @error = nil
532
+ end
533
+ else # file download (screenshot)
534
+ return doc
535
+ end
536
+ list
537
+ end
538
+
539
+ def parse(response)
540
+ resp = parse_resp(response).inner_text.transliterate
541
+ return "" if error?
542
+ resp
543
+ end
544
+
545
+ def parse_aslist(response)
546
+ list = []
547
+ parse_resp(response).each {|item| list.push(item.inner_text.transliterate.chomp)}
548
+ return [] if error?
549
+ list
550
+ end
551
+
552
+ def parse_asdictlist(response, keys)
553
+ list = []
554
+ parse_aslist(response).each {|item| list.push((item.split(';').collect {|x| x.transliterate.chomp.rstrip}).to_hash(keys) )}
555
+ return [] if error?
556
+ list
557
+ end
558
+
559
+ def parse_asdict(response)
560
+ dict = {}
561
+ resp = parse_resp(response)
562
+ return dict if error?
563
+ resp.each {|item|
564
+ item = item.inner_text.transliterate
565
+ idx = item.index(':')
566
+ next if item == nil
567
+ key = item[0..idx-1]
568
+ value = item[idx+1..-1].chomp
569
+ dict[key]=value
570
+ }
571
+ dict
572
+ end
573
+
574
+ end
575
+
576
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nezetic-ruby-xbmc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Cedric TESSIER
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-15 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.6.0
24
+ version:
25
+ description: ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API. It provides a remote access to any XBMC running on the local network, in a simple way (methods follow the ones from XBMC API).
26
+ email: nezetic at gmail d o t com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README
35
+ - lib/ruby-xbmc.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/nezetic/ruby-xbmc
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API
62
+ test_files: []
63
+