rwdmpd 0.06
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.txt +475 -0
- data/bin/rwdmpd +19 -0
- data/code/01rwdcore/01rwdcore.rb +29 -0
- data/code/01rwdcore/02helptexthashbegin.rb +4 -0
- data/code/01rwdcore/03helptexthash.rb +23 -0
- data/code/01rwdcore/04helptextend.rb +6 -0
- data/code/01rwdcore/jumplinkcommand.rb +26 -0
- data/code/01rwdcore/openhelpwindow.rb +31 -0
- data/code/01rwdcore/returntomain.rb +10 -0
- data/code/01rwdcore/rundocuments.rb +10 -0
- data/code/01rwdcore/runeditconfiguration.rb +10 -0
- data/code/01rwdcore/runhelpabout.rb +10 -0
- data/code/01rwdcore/runopentinkerdocument.rb +7 -0
- data/code/01rwdcore/rwdtinkerversion.rb +22 -0
- data/code/01rwdcore/rwdwindowreturn.rb +9 -0
- data/code/01rwdcore/selectiontab.rb +9 -0
- data/code/01rwdcore/setuphelpaboutoptions.rb +13 -0
- data/code/01rwdcore/setuptinkerdocuments.rb +6 -0
- data/code/01rwdcore/test_cases.rb +109 -0
- data/code/01rwdcore/test_harness.rb +13 -0
- data/code/01rwdcore/uploadreturns.rb +62 -0
- data/code/dd0viewphoto/dd0viewphoto.rb +3 -0
- data/code/superant.com.rwdmp3/0uninstallapplet.rb +11 -0
- data/code/superant.com.rwdmp3/clearscreendisplay.rb +7 -0
- data/code/superant.com.rwdmp3/controlmpd.rb +139 -0
- data/code/superant.com.rwdmp3/helptexthashrwdmp3.rb +41 -0
- data/code/superant.com.rwdmp3/listplaylistfiles.rb +12 -0
- data/code/superant.com.rwdmp3/loadconfigurationrecord.rb +22 -0
- data/code/superant.com.rwdmp3/loadconfigurationvariables.rb +13 -0
- data/code/superant.com.rwdmp3/loadplaylistrecord.rb +19 -0
- data/code/superant.com.rwdmp3/mpd_client.rb +41 -0
- data/code/superant.com.rwdmp3/mpd_seek.rb +23 -0
- data/code/superant.com.rwdmp3/openhelpwindowrwdmp3.rb +25 -0
- data/code/superant.com.rwdmp3/runrwdmpdwindow.rb +10 -0
- data/code/superant.com.rwdmp3/rwdtinkerversion.rb +10 -0
- data/code/superant.com.rwdmp3/saveconfigurationrecord.rb +19 -0
- data/code/superant.com.rwdmp3/saveplaylistrecord.rb +18 -0
- data/code/superant.com.rwdmp3/startmp3server.rb +16 -0
- data/code/superant.com.rwdtinkerbackwindow/diagnostictab.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/helptexthashtinkerwin2.rb +61 -0
- data/code/superant.com.rwdtinkerbackwindow/initiateapplets.rb +240 -0
- data/code/superant.com.rwdtinkerbackwindow/installgemapplet.rb +34 -0
- data/code/superant.com.rwdtinkerbackwindow/installremotegem.rb +20 -0
- data/code/superant.com.rwdtinkerbackwindow/listgemdirs.rb +12 -0
- data/code/superant.com.rwdtinkerbackwindow/listgemzips.rb +53 -0
- data/code/superant.com.rwdtinkerbackwindow/listinstalledfiles.rb +12 -0
- data/code/superant.com.rwdtinkerbackwindow/listzips.rb +33 -0
- data/code/superant.com.rwdtinkerbackwindow/loadconfigurationrecord.rb +22 -0
- data/code/superant.com.rwdtinkerbackwindow/loadconfigurationvariables.rb +14 -0
- data/code/superant.com.rwdtinkerbackwindow/network.rb +87 -0
- data/code/superant.com.rwdtinkerbackwindow/openappletname.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/openhelpwindowtinkerwin2.rb +38 -0
- data/code/superant.com.rwdtinkerbackwindow/remotegemlist.rb +24 -0
- data/code/superant.com.rwdtinkerbackwindow/removeapplet.rb +46 -0
- data/code/superant.com.rwdtinkerbackwindow/removeappletvariables.rb +52 -0
- data/code/superant.com.rwdtinkerbackwindow/runremoteinstall.rb +11 -0
- data/code/superant.com.rwdtinkerbackwindow/runrwdtinkerbackwindow.rb +15 -0
- data/code/superant.com.rwdtinkerbackwindow/rwdtinkerwin2version.rb +13 -0
- data/code/superant.com.rwdtinkerbackwindow/saveconfigurationrecord.rb +19 -0
- data/code/superant.com.rwdtinkerbackwindow/viewappletcontents.rb +22 -0
- data/code/superant.com.rwdtinkerbackwindow/viewgemappletcontents.rb +24 -0
- data/code/zz0applicationend/zz0end.rb +4 -0
- data/configuration/language.dist +8 -0
- data/configuration/rwdapplicationidentity.dist +3 -0
- data/configuration/rwdtinker.dist +15 -0
- data/configuration/rwdwmpd-0.06.dist +14 -0
- data/configuration/tinkerwin2variables.dist +18 -0
- data/gui/00coreguibegin/applicationguitop.rwd +4 -0
- data/gui/frontwindow0/cc0openphoto.rwd +22 -0
- data/gui/frontwindowselections/00selectiontabbegin.rwd +11 -0
- data/gui/frontwindowselections/jumplinkcommands.rwd +15 -0
- data/gui/frontwindowselections/wwselectionend.rwd +3 -0
- data/gui/frontwindowtdocuments/00documentbegin.rwd +6 -0
- data/gui/frontwindowtdocuments/tinkerdocuments.rwd +14 -0
- data/gui/frontwindowtdocuments/zzdocumentend.rwd +8 -0
- data/gui/helpaboutbegin/zzzrwdlasttab.rwd +6 -0
- data/gui/helpaboutbegin/zzzzhelpscreenstart.rwd +3 -0
- data/gui/helpaboutbegin/zzzzzzhelpabouttab.rwd +15 -0
- data/gui/helpaboutzend/helpscreenend.rwd +3 -0
- data/gui/helpaboutzend/zhelpscreenstart2.rwd +3 -0
- data/gui/helpaboutzend/zzzzhelpabout2.rwd +15 -0
- data/gui/helpaboutzend/zzzzhelpscreen2end.rwd +3 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/10appletbegin.rwd +4 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/20rwdmpd.rwd +37 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/25rwdseek.rwd +23 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/30editplaylist.rwd +27 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/88viewconfiguration.rwd +32 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/89rwddiagnostics.rwd +26 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/90jumplinkcommands.rwd +17 -0
- data/gui/tinkerbackwindows/superant.com.rwdmp3/9end.rwd +6 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/1appname.rwd +5 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/40rwdlistzips.rwd +41 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/45installremotezip.rwd +44 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/50rwdlistapplets.rwd +44 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/60editconfiguration.rwd +30 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/70rwddiagnostics.rwd +29 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/81jumplinkcommands.rwd +17 -0
- data/gui/tinkerbackwindows/superant.com.tinkerbackwindow/9backend.rwd +6 -0
- data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/1appname.rwd +31 -0
- data/gui/tinkerbackwindows/superant.com.tinkerhelpwindow/9end.rwd +4 -0
- data/gui/tinkerbackwindows/superant.com.versionwindow/1appname.rwd +19 -0
- data/gui/tinkerbackwindows/superant.com.versionwindow/helpaboutwindow.rwd +17 -0
- data/gui/zzcoreguiend/yy9rwdend.rwd +4 -0
- data/init.rb +277 -0
- data/installed/mpdmusicdata2.inf +2 -0
- data/installed/rwdwmpd-0.06.inf +10 -0
- data/installed/temp.rb +1 -0
- data/lang/en/rwdcore/languagefile.rb +58 -0
- data/lang/es/rwdcore/languagefile-es.rb +62 -0
- data/lang/fr/rwdcore/languagefile.rb +64 -0
- data/lang/jp/rwdcore/languagefile.rb +72 -0
- data/lang/nl/rwdcore/languagefile.rb +75 -0
- data/lib/librmpd.rb +1152 -0
- data/lib/mpdserver.rb +1204 -0
- data/lib/rconftool.rb +387 -0
- data/lib/rwd/browser.rb +123 -0
- data/lib/rwd/ftools.rb +174 -0
- data/lib/rwd/mime.rb +328 -0
- data/lib/rwd/net.rb +876 -0
- data/lib/rwd/ruby.rb +889 -0
- data/lib/rwd/rwd.rb +1422 -0
- data/lib/rwd/sgml.rb +236 -0
- data/lib/rwd/thread.rb +63 -0
- data/lib/rwd/tree.rb +371 -0
- data/lib/rwd/xml.rb +101 -0
- data/lib/rwdthemes/default.rwd +317 -0
- data/lib/rwdthemes/pda.rwd +72 -0
- data/lib/rwdthemes/windowslike.rwd +171 -0
- data/lib/zip/ioextras.rb +114 -0
- data/lib/zip/stdrubyext.rb +111 -0
- data/lib/zip/tempfile_bugfixed.rb +195 -0
- data/lib/zip/zip.rb +1378 -0
- data/lib/zip/zipfilesystem.rb +558 -0
- data/lib/zip/ziprequire.rb +61 -0
- data/music/classicalmues.mp3 +0 -0
- data/rwd_files/HowTo_Mpd.txt +136 -0
- data/rwd_files/HowTo_Tinker.txt +482 -0
- data/rwd_files/HowTo_TinkerWin2.txt +202 -0
- data/rwd_files/Readme.txt +57 -0
- data/rwd_files/RubyWebDialogs.html +6 -0
- data/rwd_files/favicon.ico +0 -0
- data/rwd_files/rdoc-style.css +175 -0
- data/rwd_files/rwdapplications.html +54 -0
- data/rwd_files/tinker.png +0 -0
- data/rwdconfig.dist +22 -0
- data/rwdmpd.rb +1 -0
- data/tests/RubyGauge.rb +179 -0
- data/tests/checkdepends.sh +4 -0
- data/tests/cleancnf.sh +6 -0
- data/tests/makedist-rwdwmpd.rb +58 -0
- data/tests/makedist.rb +66 -0
- data/tests/rdep.rb +354 -0
- data/tests/totranslate.lang +93 -0
- data/zips/rwdwaddresses-1.06.zip +0 -0
- data/zips/rwdwcalc-0.62.zip +0 -0
- data/zips/rwdwfoldeditor-0.04.zip +0 -0
- data/zips/rwdwgutenberg-0.09.zip +0 -0
- data/zips/rwdwmpd-0.06.zip +0 -0
- data/zips/rwdwruby-1.07.zip +0 -0
- data/zips/temp.rb +1 -0
- data/zips/tinkerbellw-0.02.zip +0 -0
- data/zips/wrubyslippers-1.06.zip +0 -0
- metadata +220 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
# rwdwmpd - music controller
|
2
|
+
code/superant.com.rwdmp3
|
3
|
+
rwd_files/HowTo_Mpd.txt
|
4
|
+
configuration/rwdwmpd-0.06.dist
|
5
|
+
configuration/rwdwmpd-0.06.cnf
|
6
|
+
gui/tinkerbackwindows/superant.com.rwdmp3
|
7
|
+
tests/makedist-rwdwmpd.rb
|
8
|
+
tests/gemspec-rwdwmpd
|
9
|
+
lib/mpdserver.rb
|
10
|
+
lib/librmpd.rb
|
data/installed/temp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# this file does nothing
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# English Language files for RwdTinker core
|
2
|
+
|
3
|
+
:applet_installed => '"applet installed!"',
|
4
|
+
:application_version => '"Application Version"',
|
5
|
+
:clear => "Clear",
|
6
|
+
:cancel => "Cancel",
|
7
|
+
:clickfor_version => "Click for Version" ,
|
8
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
9
|
+
:documents => '"Documents"',
|
10
|
+
:edit => "edit" ,
|
11
|
+
:file_name => '"File Name:"',
|
12
|
+
:fill_record => '"Fill Record"',
|
13
|
+
:help => '"Help"',
|
14
|
+
:help_about => '"Help About"',
|
15
|
+
:install_applet => '"install (rwdtinker) applet"',
|
16
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
17
|
+
:list_applets => '"Remove Applets"',
|
18
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
19
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
20
|
+
:list_files => '"List Files"',
|
21
|
+
:list_installed_gems => '"List installed Gems"' ,
|
22
|
+
:list_photos => '"List Photos"',
|
23
|
+
:list_zips => '"List Zips"',
|
24
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
25
|
+
:menu_panel => '"Menu Panel"',
|
26
|
+
:module_unknown => '"Unknown Module"',
|
27
|
+
:next => '"Next"',
|
28
|
+
:open => "Open",
|
29
|
+
:open_document => '"Open Document"',
|
30
|
+
:openselectedhelp_about => '"Open selected help about"',
|
31
|
+
:reload_variables => '"Reload Variables"' ,
|
32
|
+
:remove_applet => '"remove applet"',
|
33
|
+
:return => "Return",
|
34
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
35
|
+
:rwdtinker => "RwdTinker",
|
36
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
37
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
38
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
39
|
+
:showjump_links => '"Show Jump Links"',
|
40
|
+
:showdocument_list => '"Show Document List"',
|
41
|
+
:showhelpabout_links => '"Show Help About Links"',
|
42
|
+
|
43
|
+
:save => "Save" ,
|
44
|
+
:save_changes => '"Save Changes"',
|
45
|
+
:selection_panel => '"Menu Panel"',
|
46
|
+
:selection_tab => '"Menu Tab"',
|
47
|
+
:tinker_logo => "Tinker Logo",
|
48
|
+
:tinkerback_window => '"Tinker Back Window"',
|
49
|
+
:viewapplet_contents => '"View Applet Contents"',
|
50
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
51
|
+
:viewinstalled_text => '"View Install Text"',
|
52
|
+
:view_platform => '"view platform"',
|
53
|
+
:viewplatform_information => '"View Platform Information"',
|
54
|
+
:view_event => '"View event"',
|
55
|
+
:viewinstall_text => '"View Install Text"' ,
|
56
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
57
|
+
:view_photo => '"View Photo"',
|
58
|
+
:window => "Window" ,
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Spanish Language files for RwdTinker core
|
2
|
+
|
3
|
+
:applet_installed => '"applet instalado!"',
|
4
|
+
:application_version => '"Application Version"',
|
5
|
+
:clear => "Clear",
|
6
|
+
:cancel => "Cancelaci�n",
|
7
|
+
:clickfor_version => '"click para version"' ,
|
8
|
+
:clickbelowtoviewlistof_zip => '"click abajo a ver lista de zips"',
|
9
|
+
:documents => '"Documentos"',
|
10
|
+
:edit => "Editar" ,
|
11
|
+
:file_name => '"Fila Nombre:"',
|
12
|
+
:fill_record => '"Fill Record"',
|
13
|
+
:help => '"Ayuda"',
|
14
|
+
:help_about => '"Ayuda sobre"',
|
15
|
+
:install_applet => '"instale (rwdtinker) applet"',
|
16
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
17
|
+
:list_applets => '"List Applets"',
|
18
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
19
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
20
|
+
|
21
|
+
:list_files => '"Lista de Archivos"',
|
22
|
+
:list_installed_gems => '"Lista de Instalado Gems"' ,
|
23
|
+
:list_photos => '"Lista de Photos"',
|
24
|
+
:list_zips => '"List Zips"',
|
25
|
+
:listzipdirappletsavailable => '"Lista (zip directory) applets available for installation"',
|
26
|
+
:menu_panel => '"panel de herramientas"',
|
27
|
+
:module_unknown => '"Module desconocido"',
|
28
|
+
:next => "Siguiente",
|
29
|
+
:open => "Abrir",
|
30
|
+
:open_document => '"Abrir documento"',
|
31
|
+
:openselectedhelp_about => '"Open selected help about"',
|
32
|
+
:reload_variables => '"Reload Variables"' ,
|
33
|
+
:remove_applet => '"remove applet"',
|
34
|
+
:return => "anterior",
|
35
|
+
:rwdtinker_window_2 => '"RwdTinker Window 2"',
|
36
|
+
:rwdtinker => "RwdTinker",
|
37
|
+
:rwdtinker_back_window => '"RwdTinker Ventana Trasera"',
|
38
|
+
:rwdtinker_help => '"RwdTinker Ayuda"',
|
39
|
+
:rwdtinker_help_window => '"RwdTinker Ventana Ayuda"',
|
40
|
+
:showdocument_list => '"Show Document List"',
|
41
|
+
:showhelpabout_links => '"Show Help About Links"',
|
42
|
+
|
43
|
+
:showjump_links => '"Mostrar enlace saltar"',
|
44
|
+
|
45
|
+
:save => "Guardar" ,
|
46
|
+
:save_changes => '"Guardar los cambios"',
|
47
|
+
:rwdtinker => "RwdTinker",
|
48
|
+
:selection_panel => '"Panel de Selecci�n"',
|
49
|
+
|
50
|
+
:tinker_logo => '"Insignia de Tinker"',
|
51
|
+
:selection_tab => '"Selection Tab"',
|
52
|
+
:tinkerback_window => '"Tinker Back Window"',
|
53
|
+
:viewapplet_contents => '"View Applet Contents"',
|
54
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
55
|
+
:viewinstalled_text => '"View Install Text"',
|
56
|
+
:view_platform => '"view platform"',
|
57
|
+
:viewplatform_information => '"View Platform Information"',
|
58
|
+
:view_event => '"Ver evento"',
|
59
|
+
:viewinstall_text => '"Ver Texto"' ,
|
60
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
61
|
+
:view_photo => '"Ver Photo"' ,
|
62
|
+
:window => "Ventana" ,
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# English Language files for RwdTinker core
|
2
|
+
|
3
|
+
:applet_installed => '"applet installed!"',
|
4
|
+
:application_version => '"Application Version"',
|
5
|
+
:clear => "Clear",
|
6
|
+
:cancel => "Cancel",
|
7
|
+
:clickfor_version => "Click for Version" ,
|
8
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
9
|
+
:documents => '"Documents"',
|
10
|
+
:edit => "edit" ,
|
11
|
+
:file_name => '"File Name:"',
|
12
|
+
:fill_record => '"Fill Record"',
|
13
|
+
:gemcommands => '"Gem Commands"',
|
14
|
+
:gemfiles => '"Gem Files"' ,
|
15
|
+
:gemdocs => '"Gem Documents"' ,
|
16
|
+
:gem_name => '"Gem Name"' ,
|
17
|
+
:help => '"Help"',
|
18
|
+
:help_about => '"Help About"',
|
19
|
+
:installgem => '"Install Gem"' ,
|
20
|
+
:install_applet => '"install (rwdtinker) applet"',
|
21
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
22
|
+
:list_applets => '"List Applets"',
|
23
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
24
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
25
|
+
:list_files => '"List Files"',
|
26
|
+
:list_installed_gems => '"List installed Gems"' ,
|
27
|
+
:list_photos => '"List Photos"',
|
28
|
+
:list_zips => '"List Zips"',
|
29
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
30
|
+
:menu_panel => '"Menu Panel"',
|
31
|
+
:module_unknown => '"Unknown Module"',
|
32
|
+
:next => '"Next"',
|
33
|
+
:open => "Open",
|
34
|
+
:open_document => '"Open Document"',
|
35
|
+
:openselectedhelp_about => '"Open selected help about"',
|
36
|
+
:reload_variables => '"Reload Variables"' ,
|
37
|
+
:remove_applet => '"remove applet"',
|
38
|
+
:return => "Return",
|
39
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
40
|
+
:rwdtinker => "RwdTinker",
|
41
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
42
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
43
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
44
|
+
:showjump_links => '"Show Jump Links"',
|
45
|
+
:showdocument_list => '"Show Document List"',
|
46
|
+
:showhelpabout_links => '"Show Help About Links"',
|
47
|
+
|
48
|
+
:save => "Save" ,
|
49
|
+
:save_changes => '"Save Changes"',
|
50
|
+
:selection_panel => '"Selection Panel"',
|
51
|
+
:selection_tab => '"Selection Tab"',
|
52
|
+
:tinker_logo => "Tinker Logo",
|
53
|
+
:tinkerback_window => '"Tinker Back Window"',
|
54
|
+
:viewapplet_contents => '"View Applet Contents"',
|
55
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
56
|
+
:viewinstalled_text => '"View Install Text"',
|
57
|
+
:view_platform => '"view platform"',
|
58
|
+
:viewplatform_information => '"View Platform Information"',
|
59
|
+
:view_event => '"View event"',
|
60
|
+
:view_gem => '"View Gem"',
|
61
|
+
:viewinstall_text => '"View Install Text"' ,
|
62
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
63
|
+
:view_photo => '"View Photo"',
|
64
|
+
:window => "Window" ,
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Japanese Language files for RwdTinker core
|
2
|
+
:cancel => "とりけし",
|
3
|
+
:open => "あける",
|
4
|
+
:view_event => "けんかい ぎょうじょ",
|
5
|
+
:edit_event => "しゅうろく",
|
6
|
+
:install_gem => "インストール",
|
7
|
+
:list_installed_gems => "かきつらねる",
|
8
|
+
:view_gem => "けんかい",
|
9
|
+
:edit => "しゅうろく",
|
10
|
+
:gem_commands => "コマンド",
|
11
|
+
:gem_files => "ファイル",
|
12
|
+
:gem_docs => "しょるい",
|
13
|
+
:gem_name => "こうもくめい",
|
14
|
+
:selection_panel => "せんたく",
|
15
|
+
:documents => "オンラインマニュアル",
|
16
|
+
|
17
|
+
# English Language files for RwdTinker core
|
18
|
+
|
19
|
+
:applet_installed => '"applet installed!"',
|
20
|
+
:application_version => '"Application Version"',
|
21
|
+
:clear => "Clear",
|
22
|
+
:clickfor_version => "Click for Version" ,
|
23
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
24
|
+
|
25
|
+
:file_name => '"File Name:"',
|
26
|
+
:fill_record => '"Fill Record"',
|
27
|
+
:help => '"Help"',
|
28
|
+
:help_about => '"Help About"',
|
29
|
+
|
30
|
+
:install_applet => '"install (rwdtinker) applet"',
|
31
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
32
|
+
:list_applets => '"List Applets"',
|
33
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
34
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
35
|
+
:list_files => '"List Files"',
|
36
|
+
|
37
|
+
:list_photos => '"List Photos"',
|
38
|
+
:list_zips => '"List Zips"',
|
39
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
40
|
+
:menu_panel => '"Menu Panel"',
|
41
|
+
:module_unknown => '"Unknown Module"',
|
42
|
+
:next => '"Next"',
|
43
|
+
|
44
|
+
:open_document => '"Open Document"',
|
45
|
+
:openselectedhelp_about => '"Open selected help about"',
|
46
|
+
:reload_variables => '"Reload Variables"' ,
|
47
|
+
:remove_applet => '"remove applet"',
|
48
|
+
:return => "Return",
|
49
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
50
|
+
:rwdtinker => "RwdTinker",
|
51
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
52
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
53
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
54
|
+
:showjump_links => '"Show Jump Links"',
|
55
|
+
:showdocument_list => '"Show Document List"',
|
56
|
+
:showhelpabout_links => '"Show Help About Links"',
|
57
|
+
|
58
|
+
:save => "Save" ,
|
59
|
+
:save_changes => '"Save Changes"',
|
60
|
+
|
61
|
+
:selection_tab => '"Selection Tab"',
|
62
|
+
:tinker_logo => "Tinker Logo",
|
63
|
+
:tinkerback_window => '"Tinker Back Window"',
|
64
|
+
:viewapplet_contents => '"View Applet Contents"',
|
65
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
66
|
+
:viewinstalled_text => '"View Install Text"',
|
67
|
+
:view_platform => '"view platform"',
|
68
|
+
:viewplatform_information => '"View Platform Information"',
|
69
|
+
:viewinstall_text => '"View Install Text"' ,
|
70
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
71
|
+
:view_photo => '"View Photo"',
|
72
|
+
:window => "Window" ,
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# English Language files for RwdTinker core
|
2
|
+
|
3
|
+
:cancel => '"Annuleren"',
|
4
|
+
:open => '"Openen"',
|
5
|
+
:save => '"Opslaan"',
|
6
|
+
:window => '"Venster"',
|
7
|
+
:applet_installed => '"Applet geinstalleerd"',
|
8
|
+
:clickfor_version => '"Klier hier voor de Versie"',
|
9
|
+
:documents => '"Documenten"',
|
10
|
+
:help => '"Help"',
|
11
|
+
:module_unknown => '"Module onbekend"',
|
12
|
+
:next => '"Volgende"',
|
13
|
+
:rwdtinker => '"RWDTinker"',
|
14
|
+
:rwdtinker_help_window => '"RWDTinker Help Venster"',
|
15
|
+
:rwdtinker_help => '"RWDTinker Help"',
|
16
|
+
:rwdtinker_window_2 => '"RWDTinker Venster 2"',
|
17
|
+
:rwdtinker_back_window => '"RwdTinker Back Venster"',
|
18
|
+
:tinker_logo => '"RWDTinker Logo"',
|
19
|
+
:selection_panel => '"Selectie Paneel"',
|
20
|
+
# English Language files for RwdTinker core
|
21
|
+
|
22
|
+
|
23
|
+
:applet_installed => '"applet installed!"',
|
24
|
+
:application_version => '"Application Version"',
|
25
|
+
:clear => "Clear",
|
26
|
+
:clickfor_version => "Click for Version" ,
|
27
|
+
:clickbelowtoviewlistof_zip => '"click below to view lists of zips"',
|
28
|
+
|
29
|
+
:edit => "edit" ,
|
30
|
+
:file_name => '"File Name:"',
|
31
|
+
:fill_record => '"Fill Record"',
|
32
|
+
:help => '"Help"',
|
33
|
+
:help_about => '"Help About"',
|
34
|
+
:install_applet => '"install (rwdtinker) applet"',
|
35
|
+
:jumptoapplication_location => '"Jump to Application Location"',
|
36
|
+
:list_applets => '"List Applets"',
|
37
|
+
:listappletsinthegem_directory => '"List applets in the Gem Directory"',
|
38
|
+
:listappletsavailablefor_installation => "List applets available for installation",
|
39
|
+
:list_files => '"List Files"',
|
40
|
+
:list_installed_gems => '"List installed Gems"' ,
|
41
|
+
:list_photos => '"List Photos"',
|
42
|
+
:list_zips => '"List Zips"',
|
43
|
+
:listzipdirappletsavailable => '"List (zip directory) applets available for installation"',
|
44
|
+
:menu_panel => '"Menu Panel"',
|
45
|
+
|
46
|
+
|
47
|
+
:open_document => '"Open Document"',
|
48
|
+
:openselectedhelp_about => '"Open selected help about"',
|
49
|
+
:reload_variables => '"Reload Variables"' ,
|
50
|
+
:remove_applet => '"remove applet"',
|
51
|
+
:return => "Return",
|
52
|
+
:rwdtinker_window_2 => "RwdTinker Window 2",
|
53
|
+
:rwdtinker => "RwdTinker",
|
54
|
+
:rwdtinker_back_window => '"RwdTinker Back Window"',
|
55
|
+
:rwdtinker_help => '"RwdTinker Help"',
|
56
|
+
:rwdtinker_help_window => '"RwdTinker Help Window"',
|
57
|
+
:showjump_links => '"Show Jump Links"',
|
58
|
+
:showdocument_list => '"Show Document List"',
|
59
|
+
:showhelpabout_links => '"Show Help About Links"',
|
60
|
+
|
61
|
+
|
62
|
+
:save_changes => '"Save Changes"',
|
63
|
+
:selection_tab => '"Selection Tab"',
|
64
|
+
:tinker_logo => "Tinker Logo",
|
65
|
+
:tinkerback_window => '"Tinker Back Window"',
|
66
|
+
:viewapplet_contents => '"View Applet Contents"',
|
67
|
+
:viewalreadyinstalled_applications => '"View already installed GEM applications"',
|
68
|
+
:viewinstalled_text => '"View Install Text"',
|
69
|
+
:view_platform => '"view platform"',
|
70
|
+
:viewplatform_information => '"View Platform Information"',
|
71
|
+
:view_event => '"View event"',
|
72
|
+
:viewinstall_text => '"View Install Text"' ,
|
73
|
+
:viewlistinstall_files => '"View List of Installed Files"',
|
74
|
+
:view_photo => '"View Photo"',
|
75
|
+
|
data/lib/librmpd.rb
ADDED
@@ -0,0 +1,1152 @@
|
|
1
|
+
#
|
2
|
+
#== librmpd.rb
|
3
|
+
#
|
4
|
+
# librmpd.rb is another Ruby MPD Library with a goal of greater
|
5
|
+
# ease of use, more functionality, and thread safety
|
6
|
+
#
|
7
|
+
# Author:: Andrew Rader (bitwise_mcgee AT yahoo.com | http://nymb.us)
|
8
|
+
# Copyright:: Copyright (c) 2006 Andrew Rader
|
9
|
+
# License:: Distributed under the GNU GPL v2 (See COPYING file)
|
10
|
+
#
|
11
|
+
# This was written with MPD version 0.11.5 (http://www.musicpd.org)
|
12
|
+
#
|
13
|
+
# The main class is the MPD class. This provides the functionality for
|
14
|
+
# talking to the server as well as setting up callbacks for when events
|
15
|
+
# occur (such as song changes, state changes, etc). The use of callbacks
|
16
|
+
# is optional, if they are used a seperate thread will continuously poll
|
17
|
+
# the server on its status, when something is changed, your program will
|
18
|
+
# be notified via any callbacks you have set. Most methods are the same
|
19
|
+
# as specified in the MPD Server Protocol, however some have been modified
|
20
|
+
# or renamed. Most notable is the list* and lsinfo functions have been
|
21
|
+
# replace with more sane methods (such as `files` for all files)
|
22
|
+
#
|
23
|
+
#== Usage
|
24
|
+
#
|
25
|
+
# First create an MPD object
|
26
|
+
#
|
27
|
+
# require 'rubygems'
|
28
|
+
# require 'librmpd'
|
29
|
+
#
|
30
|
+
# mpd = MPD.new 'localhost', 6600
|
31
|
+
#
|
32
|
+
# and connect it to the server
|
33
|
+
#
|
34
|
+
# mpd.connect
|
35
|
+
#
|
36
|
+
# You can now issue any of the commands. Each command is documented below.
|
37
|
+
#
|
38
|
+
#=== Callbacks
|
39
|
+
#
|
40
|
+
# Callbacks are a way to easily setup your client as event based, rather
|
41
|
+
# than polling based. This means rather than having to check for changes
|
42
|
+
# in the server, you setup a few methods that will be called when those
|
43
|
+
# changes occur. For example, you could have a 'state_changed' method
|
44
|
+
# that will be called whenever the server changes state. You could then
|
45
|
+
# have this method change a label to reflect to the new state.
|
46
|
+
#
|
47
|
+
# To use callbacks in your program, first setup your callback methods. For
|
48
|
+
# example, say you have the class MyClient. Simply define whatever
|
49
|
+
# callbacks you want inside your class. See the documentation on the
|
50
|
+
# callback type constants in the MPD class for details on how each callback
|
51
|
+
# is called
|
52
|
+
#
|
53
|
+
# Once you have your callback methods defined, use the register_callback
|
54
|
+
# methods to inform librmpd about them. You can have multiple callbacks
|
55
|
+
# for each type of callback without problems. Simply use object.method('method_name')
|
56
|
+
# to get a reference to a Method object. Pass this object to the
|
57
|
+
# register_callback (along with the proper type value), and you're set.
|
58
|
+
#
|
59
|
+
# An Example:
|
60
|
+
#
|
61
|
+
# class MyClient
|
62
|
+
# ...
|
63
|
+
# def state_callback( newstate )
|
64
|
+
# puts "MPD Changed State: #{newstate}"
|
65
|
+
# end
|
66
|
+
# ...
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# client = MyClient.new
|
70
|
+
# mpd = MPD.new
|
71
|
+
# mpd.register_callback(client.method('state_callback'), MPD::STATE_CALLBACK)
|
72
|
+
#
|
73
|
+
# # Connect and Enable Callbacks
|
74
|
+
# mpd.connect( true )
|
75
|
+
#
|
76
|
+
# In order for the callback to be used, you must enable callbacks when you
|
77
|
+
# connect by passing true to the connect method. Now, whenever the state changes
|
78
|
+
# on the server, myclientobj's state_callback method will be called (and passed
|
79
|
+
# the new state as an argument)
|
80
|
+
|
81
|
+
class MPD
|
82
|
+
|
83
|
+
require 'socket'
|
84
|
+
require 'thread'
|
85
|
+
|
86
|
+
#
|
87
|
+
# These are the callback types used in registering callbacks
|
88
|
+
|
89
|
+
# STATE_CALLBACK: This is used to listen for changes in the server state
|
90
|
+
#
|
91
|
+
# The callback will always be called with a single string argument
|
92
|
+
# which may an empty string.
|
93
|
+
STATE_CALLBACK = 0
|
94
|
+
|
95
|
+
# CURRENT_SONG_CALLBACK: This is used to listen for changes in the current
|
96
|
+
#
|
97
|
+
# song being played by the server.
|
98
|
+
#
|
99
|
+
# The callback will always be called with a single argument, an MPD::Song
|
100
|
+
# object, or, if there were problems, nil
|
101
|
+
CURRENT_SONG_CALLBACK = 1
|
102
|
+
|
103
|
+
# PLAYLIST_CALLBACK: This is used to listen for when changes in the playlist
|
104
|
+
# are made.
|
105
|
+
#
|
106
|
+
# The callback will always be called with a single argument, an integer
|
107
|
+
# value for the current playlist or 0 if there were problems
|
108
|
+
PLAYLIST_CALLBACK = 2
|
109
|
+
|
110
|
+
# TIME_CALLBACK: This is used to listen for when the playback time changes
|
111
|
+
#
|
112
|
+
# The callback will always be called with two arguments. The first is
|
113
|
+
# the integer number of seconds elapsed (or 0 if errors), the second is
|
114
|
+
# the total number of seconds in the song (or 0 if errors)
|
115
|
+
TIME_CALLBACK = 3
|
116
|
+
|
117
|
+
# VOLUME_CALLBACK: This is used to listen for when the volume changes
|
118
|
+
#
|
119
|
+
# The callback will always be called with a single argument, an integer
|
120
|
+
# value of the volume (or 0 on errors)
|
121
|
+
VOLUME_CALLBACK = 4
|
122
|
+
|
123
|
+
# REPEAT_CALLBACK: This is used to listen for changes to the repeat flag
|
124
|
+
#
|
125
|
+
# The callback will always be called with a single argument, a boolean
|
126
|
+
# true or false depending on if the repeat flag is set / unset
|
127
|
+
REPEAT_CALLBACK = 5
|
128
|
+
|
129
|
+
# RANDOM_CALLBACK: This is used to listen for changed to the random flag
|
130
|
+
#
|
131
|
+
# The callback will always be called with a single argument, a boolean
|
132
|
+
# true or false depending on if the random flag is set / unset
|
133
|
+
RANDOM_CALLBACK = 6
|
134
|
+
|
135
|
+
# PLAYLIST_LENGTH_CALLBACK: This is used to listen for changes to the
|
136
|
+
# playlist length
|
137
|
+
#
|
138
|
+
# The callback will always be called with a single argument, an integer
|
139
|
+
# value of the current playlist's length (or 0 on errors)
|
140
|
+
PLAYLIST_LENGTH_CALLBACK = 7
|
141
|
+
|
142
|
+
# CROSSFADE_CALLBACK: This is used to listen for changes in the crossfade
|
143
|
+
# setting
|
144
|
+
#
|
145
|
+
# The callback will always be called with a single argument, an integer
|
146
|
+
# value of the number of seconds the crossfade is set to (or 0 on errsors)
|
147
|
+
CROSSFADE_CALLBACK = 8
|
148
|
+
|
149
|
+
# CURRENT_SONGID_CALLBACK: This is used to listen for changes in the
|
150
|
+
# current song's songid
|
151
|
+
#
|
152
|
+
# The callback will always be called with a single argument, an integer
|
153
|
+
# value of the id of the current song (or 0 on errors)
|
154
|
+
CURRENT_SONGID_CALLBACK = 9
|
155
|
+
|
156
|
+
# BITRATE_CALLBACK: This is used to listen for changes in the playback
|
157
|
+
# bitrate
|
158
|
+
#
|
159
|
+
# The callback will always be called with a single argument, an integer
|
160
|
+
# value of the bitrate of the playback (or 0 on errors)
|
161
|
+
BITRATE_CALLBACK = 10
|
162
|
+
|
163
|
+
# AUDIO_CALLBACK: This is used to listen for changes in the audio
|
164
|
+
# quality data (sample rate etc)
|
165
|
+
#
|
166
|
+
# The callback will always be called with three arguments, first,
|
167
|
+
# an integer holding the sample rate (or 0 on errors), next an
|
168
|
+
# integer holding the number of bits (or 0 on errors), finally an
|
169
|
+
# integer holding the number of channels (or 0 on errors)
|
170
|
+
AUDIO_CALLBACK = 11
|
171
|
+
|
172
|
+
# CONNECTION_CALLBACK: This is used to listen for changes in the
|
173
|
+
# connection to the server
|
174
|
+
#
|
175
|
+
# The callback will always be called with a single argument,
|
176
|
+
# a boolean true if the client is now connected to the server,
|
177
|
+
# and a boolean false if it has been disconnected
|
178
|
+
CONNECTION_CALLBACK = 12
|
179
|
+
|
180
|
+
#
|
181
|
+
#== Song
|
182
|
+
#
|
183
|
+
# This class is a glorified Hash used to represent a song
|
184
|
+
# You can access the various fields of a song (such as title) by
|
185
|
+
# either the normal hash method (song['title']) or by using
|
186
|
+
# the field as a method name (song.title).
|
187
|
+
#
|
188
|
+
# If the field doesn't exist or isn't set, nil will be returned
|
189
|
+
#
|
190
|
+
class Song < Hash
|
191
|
+
def method_missing(m, *a)
|
192
|
+
key = m.to_s
|
193
|
+
if key =~ /=$/
|
194
|
+
self[$`] = a[0]
|
195
|
+
elsif a.empty?
|
196
|
+
self[key]
|
197
|
+
else
|
198
|
+
raise NoMethodError, "#{m}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Initialize an MPD object with the specified hostname and port
|
204
|
+
# When called without arguments, 'localhost' and 6600 are used
|
205
|
+
def initialize( hostname = 'localhost', port = 6600 )
|
206
|
+
@hostname = hostname
|
207
|
+
@port = port
|
208
|
+
@socket = nil
|
209
|
+
@stop_cb_thread = false
|
210
|
+
@mutex = Mutex.new
|
211
|
+
@cb_thread = nil
|
212
|
+
@callbacks = []
|
213
|
+
@callbacks[STATE_CALLBACK] = []
|
214
|
+
@callbacks[CURRENT_SONG_CALLBACK] = []
|
215
|
+
@callbacks[PLAYLIST_CALLBACK] = []
|
216
|
+
@callbacks[TIME_CALLBACK] = []
|
217
|
+
@callbacks[VOLUME_CALLBACK] = []
|
218
|
+
@callbacks[REPEAT_CALLBACK] = []
|
219
|
+
@callbacks[RANDOM_CALLBACK] = []
|
220
|
+
@callbacks[PLAYLIST_LENGTH_CALLBACK] = []
|
221
|
+
@callbacks[CROSSFADE_CALLBACK] = []
|
222
|
+
@callbacks[CURRENT_SONGID_CALLBACK] = []
|
223
|
+
@callbacks[BITRATE_CALLBACK] = []
|
224
|
+
@callbacks[AUDIO_CALLBACK] = []
|
225
|
+
@callbacks[CONNECTION_CALLBACK] = []
|
226
|
+
end
|
227
|
+
|
228
|
+
# This will store the given method onto the given type's callback
|
229
|
+
# list. First you must get a reference to the method to call by
|
230
|
+
# the following:
|
231
|
+
#
|
232
|
+
# callback_method = my_object.method 'method name'
|
233
|
+
#
|
234
|
+
# Then you can call register_callback:
|
235
|
+
#
|
236
|
+
# mpd.register_callback( callback_method, MPD::STATE_CALLBACK )
|
237
|
+
#
|
238
|
+
# Now my_object's 'method name' method will be called whenever the
|
239
|
+
# state changes
|
240
|
+
def register_callback( method, type )
|
241
|
+
@callbacks[type].push method
|
242
|
+
end
|
243
|
+
|
244
|
+
#
|
245
|
+
# Connect to the daemon
|
246
|
+
# When called without any arguments, this will just
|
247
|
+
# connect to the server and wait for your commands
|
248
|
+
# When called with true as an argument, this will
|
249
|
+
# enable callbacks by starting a seperate polling thread.
|
250
|
+
# This polling thread will also automatically reconnect
|
251
|
+
# If is disconnected for whatever reason.
|
252
|
+
#
|
253
|
+
# connect will return OK plus the version string
|
254
|
+
# if successful, otherwise an error will be raised
|
255
|
+
#
|
256
|
+
# If connect is called on an already connected instance,
|
257
|
+
# a RuntimeError is raised
|
258
|
+
def connect( callbacks = false )
|
259
|
+
if self.connected?
|
260
|
+
raise 'MPD Error: Already Connected'
|
261
|
+
end
|
262
|
+
|
263
|
+
@socket = TCPSocket::new @hostname, @port
|
264
|
+
ret = @socket.gets # Read the version
|
265
|
+
|
266
|
+
if callbacks and (@cb_thread.nil? or !@cb_thread.alive?)
|
267
|
+
@stop_cb_thread = false
|
268
|
+
@cb_thread = Thread.new( self ) { |mpd|
|
269
|
+
old_status = {}
|
270
|
+
song = ''
|
271
|
+
connected = ''
|
272
|
+
while !@stop_cb_thread
|
273
|
+
begin
|
274
|
+
status = mpd.status
|
275
|
+
rescue
|
276
|
+
status = {}
|
277
|
+
end
|
278
|
+
|
279
|
+
begin
|
280
|
+
c = mpd.connected?
|
281
|
+
rescue
|
282
|
+
c = false
|
283
|
+
end
|
284
|
+
|
285
|
+
if connected != c
|
286
|
+
connected = c
|
287
|
+
for cb in @callbacks[CONNECTION_CALLBACK]
|
288
|
+
cb.call connected
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
if old_status['time'] != status['time']
|
293
|
+
if old_status['time'].nil? or old_status['time'].empty?
|
294
|
+
old_status['time'] = '0:0'
|
295
|
+
end
|
296
|
+
t = old_status['time'].split ':'
|
297
|
+
elapsed = t[0].to_i
|
298
|
+
total = t[1].to_i
|
299
|
+
for cb in @callbacks[TIME_CALLBACK]
|
300
|
+
cb.call elapsed, total
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
if old_status['volume'] != status['volume']
|
305
|
+
for cb in @callbacks[VOLUME_CALLBACK]
|
306
|
+
cb.call status['volume'].to_i
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
if old_status['repeat'] != status['repeat']
|
311
|
+
for cb in @callbacks[REPEAT_CALLBACK]
|
312
|
+
cb.call(status['repeat'] == '1')
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
if old_status['random'] != status['random']
|
317
|
+
for cb in @callbacks[RANDOM_CALLBACK]
|
318
|
+
cb.call(status['random'] == '1')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
if old_status['playlist'] != status['playlist']
|
323
|
+
for cb in @callbacks[PLAYLIST_CALLBACK]
|
324
|
+
cb.call status['playlist'].to_i
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
if old_status['playlistlength'] != status['playlistlength']
|
329
|
+
for cb in @callbacks[PLAYLIST_LENGTH_CALLBACK]
|
330
|
+
cb.call status['playlistlength'].to_i
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
if old_status['xfade'] != status['xfade']
|
335
|
+
for cb in @callbacks[CROSSFADE_CALLBACK]
|
336
|
+
cb.call status['xfade'].to_i
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
if old_status['state'] != status['state']
|
341
|
+
state = (status['state'].nil? ? '' : status['state'])
|
342
|
+
for cb in @callbacks[STATE_CALLBACK]
|
343
|
+
cb.call state
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
begin
|
348
|
+
s = mpd.current_song
|
349
|
+
rescue
|
350
|
+
s = nil
|
351
|
+
end
|
352
|
+
|
353
|
+
if song != s
|
354
|
+
song = s
|
355
|
+
for cb in @callbacks[CURRENT_SONG_CALLBACK]
|
356
|
+
cb.call song
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
if old_status['songid'] != status['songid']
|
361
|
+
for cb in @callbacks[CURRENT_SONGID_CALLBACK]
|
362
|
+
cb.call status['songid'].to_i
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
if old_status['bitrate'] != status['bitrate']
|
367
|
+
for cb in @callbacks[BITRATE_CALLBACK]
|
368
|
+
cb.call status['bitrate'].to_i
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
if old_status['audio'] != status['audio']
|
373
|
+
audio = (status['audio'].nil? ? '0:0:0' : status['audio'])
|
374
|
+
a = audio.split ':'
|
375
|
+
samp = a[0].to_i
|
376
|
+
bits = a[1].to_i
|
377
|
+
chans = a[2].to_i
|
378
|
+
for cb in @callbacks[AUDIO_CALLBACK]
|
379
|
+
cb.call samp, bits, chans
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
old_status = status
|
384
|
+
sleep 0.1
|
385
|
+
|
386
|
+
if !connected
|
387
|
+
sleep 2
|
388
|
+
begin
|
389
|
+
mpd.connect unless @stop_cb_thread
|
390
|
+
rescue
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
}
|
395
|
+
end
|
396
|
+
|
397
|
+
return ret
|
398
|
+
end
|
399
|
+
|
400
|
+
#
|
401
|
+
# Check if the client is connected
|
402
|
+
#
|
403
|
+
# This will return true only if the server responds
|
404
|
+
# otherwise false is returned
|
405
|
+
def connected?
|
406
|
+
return false if @socket.nil?
|
407
|
+
begin
|
408
|
+
ret = send_command 'ping'
|
409
|
+
rescue
|
410
|
+
ret = false
|
411
|
+
end
|
412
|
+
|
413
|
+
return ret
|
414
|
+
end
|
415
|
+
|
416
|
+
#
|
417
|
+
# Disconnect from the server. This has no effect
|
418
|
+
# if the client is not connected. Reconnect using
|
419
|
+
# the connect method. This will also stop the
|
420
|
+
# callback thread, thus disabling callbacks
|
421
|
+
def disconnect
|
422
|
+
@stop_cb_thread = true
|
423
|
+
|
424
|
+
return if @socket.nil?
|
425
|
+
|
426
|
+
@socket.puts 'close'
|
427
|
+
@socket.close
|
428
|
+
@socket = nil
|
429
|
+
end
|
430
|
+
|
431
|
+
#
|
432
|
+
# Add the file _path_ to the playlist. If path is a
|
433
|
+
# directory, it will be added recursively.
|
434
|
+
#
|
435
|
+
# Returns true if this was successful,
|
436
|
+
# Raises a RuntimeError if the command failed
|
437
|
+
def add( path )
|
438
|
+
send_command "add \"#{path}\""
|
439
|
+
end
|
440
|
+
|
441
|
+
#
|
442
|
+
# Clears the current playlist
|
443
|
+
#
|
444
|
+
# Returns true if this was successful,
|
445
|
+
# Raises a RuntimeError if the command failed
|
446
|
+
def clear
|
447
|
+
send_command 'clear'
|
448
|
+
end
|
449
|
+
|
450
|
+
#
|
451
|
+
# Clears the current error message reported in status
|
452
|
+
# ( This is also accomplished by any command that starts playback )
|
453
|
+
#
|
454
|
+
# Returns true if this was successful,
|
455
|
+
# Raises a RuntimeError if the command failed
|
456
|
+
def clearerror
|
457
|
+
send_command 'clearerror'
|
458
|
+
end
|
459
|
+
|
460
|
+
#
|
461
|
+
# Set the crossfade between songs in seconds
|
462
|
+
#
|
463
|
+
# Raises a RuntimeError if the command failed
|
464
|
+
def crossfade=( seconds )
|
465
|
+
send_command "crossfade #{seconds}"
|
466
|
+
end
|
467
|
+
|
468
|
+
#
|
469
|
+
# Read the crossfade between songs in seconds,
|
470
|
+
# Raises a RuntimeError if the command failed
|
471
|
+
def crossfade
|
472
|
+
status = self.status
|
473
|
+
return if status.nil?
|
474
|
+
return status['xfade'].to_i
|
475
|
+
end
|
476
|
+
|
477
|
+
#
|
478
|
+
# Read the currently playing song
|
479
|
+
#
|
480
|
+
# Returns a Song object with the current song's data,
|
481
|
+
# Raises a RuntimeError if the command failed
|
482
|
+
def current_song
|
483
|
+
build_song( send_command('currentsong') )
|
484
|
+
end
|
485
|
+
|
486
|
+
#
|
487
|
+
# Delete the song from the playlist, where pos
|
488
|
+
# is the song's position in the playlist
|
489
|
+
#
|
490
|
+
# Returns true if successful,
|
491
|
+
# Raises a RuntimeError if the command failed
|
492
|
+
def delete( pos )
|
493
|
+
send_command "delete #{pos}"
|
494
|
+
end
|
495
|
+
|
496
|
+
#
|
497
|
+
# Delete the song with the songid from the playlist
|
498
|
+
#
|
499
|
+
# Returns true if successful,
|
500
|
+
# Raises a RuntimeError if the command failed
|
501
|
+
def deleteid( songid )
|
502
|
+
send_command "deleteid #{songid}"
|
503
|
+
end
|
504
|
+
|
505
|
+
#
|
506
|
+
# Finds songs in the database that are EXACTLY
|
507
|
+
# matched by the what argument. type should be
|
508
|
+
# 'album', 'artist', or 'title'
|
509
|
+
#
|
510
|
+
# This returns an Array of MPD::Songs,
|
511
|
+
# Raises a RuntimeError if the command failed
|
512
|
+
def find( type, what )
|
513
|
+
response = send_command "find \"#{type}\" \"#{what}\""
|
514
|
+
build_songs_list response
|
515
|
+
end
|
516
|
+
|
517
|
+
#
|
518
|
+
# Kills MPD
|
519
|
+
#
|
520
|
+
# Returns true if successful.
|
521
|
+
# Raises a RuntimeError if the command failed
|
522
|
+
def kill
|
523
|
+
send_command 'kill'
|
524
|
+
end
|
525
|
+
|
526
|
+
#
|
527
|
+
# Lists all of the albums in the database
|
528
|
+
# The optional argument is for specifying an
|
529
|
+
# artist to list the albums for
|
530
|
+
#
|
531
|
+
# Returns an Array of Album names (Strings),
|
532
|
+
# Raises a RuntimeError if the command failed
|
533
|
+
def albums( artist = nil )
|
534
|
+
list 'album', artist
|
535
|
+
end
|
536
|
+
|
537
|
+
#
|
538
|
+
# Lists all of the artists in the database
|
539
|
+
#
|
540
|
+
# Returns an Array of Artist names (Strings),
|
541
|
+
# Raises a RuntimeError if the command failed
|
542
|
+
def artists
|
543
|
+
list 'artist'
|
544
|
+
end
|
545
|
+
|
546
|
+
#
|
547
|
+
# This is used by the albums and artists methods
|
548
|
+
# type should be 'album' or 'artist'. If type is 'album'
|
549
|
+
# then arg can be a specific artist to list the albums for
|
550
|
+
#
|
551
|
+
# Returns an Array of Strings,
|
552
|
+
# Raises a RuntimeError if the command failed
|
553
|
+
def list( type, arg = nil )
|
554
|
+
if not arg.nil?
|
555
|
+
response = send_command "list #{type} \"#{arg}\""
|
556
|
+
else
|
557
|
+
response = send_command "list #{type}"
|
558
|
+
end
|
559
|
+
|
560
|
+
list = []
|
561
|
+
if not response.nil? and response.kind_of? String
|
562
|
+
lines = response.split "\n"
|
563
|
+
re = Regexp.new "\\A#{type}: ", 'i'
|
564
|
+
for line in lines
|
565
|
+
list << line.gsub( re, '' )
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
return list
|
570
|
+
end
|
571
|
+
|
572
|
+
#
|
573
|
+
# List all of the directories in the database, starting at path.
|
574
|
+
# If path isn't specified, the root of the database is used
|
575
|
+
#
|
576
|
+
# Returns an Array of directory names (Strings),
|
577
|
+
# Raises a RuntimeError if the command failed
|
578
|
+
def directories( path = nil )
|
579
|
+
if not path.nil?
|
580
|
+
response = send_command "listall \"#{path}\""
|
581
|
+
else
|
582
|
+
response = send_command 'listall'
|
583
|
+
end
|
584
|
+
|
585
|
+
filter_response response, /\Adirectory: /i
|
586
|
+
end
|
587
|
+
|
588
|
+
#
|
589
|
+
# List all of the files in the database, starting at path.
|
590
|
+
# If path isn't specified, the root of the database is used
|
591
|
+
#
|
592
|
+
# Returns an Array of file names (Strings).
|
593
|
+
# Raises a RuntimeError if the command failed
|
594
|
+
def files( path = nil )
|
595
|
+
if not path.nil?
|
596
|
+
response = send_command "listall \"#{path}\""
|
597
|
+
else
|
598
|
+
response = send_command 'listall'
|
599
|
+
end
|
600
|
+
|
601
|
+
filter_response response, /\Afile: /i
|
602
|
+
end
|
603
|
+
|
604
|
+
#
|
605
|
+
# List all of the playlists in the database
|
606
|
+
#
|
607
|
+
# Returns an Array of playlist names (Strings)
|
608
|
+
def playlists
|
609
|
+
response = send_command 'lsinfo'
|
610
|
+
|
611
|
+
filter_response response, /\Aplaylist: /i
|
612
|
+
end
|
613
|
+
|
614
|
+
#
|
615
|
+
# List all of the songs in the database starting at path.
|
616
|
+
# If path isn't specified, the root of the database is used
|
617
|
+
#
|
618
|
+
# Returns an Array of MPD::Songs,
|
619
|
+
# Raises a RuntimeError if the command failed
|
620
|
+
def songs( path = nil )
|
621
|
+
if not path.nil?
|
622
|
+
response = send_command "listallinfo \"#{path}\""
|
623
|
+
else
|
624
|
+
response = send_command 'listallinfo'
|
625
|
+
end
|
626
|
+
|
627
|
+
build_songs_list response
|
628
|
+
end
|
629
|
+
|
630
|
+
#
|
631
|
+
# List all of the songs by an artist
|
632
|
+
#
|
633
|
+
# Returns an Array of MPD::Songs by the artist `artist`,
|
634
|
+
# Raises a RuntimeError if the command failed
|
635
|
+
def songs_by_artist( artist )
|
636
|
+
all_songs = self.songs
|
637
|
+
artist_songs = []
|
638
|
+
all_songs.each do |song|
|
639
|
+
if song.artist == artist
|
640
|
+
artist_songs << song
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
644
|
+
return artist_songs
|
645
|
+
end
|
646
|
+
|
647
|
+
#
|
648
|
+
# Loads the playlist name.m3u (do not pass the m3u extension
|
649
|
+
# when calling) from the playlist directory. Use `playlists`
|
650
|
+
# to what playlists are available
|
651
|
+
#
|
652
|
+
# Returns true if successful,
|
653
|
+
# Raises a RuntimeError if the command failed
|
654
|
+
def load( name )
|
655
|
+
send_command "load \"#{name}\""
|
656
|
+
end
|
657
|
+
|
658
|
+
#
|
659
|
+
# Move the song at `from` to `to` in the playlist
|
660
|
+
#
|
661
|
+
# Returns true if successful,
|
662
|
+
# Raises a RuntimeError if the command failed
|
663
|
+
def move( from, to )
|
664
|
+
send_command "move #{from} #{to}"
|
665
|
+
end
|
666
|
+
|
667
|
+
#
|
668
|
+
# Move the song with the `songid` to `to` in the playlist
|
669
|
+
#
|
670
|
+
# Returns true if successful,
|
671
|
+
# Raises a RuntimeError if the command failed
|
672
|
+
def moveid( songid, to )
|
673
|
+
send_command "moveid #{songid} #{to}"
|
674
|
+
end
|
675
|
+
|
676
|
+
#
|
677
|
+
# Plays the next song in the playlist
|
678
|
+
#
|
679
|
+
# Returns true if successful,
|
680
|
+
# Raises a RuntimeError if the command failed
|
681
|
+
def next
|
682
|
+
send_command 'next'
|
683
|
+
end
|
684
|
+
|
685
|
+
#
|
686
|
+
# Set / Unset paused playback
|
687
|
+
#
|
688
|
+
# Returns true if successful,
|
689
|
+
# Raises a RuntimeError if the command failed
|
690
|
+
def pause=( toggle )
|
691
|
+
send_command 'pause ' + (toggle ? '1' : '0')
|
692
|
+
end
|
693
|
+
|
694
|
+
#
|
695
|
+
# Returns true if MPD is paused,
|
696
|
+
# Raises a RuntimeError if the command failed
|
697
|
+
def paused?
|
698
|
+
status = self.status
|
699
|
+
return false if status.nil?
|
700
|
+
return status['state'] == 'pause'
|
701
|
+
end
|
702
|
+
|
703
|
+
#
|
704
|
+
# This is used for authentication with the server
|
705
|
+
# `pass` is simply the plaintext password
|
706
|
+
#
|
707
|
+
# Raises a RuntimeError if the command failed
|
708
|
+
def password( pass )
|
709
|
+
send_command "password \"#{pass}\""
|
710
|
+
end
|
711
|
+
|
712
|
+
#
|
713
|
+
# Ping the server
|
714
|
+
#
|
715
|
+
# Returns true if successful,
|
716
|
+
# Raises a RuntimeError if the command failed
|
717
|
+
def ping
|
718
|
+
send_command 'ping'
|
719
|
+
end
|
720
|
+
|
721
|
+
#
|
722
|
+
# Begin playing the playist. Optionally
|
723
|
+
# specify the pos to start on
|
724
|
+
#
|
725
|
+
# Returns true if successful,
|
726
|
+
# Raises a RuntimeError if the command failed
|
727
|
+
def play( pos = nil )
|
728
|
+
if pos.nil?
|
729
|
+
return send_command('play')
|
730
|
+
else
|
731
|
+
return send_command("play #{pos}")
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
#
|
736
|
+
# Returns true if the server's state is set to 'play',
|
737
|
+
# Raises a RuntimeError if the command failed
|
738
|
+
def playing?
|
739
|
+
state = self.status['state']
|
740
|
+
return state == 'play'
|
741
|
+
end
|
742
|
+
|
743
|
+
#
|
744
|
+
# Begin playing the playlist. Optionally
|
745
|
+
# specify the songid to start on
|
746
|
+
#
|
747
|
+
# Returns true if successful,
|
748
|
+
# Raises a RuntimeError if the command failed
|
749
|
+
def playid( songid = nil )
|
750
|
+
if not songid.nil?
|
751
|
+
return(send_command("playid #{songid}"))
|
752
|
+
else
|
753
|
+
return(send_command('playid'))
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
#
|
758
|
+
# Returns the current playlist version number,
|
759
|
+
# Raises a RuntimeError if the command failed
|
760
|
+
def playlist_version
|
761
|
+
self.status['playlist'].to_i
|
762
|
+
end
|
763
|
+
|
764
|
+
#
|
765
|
+
# List the current playlist
|
766
|
+
# This is the same as playlistinfo w/o args
|
767
|
+
#
|
768
|
+
# Returns an Array of MPD::Songs,
|
769
|
+
# Raises a RuntimeError if the command failed
|
770
|
+
def playlist
|
771
|
+
response = send_command 'playlistinfo'
|
772
|
+
build_songs_list response
|
773
|
+
end
|
774
|
+
|
775
|
+
#
|
776
|
+
# Returns the MPD::Song at the position `pos` in the playlist,
|
777
|
+
# Raises a RuntimeError if the command failed
|
778
|
+
def song_at_pos( pos )
|
779
|
+
build_song( send_command("playlistinfo #{pos}") )
|
780
|
+
end
|
781
|
+
|
782
|
+
#
|
783
|
+
# Returns the MPD::Song with the `songid` in the playlist,
|
784
|
+
# Raises a RuntimeError if the command failed
|
785
|
+
def song_with_id( songid )
|
786
|
+
build_song( send_command("playlistid #{songid}") )
|
787
|
+
end
|
788
|
+
|
789
|
+
#
|
790
|
+
# List the changes since the specified version in the playlist
|
791
|
+
#
|
792
|
+
# Returns an Array of MPD::Songs,
|
793
|
+
# Raises a RuntimeError if the command failed
|
794
|
+
def playlist_changes( version )
|
795
|
+
response = send_command "plchanges #{version}"
|
796
|
+
build_songs_list response
|
797
|
+
end
|
798
|
+
|
799
|
+
#
|
800
|
+
# Plays the previous song in the playlist
|
801
|
+
#
|
802
|
+
# Returns true if successful,
|
803
|
+
# Raises a RuntimeError if the command failed
|
804
|
+
def previous
|
805
|
+
send_command 'previous'
|
806
|
+
end
|
807
|
+
|
808
|
+
#
|
809
|
+
# Enable / Disable random playback,
|
810
|
+
# Raises a RuntimeError if the command failed
|
811
|
+
def random=( toggle )
|
812
|
+
send_command 'random ' + (toggle ? '1' : '0')
|
813
|
+
end
|
814
|
+
|
815
|
+
#
|
816
|
+
# Returns true if random playback is currently enabled,
|
817
|
+
# Raises a RuntimeError if the command failed
|
818
|
+
def random?
|
819
|
+
rand = self.status['random']
|
820
|
+
return rand == '1'
|
821
|
+
end
|
822
|
+
|
823
|
+
#
|
824
|
+
# Enable / Disable repeat,
|
825
|
+
# Raises a RuntimeError if the command failed
|
826
|
+
def repeat=( toggle )
|
827
|
+
send_command 'repeat ' + (toggle ? '1' : '0')
|
828
|
+
end
|
829
|
+
|
830
|
+
#
|
831
|
+
# Returns true if repeat is enabled,
|
832
|
+
# Raises a RuntimeError if the command failed
|
833
|
+
def repeat?
|
834
|
+
repeat = self.status['repeat']
|
835
|
+
return repeat == '1'
|
836
|
+
end
|
837
|
+
|
838
|
+
#
|
839
|
+
# Removes (PERMANENTLY!) the playlist `playlist.m3u` from
|
840
|
+
# the playlist directory
|
841
|
+
#
|
842
|
+
# Returns true if successful,
|
843
|
+
# Raises a RuntimeError if the command failed
|
844
|
+
def rm( playlist )
|
845
|
+
send_command "rm \"#{playlist}\""
|
846
|
+
end
|
847
|
+
|
848
|
+
#
|
849
|
+
# An Alias for rm
|
850
|
+
def remove_playlist( playlist )
|
851
|
+
rm playlist
|
852
|
+
end
|
853
|
+
|
854
|
+
#
|
855
|
+
# Saves the current playlist to `playlist`.m3u in the
|
856
|
+
# playlist directory
|
857
|
+
#
|
858
|
+
# Returns true if successful,
|
859
|
+
# Raises a RuntimeError if the command failed
|
860
|
+
def save( playlist )
|
861
|
+
send_command "save \"#{playlist}\""
|
862
|
+
end
|
863
|
+
|
864
|
+
#
|
865
|
+
# Searches for any song that contains `what` in the `type` field
|
866
|
+
# `type` can be 'title', 'artist', 'album' or 'filename'
|
867
|
+
# Searches are NOT case sensitive
|
868
|
+
#
|
869
|
+
# Returns an Array of MPD::Songs,
|
870
|
+
# Raises a RuntimeError if the command failed
|
871
|
+
def search( type, what )
|
872
|
+
build_songs_list( send_command("search #{type} \"#{what}\"") )
|
873
|
+
end
|
874
|
+
|
875
|
+
#
|
876
|
+
# Seeks to the position `time` (in seconds) of the
|
877
|
+
# song at `pos` in the playlist
|
878
|
+
#
|
879
|
+
# Returns true if successful,
|
880
|
+
# Raises a RuntimeError if the command failed
|
881
|
+
def seek( pos, time )
|
882
|
+
send_command "seek #{pos} #{time}"
|
883
|
+
end
|
884
|
+
|
885
|
+
#
|
886
|
+
# Seeks to the position `time` (in seconds) of the song with
|
887
|
+
# the id `songid`
|
888
|
+
#
|
889
|
+
# Returns true if successful,
|
890
|
+
# Raises a RuntimeError if the command failed
|
891
|
+
def seekid( songid, time )
|
892
|
+
send_command "seekid #{songid} #{time}"
|
893
|
+
end
|
894
|
+
|
895
|
+
#
|
896
|
+
# Set the volume
|
897
|
+
# The argument `vol` will automatically be bounded to 0 - 100
|
898
|
+
#
|
899
|
+
# Raises a RuntimeError if the command failed
|
900
|
+
def volume=( vol )
|
901
|
+
send_command "setvol #{vol}"
|
902
|
+
end
|
903
|
+
|
904
|
+
#
|
905
|
+
# Returns the volume,
|
906
|
+
# Raises a RuntimeError if the command failed
|
907
|
+
def volume
|
908
|
+
status = self.status
|
909
|
+
return if status.nil?
|
910
|
+
return status['volume'].to_i
|
911
|
+
end
|
912
|
+
|
913
|
+
#
|
914
|
+
# Shuffles the playlist,
|
915
|
+
# Raises a RuntimeError if the command failed
|
916
|
+
def shuffle
|
917
|
+
send_command 'shuffle'
|
918
|
+
end
|
919
|
+
|
920
|
+
#
|
921
|
+
# Returns a Hash of MPD's stats,
|
922
|
+
# Raises a RuntimeError if the command failed
|
923
|
+
def stats
|
924
|
+
response = send_command 'stats'
|
925
|
+
build_hash response
|
926
|
+
end
|
927
|
+
|
928
|
+
#
|
929
|
+
# Returns a Hash of the current status,
|
930
|
+
# Raises a RuntimeError if the command failed
|
931
|
+
def status
|
932
|
+
response = send_command 'status'
|
933
|
+
build_hash response
|
934
|
+
end
|
935
|
+
|
936
|
+
#
|
937
|
+
# Stop playing
|
938
|
+
#
|
939
|
+
# Returns true if successful,
|
940
|
+
# Raises a RuntimeError if the command failed
|
941
|
+
def stop
|
942
|
+
send_command 'stop'
|
943
|
+
end
|
944
|
+
|
945
|
+
#
|
946
|
+
# Returns true if the server's state is 'stop',
|
947
|
+
# Raises a RuntimeError if the command failed
|
948
|
+
def stopped?
|
949
|
+
status = self.status
|
950
|
+
return false if status.nil?
|
951
|
+
return status['state'] == 'stop'
|
952
|
+
end
|
953
|
+
|
954
|
+
#
|
955
|
+
# Swaps the song at position `posA` with the song
|
956
|
+
# as position `posB` in the playlist
|
957
|
+
#
|
958
|
+
# Returns true if successful,
|
959
|
+
# Raises a RuntimeError if the command failed
|
960
|
+
def swap( posA, posB )
|
961
|
+
send_command "swap #{posA} #{posB}"
|
962
|
+
end
|
963
|
+
|
964
|
+
#
|
965
|
+
# Swaps the song with the id `songidA` with the song
|
966
|
+
# with the id `songidB`
|
967
|
+
#
|
968
|
+
# Returns true if successful,
|
969
|
+
# Raises a RuntimeError if the command failed
|
970
|
+
def swapid( songidA, songidB )
|
971
|
+
send_command "swapid #{songidA} #{songidB}"
|
972
|
+
end
|
973
|
+
|
974
|
+
#
|
975
|
+
# Tell the server to update the database. Optionally,
|
976
|
+
# specify the path to update
|
977
|
+
def update( path = nil )
|
978
|
+
ret = ''
|
979
|
+
if not path.nil?
|
980
|
+
ret = send_command("update \"#{path}\"")
|
981
|
+
else
|
982
|
+
ret = send_command('update')
|
983
|
+
end
|
984
|
+
|
985
|
+
return(ret.gsub('updating_db: ', '').to_i)
|
986
|
+
end
|
987
|
+
|
988
|
+
#
|
989
|
+
# Private Method
|
990
|
+
#
|
991
|
+
# Used to send a command to the server. This synchronizes
|
992
|
+
# on a mutex to be thread safe
|
993
|
+
#
|
994
|
+
# Returns the server response as processed by `handle_server_response`,
|
995
|
+
# Raises a RuntimeError if the command failed
|
996
|
+
def send_command( command )
|
997
|
+
if @socket.nil?
|
998
|
+
raise "MPD: Not Connected to the Server"
|
999
|
+
end
|
1000
|
+
|
1001
|
+
ret = nil
|
1002
|
+
|
1003
|
+
@mutex.synchronize do
|
1004
|
+
begin
|
1005
|
+
@socket.puts command
|
1006
|
+
ret = handle_server_response
|
1007
|
+
rescue Errno::EPIPE
|
1008
|
+
@socket = nil
|
1009
|
+
raise 'MPD Error: Broken Pipe (Disconnected)'
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
return ret
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
#
|
1017
|
+
# Private Method
|
1018
|
+
#
|
1019
|
+
# Handles the server's response (called inside send_command)
|
1020
|
+
#
|
1021
|
+
# This will repeatedly read the server's response from the socket
|
1022
|
+
# and will process the output. If a string is returned by the server
|
1023
|
+
# that is what is returned. If just an "OK" is returned, this returns
|
1024
|
+
# true. If an "ACK" is returned, this raises an error
|
1025
|
+
def handle_server_response
|
1026
|
+
return if @socket.nil?
|
1027
|
+
|
1028
|
+
msg = ''
|
1029
|
+
reading = true
|
1030
|
+
error = nil
|
1031
|
+
while reading
|
1032
|
+
line = @socket.gets
|
1033
|
+
case line
|
1034
|
+
when "OK\n"
|
1035
|
+
reading = false
|
1036
|
+
when /^ACK/
|
1037
|
+
error = line
|
1038
|
+
reading = false
|
1039
|
+
when nil
|
1040
|
+
reading = false
|
1041
|
+
else
|
1042
|
+
msg += line
|
1043
|
+
end
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
if error.nil?
|
1047
|
+
return true if msg.empty?
|
1048
|
+
return msg
|
1049
|
+
else
|
1050
|
+
raise error.gsub( /^ACK \[(\d+)\@(\d+)\] \{(.+)\} (.+)$/, 'MPD Error #\1: \3: \4')
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
#
|
1055
|
+
# Private Method
|
1056
|
+
#
|
1057
|
+
# This builds a hash out of lines returned from the server.
|
1058
|
+
# First the response is turned into an array of lines
|
1059
|
+
# then each entry is parsed so that the line is viewed as
|
1060
|
+
# "key: value"
|
1061
|
+
#
|
1062
|
+
# The end result is a hash containing the proper key/value pairs
|
1063
|
+
def build_hash( string )
|
1064
|
+
return {} if string.nil? or !string.kind_of? String
|
1065
|
+
|
1066
|
+
hash = {}
|
1067
|
+
lines = string.split "\n"
|
1068
|
+
lines.each do |line|
|
1069
|
+
hash[ line.gsub(/:.*/, '').downcase ] = line.gsub(/\A[^:]*: /, '')
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
return hash
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
#
|
1076
|
+
# Private Method
|
1077
|
+
#
|
1078
|
+
# This is similar to build_hash, but instead of building a Hash,
|
1079
|
+
# a MPD::Song is built
|
1080
|
+
def build_song( string )
|
1081
|
+
return if string.nil? or !string.kind_of? String
|
1082
|
+
|
1083
|
+
song = Song.new
|
1084
|
+
lines = string.split "\n"
|
1085
|
+
lines.each do |line|
|
1086
|
+
song[ line.gsub(/:.*/, '').downcase ] = line.gsub(/\A[^:]*: /, '')
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
return song
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
#
|
1093
|
+
# Private Method
|
1094
|
+
#
|
1095
|
+
# This first creates an array of lines as returned from the server
|
1096
|
+
# Then each entry is processed and added to an MPD::Song
|
1097
|
+
# Whenever a new 'file:' entry is found, the current MPD::Song
|
1098
|
+
# is added to an array, and a new one is created
|
1099
|
+
#
|
1100
|
+
# The end result is an Array of MPD::Songs
|
1101
|
+
def build_songs_list( string )
|
1102
|
+
return [] if string.nil? or !string.kind_of? String
|
1103
|
+
|
1104
|
+
list = []
|
1105
|
+
song = Song.new
|
1106
|
+
lines = string.split "\n"
|
1107
|
+
lines.each do |line|
|
1108
|
+
key = line.gsub(/:.*/, '')
|
1109
|
+
line.gsub!(/\A[^:]*: /, '')
|
1110
|
+
|
1111
|
+
if key == 'file' && !song.file.nil?
|
1112
|
+
list << song
|
1113
|
+
song = Song.new
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
song[key.downcase] = line
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
list << song
|
1120
|
+
|
1121
|
+
return list
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
#
|
1125
|
+
# Private Method
|
1126
|
+
#
|
1127
|
+
# This filters each line from the server to return
|
1128
|
+
# only those matching the regexp. The regexp is removed
|
1129
|
+
# from the line before it is added to an Array
|
1130
|
+
#
|
1131
|
+
# This is used in the `directories`, `files`, etc methods
|
1132
|
+
# to return only the directory/file names
|
1133
|
+
def filter_response( string, regexp )
|
1134
|
+
list = []
|
1135
|
+
lines = string.split "\n"
|
1136
|
+
lines.each do |line|
|
1137
|
+
if line =~ regexp
|
1138
|
+
list << line.gsub(regexp, '')
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
return list
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
private :send_command
|
1146
|
+
private :handle_server_response
|
1147
|
+
private :build_hash
|
1148
|
+
private :build_song
|
1149
|
+
private :build_songs_list
|
1150
|
+
private :filter_response
|
1151
|
+
|
1152
|
+
end
|