ruby-xbmc 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README +26 -0
  2. data/lib/ruby-xbmc.rb +606 -0
  3. metadata +69 -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
+
@@ -0,0 +1,606 @@
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 'nokogiri'
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
+ # Commands that Modify Files
474
+ def FileCopy(src, dst)
475
+ success?(parse(cmd(with_args(src, dst))))
476
+ end
477
+
478
+ def FileDelete(filename)
479
+ success?(parse(cmd(with_args(filename))))
480
+ end
481
+
482
+ def FileDownload(filename, bare=nil)
483
+ parse(cmd(with_args(filename, bare)))
484
+ end
485
+
486
+ def FileDownloadFromInternet(url, filename=nil, bare=nil)
487
+ parse(cmd(with_args(url, filename, bare)))
488
+ end
489
+
490
+ def FileExists(filename)
491
+ return true if parse(cmd(with_args(filename))) == "True"
492
+ false
493
+ end
494
+
495
+ def FileSize(filename)
496
+ parse(cmd(with_args(filename))).to_i
497
+ end
498
+
499
+ def FileUpload(filename, contents)
500
+ success?(parse(cmd(with_args(filename, contents))))
501
+ end
502
+
503
+ ##### END XBMC API
504
+
505
+ def initialize(host, port=nil, user=nil, pass=nil)
506
+ @@connection = Connection.new(host, port, user, pass)
507
+ end
508
+
509
+ def error?
510
+ not error.nil?
511
+ end
512
+
513
+ def print_error
514
+ $stderr.puts "Error: " + @error if @error != nil
515
+ end
516
+
517
+ def host
518
+ @@connection.host
519
+ end
520
+
521
+ def port
522
+ @@connection.port
523
+ end
524
+
525
+ # private members
526
+ private
527
+
528
+ def with_args(*args)
529
+ args.flatten! if args.length == 1
530
+ command = self.caller_method
531
+ command += "("
532
+ cmdargs = ""
533
+ args.each {|arg| cmdargs += (";" + (URI.escape(arg.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")))) unless arg.nil? }
534
+ cmdargs = cmdargs[1..-1]
535
+ command += cmdargs unless cmdargs.nil?
536
+ command += ")"
537
+ end
538
+
539
+ def cmd(command=nil)
540
+ command = self.caller_method if command.nil?
541
+ response = @@connection.exec(command)
542
+ end
543
+
544
+ def success?(res)
545
+ (not error? and not res.nil? and res == "OK")
546
+ end
547
+
548
+ def parse_resp(response)
549
+ doc = Nokogiri::HTML(response)
550
+ list = (doc/"li")
551
+ if(list.length > 0)
552
+ text = list.inner_text
553
+ if(text[0,5] == "Error")
554
+ idx = text.index(':')
555
+ if(idx != nil)
556
+ @error = text[(idx+2)..-1]
557
+ else
558
+ @error = "Unknown error"
559
+ end
560
+ else
561
+ @error = nil
562
+ end
563
+ else # file download (screenshot)
564
+ return doc
565
+ end
566
+ list
567
+ end
568
+
569
+ def parse(response)
570
+ resp = parse_resp(response).inner_text.transliterate
571
+ return "" if error?
572
+ resp
573
+ end
574
+
575
+ def parse_aslist(response)
576
+ list = []
577
+ parse_resp(response).each {|item| list.push(item.inner_text.transliterate.chomp)}
578
+ return [] if error?
579
+ list
580
+ end
581
+
582
+ def parse_asdictlist(response, keys)
583
+ list = []
584
+ parse_aslist(response).each {|item| list.push((item.split(';').collect {|x| x.transliterate.chomp.rstrip}).to_hash(keys) )}
585
+ return [] if error?
586
+ list
587
+ end
588
+
589
+ def parse_asdict(response)
590
+ dict = {}
591
+ resp = parse_resp(response)
592
+ return dict if error?
593
+ resp.each {|item|
594
+ item = item.inner_text.transliterate
595
+ idx = item.index(':')
596
+ next if item == nil
597
+ key = item[0..idx-1]
598
+ value = item[idx+1..-1].chomp
599
+ dict[key]=value
600
+ }
601
+ dict
602
+ end
603
+
604
+ end
605
+
606
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-xbmc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Cedric TESSIER
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-11 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.3.0
24
+ version:
25
+ description: |-
26
+ ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API.
27
+
28
+ It provides a remote access to any XBMC running on the local network,
29
+ in a simple way (methods follow the ones from XBMC API).
30
+ email: nezetic at gmail d o t com
31
+ executables: []
32
+
33
+ extensions: []
34
+
35
+ extra_rdoc_files: []
36
+
37
+ files:
38
+ - README
39
+ - lib/ruby-xbmc.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/nezetic/ruby-xbmc
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: ruby-xbmc is a ruby wrapper for the XBMC Web Server HTTP API
68
+ test_files: []
69
+