ncXBMC 0.3

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 +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
+