ncXBMC 0.3

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 +14 -0
  2. data/bin/ncxbmc.rb +741 -0
  3. metadata +78 -0
data/README ADDED
@@ -0,0 +1,14 @@
1
+ ncXBMC
2
+ ==================
3
+
4
+ ncXBMC is a remote XBMC client, which aims to provide a full control of
5
+ the music player over a local network.
6
+
7
+ It can be used to browse library, manage playlist, and control playback.
8
+
9
+ The interface has been greatly inspired by ncmpc (http://hem.bredband.net/kaw/ncmpc/),
10
+ a curses client for the Music Player Daemon (MPD) (I'm a fan).
11
+
12
+
13
+ Usage: ncxbmc.rb [options] hostname
14
+
data/bin/ncxbmc.rb ADDED
@@ -0,0 +1,741 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+ ncXBMC
5
+
6
+ ncXBMC is a remote XBMC client, which aims to provide a full control of
7
+ the music player over a local network.
8
+
9
+ It can be used to browse library, manage playlist, and control playback.
10
+
11
+ The interface has been greatly inspired by ncmpc (http://hem.bredband.net/kaw/ncmpc/),
12
+ a curses client for the Music Player Daemon (MPD) (I'm a fan).
13
+
14
+
15
+ Copyright (C) 2009 Cedric TESSIER
16
+
17
+ Contact: nezetic.info
18
+
19
+ This program is free software: you can redistribute it and/or modify
20
+ it under the terms of the GNU General Public License as published by
21
+ the Free Software Foundation, either version 3 of the License, or
22
+ (at your option) any later version.
23
+
24
+ This program is distributed in the hope that it will be useful,
25
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
26
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
+ GNU General Public License for more details.
28
+
29
+ You should have received a copy of the GNU General Public License
30
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
31
+ =end
32
+
33
+
34
+ require 'optparse'
35
+
36
+ begin
37
+ require 'rubygems'
38
+ rescue LoadError
39
+ end
40
+
41
+ require 'ncurses'
42
+ require 'ruby-xbmc'
43
+
44
+ NCXMBC_DEFAULTPORT="8080"
45
+ NCXBMC_VERSION=0.3
46
+
47
+
48
+ module Interface
49
+ KEY_TAB = 9
50
+ KEY_ENTER = 10
51
+ KEY_SPACE = 32
52
+ KEY_RIGHT = 261
53
+ KEY_LEFT = 260
54
+ KEY_UP = 259
55
+ KEY_DOWN = 258
56
+ KEY_b = 98
57
+ KEY_c = 99
58
+ KEY_d = 100
59
+ KEY_f = 102
60
+ KEY_h = 104
61
+ KEY_m = 109
62
+ KEY_p = 112
63
+ KEY_s = 115
64
+ KEY_RETURN = 127
65
+
66
+ class NcurseInterface
67
+ REFRESH_DELAY=1 # seconds
68
+ KEY_ESC=27
69
+
70
+ $interface_error = nil
71
+
72
+ def initialize(xbmc)
73
+ @stdscr = Ncurses.initscr
74
+
75
+ Ncurses.curs_set(0)
76
+ Ncurses.noecho
77
+ Ncurses.keypad(@stdscr,TRUE)
78
+ Ncurses.cbreak
79
+ @stdscr.nodelay(true)
80
+
81
+ ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc)
82
+
83
+ if Ncurses.has_colors?
84
+ Ncurses.start_color
85
+ Ncurses.use_default_colors
86
+ init_bgcolor()
87
+ end
88
+
89
+ @maxy=Ncurses.getmaxy(@stdscr)
90
+ @maxx=Ncurses.getmaxx(@stdscr)
91
+
92
+ @xbmc = xbmc
93
+
94
+ @wins = []
95
+ @wins << PlaylistWin.new(@stdscr, @xbmc)
96
+ @wins << LibraryWin.new(@stdscr, @xbmc)
97
+
98
+ @currentwin = 0
99
+ end
100
+
101
+ def refresh
102
+ @stdscr.refresh
103
+ @wins[@currentwin].refresh
104
+ end
105
+
106
+ def drawCurrentWin
107
+ @wins[@currentwin].draw
108
+ end
109
+
110
+ def UpdateCurrentWin
111
+ @wins[@currentwin].autorefresh
112
+ @wins[@currentwin].update
113
+ end
114
+
115
+ def run
116
+ begin
117
+ self.drawCurrentWin
118
+ self.refresh
119
+
120
+ key=0
121
+ timespent=0
122
+ while key!=KEY_ESC
123
+ key=@stdscr.getch
124
+
125
+ if(key == KEY_TAB) # Tabs Cycling
126
+ @currentwin += 1
127
+ @currentwin = 0 if(@currentwin > (@wins.length - 1))
128
+
129
+ self.drawCurrentWin
130
+ self.refresh
131
+ next
132
+ end
133
+
134
+ @wins[@currentwin].handleKey(key) if key != -1
135
+
136
+ sleep(0.01)
137
+ timespent += 0.01
138
+
139
+ if(timespent >= REFRESH_DELAY)
140
+ timespent = 0
141
+ self.UpdateCurrentWin
142
+ end
143
+ end
144
+ rescue Interrupt
145
+ rescue Exception => exc
146
+ $interface_error = exc
147
+ end
148
+ end
149
+
150
+ protected
151
+
152
+ def init_bgcolor(pair=1, bgcolor=Ncurses::COLOR_BLACK)
153
+ Ncurses.init_pair(1, Ncurses::COLOR_WHITE, bgcolor)
154
+ Ncurses.init_pair(2, Ncurses::COLOR_YELLOW, bgcolor)
155
+ Ncurses.init_pair(3, Ncurses::COLOR_RED, bgcolor)
156
+ Ncurses.init_pair(4, Ncurses::COLOR_GREEN, bgcolor)
157
+ Ncurses.init_pair(5, Ncurses::COLOR_BLUE, bgcolor)
158
+ Ncurses.init_pair(6, Ncurses::COLOR_CYAN, bgcolor)
159
+ Ncurses.init_pair(7, Ncurses::COLOR_MAGENTA, bgcolor)
160
+ Ncurses.init_pair(8, Ncurses::COLOR_BLACK, bgcolor)
161
+ Ncurses.bkgd(Ncurses.COLOR_PAIR(pair))
162
+ end
163
+
164
+ private
165
+
166
+ def self.finalize(id)
167
+ Ncurses.echo()
168
+ Ncurses.nocbreak()
169
+ Ncurses.nl()
170
+ Ncurses.curs_set(1)
171
+
172
+ Ncurses.endwin
173
+
174
+ if not $interface_error.nil?
175
+ case $interface_error
176
+ when SocketError, Errno::ETIMEDOUT then
177
+ puts "\nConnection lost !\n"
178
+ else
179
+ puts "\nFATAL ERROR in ncXBMC v%g !" % NCXBMC_VERSION
180
+ puts $interface_error.class.to_s + ": " + $interface_error.to_s
181
+ puts "Callstack:"
182
+ $interface_error.backtrace.each {|line| puts "\t#{line}"}
183
+ puts ""
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ class TabWins
190
+
191
+ def initialize(pscreen, xbmc)
192
+ @stdscr = pscreen
193
+ @xbmc = xbmc
194
+
195
+ @title = ""
196
+
197
+ @maxy=Ncurses.getmaxy(@stdscr)
198
+ @maxx=Ncurses.getmaxx(@stdscr)
199
+
200
+ @header = Ncurses::WINDOW.new(2,@maxx,0,0)
201
+ @main = Ncurses::WINDOW.new(@maxy-2-2,@maxx,2,0)
202
+ @footer = Ncurses::WINDOW.new(2,@maxx,@maxy-2,0)
203
+
204
+ @selected = 0
205
+ @scroll = 0
206
+
207
+ @rheader = false
208
+ @rmain = false
209
+ @rfooter = false
210
+
211
+ @autorefresh_header = false
212
+ @autorefresh_main = false
213
+ @autorefresh_footer = false
214
+
215
+ @defaultKeys = true
216
+
217
+ @helpMSGCommon = "TAB switch between windows\nESC quit ncXBMC\n\n"
218
+ @helpMSG = ""
219
+ end
220
+
221
+ def draw
222
+ self.drawHeader
223
+ self.drawMain
224
+ self.drawFooter
225
+ end
226
+
227
+ def autorefresh
228
+ self.updateHeader if @autorefresh_header
229
+ self.updateMain if @autorefresh_main
230
+ self.updateFooter if @autorefresh_footer
231
+ end
232
+
233
+ def refresh
234
+ @header.refresh
235
+ @main.refresh
236
+ @footer.refresh
237
+ end
238
+
239
+ def updateHeader
240
+ @rheader = true
241
+ end
242
+
243
+ def updateMain
244
+ @rmain = true
245
+ end
246
+
247
+ def updateFooter
248
+ @rfooter = true
249
+ end
250
+
251
+ def update
252
+ if(@rheader)
253
+ self.drawHeader
254
+ @header.refresh
255
+ @rheader = false
256
+ end
257
+ if(@rmain)
258
+ self.drawMain
259
+ @main.refresh
260
+ @rmain = false
261
+ end
262
+ if(@rfooter)
263
+ self.drawFooter
264
+ @footer.refresh
265
+ @rfooter = false
266
+ end
267
+ end
268
+
269
+ def handleKey(key)
270
+ #Ncurses.mvwaddstr(@main, 20, 1, "KeyCode: " + key.to_s)
271
+ #@main.refresh
272
+
273
+ if (@defaultKeys)
274
+ case key
275
+ when KEY_h then
276
+ self.showHelp
277
+ end
278
+ end
279
+ end
280
+
281
+ protected
282
+
283
+ def drawHeader
284
+ @header.erase
285
+ @header.attron(Ncurses::A_BOLD)
286
+ Ncurses.mvwaddstr(@header, 0, 1, "Ncurses XBMC Client v." + NCXBMC_VERSION.to_s + " -- " + @title)
287
+ @header.attroff(Ncurses::A_BOLD)
288
+ @header.mvhline(1,0,Ncurses::ACS_HLINE, @maxx)
289
+ end
290
+
291
+ def drawMain
292
+ @main.erase
293
+ end
294
+
295
+ def drawFooter
296
+ @footer.erase
297
+ @footer.mvhline(0,0,Ncurses::ACS_HLINE, @maxx)
298
+ end
299
+
300
+ def handleScroll
301
+ maxwy = Ncurses.getmaxy(@main) - 1
302
+ if((@selected - @scroll) > maxwy)
303
+ @scroll += 1
304
+ elsif((@selected - @scroll) < 0)
305
+ @scroll -= 1
306
+ end
307
+ return maxwy
308
+ end
309
+
310
+ def showHelp
311
+ border = 15
312
+ title = "Help"
313
+
314
+ helpmsg = @helpMSGCommon + @helpMSG
315
+
316
+ lines = helpmsg.count("\n") + 3
317
+ helpwin = Ncurses::WINDOW.new(lines, @maxx-border, 2, (@maxx/2 - (@maxx-border)/2))
318
+ Ncurses.box(helpwin, Ncurses::ACS_VLINE, Ncurses::ACS_HLINE)
319
+ helpwin.attron(Ncurses::A_REVERSE)
320
+ Ncurses.mvwaddstr(helpwin, 0, (@maxx-border)/2 - (title.length + 2)/2, " #{title} ")
321
+ helpwin.attroff(Ncurses::A_REVERSE)
322
+
323
+ linenbr = 1
324
+ helpmsg.each { |line|
325
+ Ncurses.mvwaddstr(helpwin, linenbr, 2, line.chomp)
326
+ linenbr += 1
327
+ }
328
+ helpwin.refresh
329
+ end
330
+ end
331
+
332
+ class PlaylistWin < TabWins
333
+ def initialize(pscreen, xbmc)
334
+ super
335
+ @title = "Playlist"
336
+ @autorefresh_footer = true
337
+
338
+ @forcesel = 0
339
+ @fastRefresh = false
340
+ end
341
+
342
+ def drawHeader
343
+ super
344
+
345
+ volume = @xbmc.GetVolume.to_i
346
+ if(volume > 0)
347
+ volume_label = "Volume %d%%" % volume
348
+ else
349
+ volume_label = "Volume Muted"
350
+ end
351
+ Ncurses.mvwaddstr(@header, 0, @maxx - volume_label.length - 1, volume_label)
352
+ end
353
+
354
+ def drawMain
355
+ super
356
+
357
+ if(not @fastRefresh)
358
+ getCurrentPlaylist
359
+ getCurrentSong
360
+ else
361
+ @fastRefresh = false
362
+ end
363
+
364
+ return if @playlist.length == 1 and @playlist[0]["artist"].nil?
365
+
366
+ maxwy = self.handleScroll
367
+
368
+ idx = 0
369
+ @playlist.each { |song|
370
+ line = idx - @scroll
371
+ if(line >= 0)
372
+ break if(line > maxwy)
373
+
374
+ current = (@playing and @current_song["URL"] == song["path"])
375
+ if(current)
376
+ @main.attron(Ncurses.COLOR_PAIR(2)) if Ncurses.has_colors?
377
+ @main.attron(Ncurses::A_BOLD)
378
+ end
379
+ @main.attron(Ncurses::A_REVERSE) if(idx == @selected)
380
+ Ncurses.mvwaddstr(@main, line, 1, "#{song["artist"]} - #{song["title"]}")
381
+ if(current)
382
+ @main.attron(Ncurses.COLOR_PAIR(1)) if Ncurses.has_colors?
383
+ @main.attroff(Ncurses::A_BOLD)
384
+ end
385
+ @main.attroff(Ncurses::A_REVERSE) if(idx == @selected)
386
+ end
387
+ idx += 1
388
+ }
389
+
390
+ end
391
+
392
+ def drawFooter
393
+ super
394
+
395
+ getCurrentSong
396
+
397
+ if(@playing)
398
+ speed = @xbmc.GetPlaySpeed.to_i
399
+ @footer.mvwaddstr(0, 0, '=' * (@maxx * @current_song["Percentage"].to_i / 100) + (speed == 1 ? '0': (speed > 1 ? '>>':'<<')))
400
+ paused = (@current_song["PlayStatus"] == "Paused")
401
+ Ncurses.mvwaddstr(@footer, 1, 1, "Playing: #{@current_song["Artist"]} - #{@current_song["Title"]} #{(paused ? "(Pause)":"")}")
402
+ time_label = "["+@current_song["Time"]+"/"+@current_song["Duration"]+"]"
403
+ Ncurses.mvwaddstr(@footer, 1, @maxx - time_label.length - 1, time_label)
404
+ speed_label = "(%dX)" % speed
405
+ @footer.mvwaddstr(0, @maxx - speed_label.length - 1, speed_label) if(speed != 1)
406
+ end
407
+ end
408
+
409
+ def update
410
+ super
411
+
412
+ if(@playing) # new song playing, refresh playlist
413
+ if(not @rmain and (@current_song["Changed"] == "True"))
414
+ self.drawMain
415
+ @main.refresh
416
+ end
417
+ end
418
+ end
419
+
420
+ def handleKey(key)
421
+ super
422
+
423
+ case key
424
+ when KEY_LEFT then
425
+ @xbmc.SetVolume(@xbmc.GetVolume.to_i - 1)
426
+ self.updateHeader
427
+ when KEY_RIGHT then
428
+ @xbmc.SetVolume(@xbmc.GetVolume.to_i + 1)
429
+ self.updateHeader
430
+ when KEY_DOWN then
431
+ @selected += 1 if @selected < (@playlist.length - 1)
432
+ @fastRefresh = true
433
+ self.updateMain
434
+ when KEY_UP then
435
+ @selected -= 1 if @selected > 0
436
+ @fastRefresh = true
437
+ self.updateMain
438
+ when KEY_ENTER then
439
+ if(@xbmc.GetPlaySpeed.to_i != 1)
440
+ @xbmc.SetPlaySpeed(1)
441
+ else
442
+ @xbmc.SetPlaylistSong(@selected)
443
+ end
444
+ self.updateMain
445
+ when KEY_d then
446
+ @xbmc.RemoveFromPlaylist(@selected)
447
+ @selected -= 1 if @selected >= (@playlist.length - 1)
448
+ @forcesel = @selected
449
+ self.updateMain
450
+ when KEY_c then
451
+ @xbmc.Stop
452
+ @xbmc.ClearPlayList(XBMC::MUSIC_PLAYLIST)
453
+ @selected = 0
454
+ self.updateMain
455
+ when KEY_f then
456
+ speed = @xbmc.GetPlaySpeed.to_i
457
+ newspeed = speed < 0 ? speed / 2 : speed * 2
458
+ newspeed = 1 if(newspeed == -1)
459
+ @xbmc.SetPlaySpeed(newspeed) if(speed < 32)
460
+ when KEY_b then
461
+ speed = @xbmc.GetPlaySpeed.to_i
462
+ newspeed = speed > 0 ? speed / 2 : speed * 2
463
+ newspeed = -2 if(newspeed == 0)
464
+ @xbmc.SetPlaySpeed(newspeed) if(speed > -32)
465
+ when KEY_m then
466
+ @xbmc.Mute
467
+ self.updateHeader
468
+ when KEY_p then
469
+ @xbmc.Pause
470
+ self.updateFooter
471
+ end
472
+
473
+ self.update
474
+ end
475
+
476
+ def showHelp
477
+ @helpMSG = "UP select previous entry\n"
478
+ @helpMSG += "DOWN select next entry\n"
479
+ @helpMSG += "ENTER play selected entry\n"
480
+ @helpMSG += "d remove selected entry\n"
481
+ @helpMSG += "c clear current playlist\n\n"
482
+ @helpMSG += "RIGHT Volume +\n"
483
+ @helpMSG += "LEFT Volume -\n"
484
+ @helpMSG += "m mute volume\n\n"
485
+ @helpMSG += "f fast forward\n"
486
+ @helpMSG += "b fast rewind\n"
487
+ @helpMSG += "p pause playback"
488
+ super
489
+ end
490
+
491
+ def getCurrentSong
492
+ @current_song = @xbmc.GetCurrentlyPlaying
493
+ @playing = (@current_song != nil)
494
+ end
495
+
496
+ def getCurrentPlaylist
497
+ playlist = @xbmc.GetPlaylistContents
498
+ return if(playlist == @last_playlist)
499
+ @last_playlist = playlist
500
+
501
+ @selected = @forcesel
502
+ @forcesel = 0
503
+
504
+ @playlist = [] # cache tags infos for playlist
505
+ playlist.each { |file|
506
+ song = @xbmc.GetTagFromFilename(file)
507
+ @playlist.push({"path"=>file, "artist"=>song["Artist"], "title"=>song["Title"]})
508
+ }
509
+ end
510
+
511
+ end
512
+
513
+ class LibraryWin < TabWins
514
+
515
+ def initialize(pscreen, xbmc)
516
+ super
517
+ @title = "Library"
518
+ @deepth = 0
519
+ @lastdeepth = -1
520
+ @currentdir = nil
521
+ @history = []
522
+
523
+ @search = false
524
+ @searched = ""
525
+ end
526
+
527
+ def drawMain
528
+ super
529
+
530
+ if(@lastdeepth != @deepth) # refresh library entries list if needed
531
+ @list = @xbmc.GetMediaLocation("music", @currentdir)
532
+ @lastdeepth = @deepth
533
+ end
534
+
535
+ maxwy = self.handleScroll
536
+
537
+ idx = 0
538
+ @list.each { |entry|
539
+ line = idx - @scroll
540
+ if(line >= 0)
541
+ break if(line > maxwy)
542
+
543
+ @main.attron(Ncurses::A_REVERSE) if(idx == @selected)
544
+ Ncurses.mvwaddstr(@main, line, 1, "#{entry["name"]}")
545
+ @main.attroff(Ncurses::A_REVERSE) if(idx == @selected)
546
+ end
547
+ idx += 1
548
+ }
549
+ end
550
+
551
+ def drawFooter
552
+ super
553
+
554
+ if (@search)
555
+ Ncurses.mvwaddstr(@footer, 1, 1, "/ " + @searched)
556
+ else
557
+ histline = ""
558
+ @history.reverse.each {|old|
559
+ histline += " > " + old[:currentname]
560
+ }
561
+
562
+ Ncurses.mvwaddstr(@footer, 1, 1, histline)
563
+
564
+ nitems_label = @list.length.to_s + " items"
565
+ @footer.mvwaddstr(1, @maxx - nitems_label.length - 1, nitems_label)
566
+ end
567
+ end
568
+
569
+ def searchEntry
570
+ if @searched.size > 2
571
+ idx = 0
572
+ @list.each { |entry|
573
+ if entry["name"].downcase.include?(@searched.downcase)
574
+ @selected = idx
575
+ @scroll = idx
576
+ return true
577
+ end
578
+ idx += 1
579
+ }
580
+ end
581
+ return false
582
+ end
583
+
584
+ def handleKeySearch(key)
585
+
586
+ case key
587
+ when KEY_ENTER then
588
+ @search = false
589
+ @searched = ""
590
+ @defaultKeys = true
591
+ self.updateFooter
592
+ when KEY_RETURN then
593
+ @searched.chop!()
594
+ self.updateFooter
595
+ else
596
+ begin
597
+ @searched += key.chr
598
+ self.updateFooter
599
+ rescue
600
+ return
601
+ end
602
+ self.updateMain if self.searchEntry
603
+ end
604
+
605
+ self.update
606
+ end
607
+
608
+ def handleKeyMain(key)
609
+
610
+ case key
611
+ when KEY_DOWN then
612
+ @selected += 1 if @selected < (@list.length - 1)
613
+ self.updateMain
614
+ when KEY_UP then
615
+ @selected -= 1 if @selected > 0
616
+ self.updateMain
617
+ when KEY_ENTER then
618
+ if @list[@selected]["type"].to_i == XBMC::TYPE_DIRECTORY
619
+ @history.insert(0, {:currentdir=>@currentdir, :selected=>@selected, :scroll=>@scroll, :currentname=>@list[@selected]["name"]})
620
+ @currentdir = @list[@selected]["path"]
621
+ @selected = 0
622
+ @scroll = 0
623
+ @deepth += 1
624
+ end
625
+ self.updateMain
626
+ self.updateFooter
627
+ when KEY_RETURN then
628
+ return if(@history.length < 1)
629
+ oldentry = @history.shift
630
+ @currentdir = oldentry[:currentdir]
631
+ @selected = oldentry[:selected]
632
+ @scroll = oldentry[:scroll]
633
+ @deepth -= 1
634
+ self.updateMain
635
+ self.updateFooter
636
+ when KEY_SPACE then
637
+ @xbmc.AddToPlayList(@list[@selected]["path"], XBMC::MUSIC_PLAYLIST, "[music]")
638
+ @xbmc.SetCurrentPlaylist(XBMC::MUSIC_PLAYLIST)
639
+ #@xbmc.SetPlaylistSong(0)
640
+ when KEY_c then
641
+ @xbmc.Stop
642
+ @xbmc.ClearPlayList(XBMC::MUSIC_PLAYLIST)
643
+ when KEY_s then
644
+ @search = true
645
+ @defaultKeys = false
646
+ self.updateFooter
647
+ end
648
+
649
+ self.update
650
+ end
651
+
652
+ def handleKey(key)
653
+ super
654
+
655
+ if (@search)
656
+ self.handleKeySearch(key)
657
+ else
658
+ self.handleKeyMain(key)
659
+ end
660
+ end
661
+
662
+ def showHelp
663
+ @helpMSG = "UP select previous entry\n"
664
+ @helpMSG += "DOWN select next entry\n"
665
+ @helpMSG += "ENTER browse selected directory\n"
666
+ @helpMSG += "RETURN return into previous directory\n"
667
+ @helpMSG += "SPACE add selected directory/file to playlist\n\n"
668
+ @helpMSG += "c clear current playlist\n"
669
+ @helpMSG += "s search an entry in playlist"
670
+ super
671
+ end
672
+ end
673
+ end
674
+
675
+ ##### MAIN ######
676
+
677
+ options = {}
678
+ inputopts = OptionParser.new do |opts|
679
+ options[:port] = NCXMBC_DEFAULTPORT
680
+
681
+ opts.banner = "Usage: %s [options] hostname" % File.basename($0)
682
+ opts.separator " "
683
+
684
+ opts.on("-v", "--version", "Print version") do |v|
685
+ options[:version] = v
686
+ end
687
+ opts.on("-p", "--port [NUMBER]", Integer,"Port used (default %d)" % NCXMBC_DEFAULTPORT) do |port|
688
+ options[:port] = port
689
+ end
690
+ opts.on("-U", "--user [NAME]", String, "Username used for authentication") do |name|
691
+ options[:user] = name
692
+ end
693
+ opts.on("-P", "--password [PASS]", String, "Password used for authentication") do |pass|
694
+ options[:pass] = pass
695
+ end
696
+ end
697
+
698
+ begin
699
+ inputopts.parse!
700
+ rescue OptionParser::InvalidOption => e
701
+ puts "Error: " + e
702
+ puts "\n" + inputopts.banner
703
+ exit 1
704
+ end
705
+
706
+ if(options[:version])
707
+ puts "ncXBMC version " + NCXBMC_VERSION.to_s
708
+ puts <<EOF
709
+
710
+ Copyright (C) 2009-2010 Cedric TESSIER
711
+
712
+ This program may be redistributed under
713
+ the terms of the GPL v2 License
714
+ EOF
715
+ exit 0
716
+ end
717
+
718
+ if(ARGV.length != 1)
719
+ puts inputopts
720
+ exit 1
721
+ end
722
+
723
+ hostname = ARGV.first
724
+
725
+ xbmc = XBMC::XBMC.new(hostname, options[:port], options[:user], options[:pass])
726
+
727
+ begin
728
+ puts "Connected to " + xbmc.host + " (xbmc " + xbmc.GetSystemInfo(120).first + ")"
729
+ rescue XBMC::UnauthenticatedError => e
730
+ puts "ERROR: " + e
731
+ exit 1
732
+ rescue SocketError => e
733
+ puts "ERROR: Connection error"
734
+ puts "Please check given hostname (and/or port)"
735
+ exit 1
736
+ end
737
+
738
+ interface = Interface::NcurseInterface.new(xbmc)
739
+
740
+ interface.run
741
+
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ncXBMC
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.3"
5
+ platform: ruby
6
+ authors:
7
+ - Cedric TESSIER
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-15 00:00:00 +01:00
13
+ default_executable: ncxbmc.rb
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ncurses
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0.9"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: nezetic-ruby-xbmc
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ version:
35
+ description: |-
36
+ ncXBMC is a remote XBMC client, with an ncurses interface, which aims to provide a full control of the music player over a local network.
37
+
38
+ It can be used to browse library, manage playlist, and control playback.
39
+ email: nezetic at gmail d o t com
40
+ executables:
41
+ - ncxbmc.rb
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - README
48
+ - bin/ncxbmc.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/nezetic/ncXBMC
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: ncXBMC is a remote XBMC client, with an ncurses interface
77
+ test_files: []
78
+