lyrics-ebook 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/LICENSE +340 -0
  2. data/README +46 -0
  3. data/bin/lyrics-ebook +47 -0
  4. data/lib/eeepub/LICENSE +20 -0
  5. data/lib/eeepub/README.md +77 -0
  6. data/lib/eeepub/Rakefile +49 -0
  7. data/lib/eeepub/VERSION +1 -0
  8. data/lib/eeepub/examples/files/bar.html +42 -0
  9. data/lib/eeepub/examples/files/foo.html +42 -0
  10. data/lib/eeepub/examples/simple_epub.rb +25 -0
  11. data/lib/eeepub/lib/eeepub.rb +17 -0
  12. data/lib/eeepub/lib/eeepub/container_item.rb +108 -0
  13. data/lib/eeepub/lib/eeepub/easy.rb +100 -0
  14. data/lib/eeepub/lib/eeepub/maker.rb +127 -0
  15. data/lib/eeepub/lib/eeepub/ncx.rb +68 -0
  16. data/lib/eeepub/lib/eeepub/ocf.rb +112 -0
  17. data/lib/eeepub/lib/eeepub/opf.rb +148 -0
  18. data/lib/eeepub/spec/eeepub/easy_spec.rb +66 -0
  19. data/lib/eeepub/spec/eeepub/maker_spec.rb +124 -0
  20. data/lib/eeepub/spec/eeepub/ncx_spec.rb +80 -0
  21. data/lib/eeepub/spec/eeepub/ocf_spec.rb +43 -0
  22. data/lib/eeepub/spec/eeepub/opf_spec.rb +262 -0
  23. data/lib/eeepub/spec/eeepub_spec.rb +4 -0
  24. data/lib/eeepub/spec/spec_helper.rb +12 -0
  25. data/lib/lyrics_ebook.rb +71 -0
  26. data/lib/lyrics_ebook/cache2html.rb +33 -0
  27. data/lib/lyrics_ebook/cache_manager.rb +77 -0
  28. data/lib/lyrics_ebook/lyric.rb +32 -0
  29. data/lib/lyrics_ebook/lyric_download.rb +50 -0
  30. data/lib/lyrics_ebook/lyrics2html.rb +106 -0
  31. data/lib/lyrics_ebook/mini_lyrics_parser.rb +26 -0
  32. data/lib/lyrics_ebook/mp3_parser.rb +37 -0
  33. data/lib/wiki_lyrics/amarok/COPYING +340 -0
  34. data/lib/wiki_lyrics/amarok/README +51 -0
  35. data/lib/wiki_lyrics/amarok/amarok.rb +355 -0
  36. data/lib/wiki_lyrics/amarok/pluginadapter.rb +113 -0
  37. data/lib/wiki_lyrics/amarok/plugins.rb +95 -0
  38. data/lib/wiki_lyrics/amarok/wikilyrics.rb +578 -0
  39. data/lib/wiki_lyrics/amarok/wikilyrics.spec +4 -0
  40. data/lib/wiki_lyrics/amarok/wikipluginadapter.rb +392 -0
  41. data/lib/wiki_lyrics/cli/optionsparser.rb +228 -0
  42. data/lib/wiki_lyrics/cli/pluginadapter.rb +56 -0
  43. data/lib/wiki_lyrics/cli/plugins.rb +79 -0
  44. data/lib/wiki_lyrics/cli/wikilyrics.rb +178 -0
  45. data/lib/wiki_lyrics/cli/wikipluginadapter.rb +140 -0
  46. data/lib/wiki_lyrics/docs/COPYING +340 -0
  47. data/lib/wiki_lyrics/docs/ChangeLog +293 -0
  48. data/lib/wiki_lyrics/docs/HOWTO.pdf +0 -0
  49. data/lib/wiki_lyrics/docs/HOWTO.tex +151 -0
  50. data/lib/wiki_lyrics/docs/HOWTO.txt +139 -0
  51. data/lib/wiki_lyrics/docs/README +45 -0
  52. data/lib/wiki_lyrics/docs/TODO +12 -0
  53. data/lib/wiki_lyrics/gui/gui-gtk.rb +945 -0
  54. data/lib/wiki_lyrics/gui/gui-qt3.rb +785 -0
  55. data/lib/wiki_lyrics/gui/gui-qt4.rb +845 -0
  56. data/lib/wiki_lyrics/gui/gui-tk.rb +1104 -0
  57. data/lib/wiki_lyrics/gui/gui.rb +268 -0
  58. data/lib/wiki_lyrics/gui/test.rb +74 -0
  59. data/lib/wiki_lyrics/i18n/README +1 -0
  60. data/lib/wiki_lyrics/i18n/en.rb +181 -0
  61. data/lib/wiki_lyrics/i18n/es.rb +181 -0
  62. data/lib/wiki_lyrics/i18n/i18n.rb +126 -0
  63. data/lib/wiki_lyrics/i18n/sk.rb +174 -0
  64. data/lib/wiki_lyrics/itrans/COPYRIGHT +31 -0
  65. data/lib/wiki_lyrics/itrans/itrans +0 -0
  66. data/lib/wiki_lyrics/itrans/itrans.txt +8 -0
  67. data/lib/wiki_lyrics/itrans/lyric.txt +23 -0
  68. data/lib/wiki_lyrics/itrans/udvng.ifm +206 -0
  69. data/lib/wiki_lyrics/lyrics.rb +567 -0
  70. data/lib/wiki_lyrics/lyrics_AZLyrics.rb +113 -0
  71. data/lib/wiki_lyrics/lyrics_DarkLyrics.rb +124 -0
  72. data/lib/wiki_lyrics/lyrics_Giitaayan.rb +124 -0
  73. data/lib/wiki_lyrics/lyrics_Jamendo.rb +166 -0
  74. data/lib/wiki_lyrics/lyrics_LeosLyrics.rb +142 -0
  75. data/lib/wiki_lyrics/lyrics_LoudSongs.rb +135 -0
  76. data/lib/wiki_lyrics/lyrics_LyricWiki.rb +329 -0
  77. data/lib/wiki_lyrics/lyrics_LyricsDownload.rb +118 -0
  78. data/lib/wiki_lyrics/lyrics_LyricsMania.rb +141 -0
  79. data/lib/wiki_lyrics/lyrics_Lyriki.rb +287 -0
  80. data/lib/wiki_lyrics/lyrics_SeekLyrics.rb +108 -0
  81. data/lib/wiki_lyrics/lyrics_Sing365.rb +103 -0
  82. data/lib/wiki_lyrics/lyrics_TerraLetras.rb +126 -0
  83. data/lib/wiki_lyrics/mediawikilyrics.rb +1464 -0
  84. data/lib/wiki_lyrics/tests/plugin_tests.rb +161 -0
  85. data/lib/wiki_lyrics/tests/tests.rb +74 -0
  86. data/lib/wiki_lyrics/utils/formdata.rb +56 -0
  87. data/lib/wiki_lyrics/utils/htmlentities.rb +291 -0
  88. data/lib/wiki_lyrics/utils/http.rb +198 -0
  89. data/lib/wiki_lyrics/utils/itrans.rb +160 -0
  90. data/lib/wiki_lyrics/utils/kde.rb +88 -0
  91. data/lib/wiki_lyrics/utils/logger.rb +123 -0
  92. data/lib/wiki_lyrics/utils/strings.rb +378 -0
  93. data/lib/wiki_lyrics/utils/xmlhash.rb +111 -0
  94. data/lib/wiki_lyrics/wikilyrics.kdevelop +152 -0
  95. data/lib/wiki_lyrics/wikilyrics.kdevses +18 -0
  96. data/lib/wiki_lyrics/win/gtk_fix.bat +3 -0
  97. data/lib/wiki_lyrics/win/tk_fix.bat +3 -0
  98. data/lib/wiki_lyrics/win/wikilyrics.bat +4 -0
  99. data/lib/wiki_lyrics/win/win_fix.rb +52 -0
  100. data/templates/art_.erb.xhtml +29 -0
  101. data/templates/artists.erb.xhtml +19 -0
  102. data/templates/cover.erb.xhtml +22 -0
  103. data/test/cache_manager_test.rb +73 -0
  104. data/test/data/artist name/title name.lyric +6 -0
  105. data/test/lyric_test.rb +41 -0
  106. metadata +194 -0
@@ -0,0 +1,113 @@
1
+ # Copyright (C) 2006-2008 by Sergio Pistone
2
+ # sergio_pistone@yahoo.com.ar
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the
16
+ # Free Software Foundation, Inc.,
17
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
+
19
+ require File.expand_path( File.dirname( __FILE__ ) + "/amarok" )
20
+
21
+ require "cgi"
22
+
23
+ module PluginAdapter
24
+
25
+ # Hack to make module methods become class methods when the module gets included
26
+ def PluginAdapter.included( including )
27
+ if including.is_a?( Class )
28
+ including.extend( ClassMethods ) # adds class methods
29
+ else # if including.is_a?( Module )
30
+ including::ClassMethods.append_class_methods( self )
31
+ end
32
+ end
33
+
34
+ # Methods under this module will became class methods when the module gets included
35
+ # Note: don't use def self.<method name> but just <method name>
36
+ module ClassMethods
37
+ def ClassMethods.append_class_methods( mod )
38
+ include mod::ClassMethods
39
+ end
40
+
41
+ def plugin_name()
42
+ return site_name()
43
+ end
44
+
45
+ def notify( message )
46
+ Amarok.notify( "<b>" + plugin_name() + "</b>: " + message.gsub( "\n", " " ) )
47
+ end
48
+
49
+ def popup( message )
50
+ Amarok.popup( "<b>" + plugin_name() + "</b>:<br/>" + message )
51
+ end
52
+
53
+ def add_custom_menu_item( menu_item )
54
+ Amarok.add_custom_menu_item( plugin_name(), menu_item )
55
+ end
56
+
57
+ def remove_custom_menu_item( menu_item )
58
+ Amarok.remove_custom_menu_item( plugin_name(), menu_item )
59
+ end
60
+
61
+ def add_custom_checkeable_menu_item( menu_item, checked )
62
+ Amarok.add_custom_checkeable_menu_item( plugin_name(), menu_item, checked )
63
+ end
64
+
65
+ def remove_custom_checkeable_menu_item( menu_item )
66
+ Amarok.remove_custom_checkeable_menu_item( plugin_name(), menu_item )
67
+ end
68
+ end
69
+
70
+ def plugin_name()
71
+ return self.class.plugin_name()
72
+ end
73
+
74
+ def notify( message )
75
+ self.class.notify( message )
76
+ end
77
+
78
+ def popup( message )
79
+ self.class.popup( message )
80
+ end
81
+
82
+ def add_custom_menu_item( menu_item )
83
+ self.class.add_custom_menu_item( menu_item )
84
+ end
85
+
86
+ def remove_custom_menu_item( menu_item )
87
+ self.class.remove_custom_menu_item( menu_item )
88
+ end
89
+
90
+ def add_custom_checkeable_menu_item( menu_item, checked )
91
+ self.class.add_custom_checkeable_menu_item( menu_item, checked )
92
+ end
93
+
94
+ def remove_custom_checkeable_menu_item( menu_item )
95
+ self.class.remove_custom_checkeable_menu_item( menu_item )
96
+ end
97
+
98
+ def on_start()
99
+ end
100
+
101
+ def on_quit()
102
+ end
103
+
104
+ def configure()
105
+ end
106
+
107
+ def read_config( config_file )
108
+ end
109
+
110
+ def write_config( config_file )
111
+ end
112
+
113
+ end
@@ -0,0 +1,95 @@
1
+ # Copyright (C) 2007 by Sergio Pistone
2
+ # sergio_pistone@yahoo.com.ar
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the
16
+ # Free Software Foundation, Inc.,
17
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
+
19
+ require File.expand_path( File.dirname( __FILE__ ) + "/../mediawikilyrics" )
20
+ require File.expand_path( File.dirname( __FILE__ ) + "/pluginadapter" )
21
+ require File.expand_path( File.dirname( __FILE__ ) + "/wikipluginadapter" )
22
+
23
+ module Plugins
24
+
25
+ @@ALL = []
26
+ @@WIKI = []
27
+
28
+ # Load lyrics plugins:
29
+
30
+ plugins_dir = Dir.new( File.expand_path( File.dirname( __FILE__ ) + "/.." ) )
31
+ plugins_dir.each() do |file_name|
32
+ file_name = File.expand_path( File.dirname( __FILE__ ) + "/../" + file_name )
33
+ next if ! File.file?( file_name ) || (md = /\/lyrics_([A-Za-z_0-9]*)\.rb$/.match( file_name )) == nil
34
+ require( file_name )
35
+ class_name = md[1]
36
+ class_obj = eval( class_name )
37
+ if class_obj.ancestors().include?( MediaWikiLyrics )
38
+ eval( "class Amarok#{class_name} < #{class_name}\ninclude WikiPluginAdapter\nend" )
39
+ plugin = eval( "Amarok#{class_name}.new()" )
40
+ @@WIKI << plugin
41
+ else
42
+ eval( "class Amarok#{class_name} < #{class_name}\ninclude PluginAdapter\nend" )
43
+ plugin = eval( "Amarok#{class_name}.new()" )
44
+ end
45
+ @@ALL << plugin
46
+ end
47
+
48
+ @@ALL.sort!() { |x,y| x.site_name <=> y.site_name }
49
+ @@WIKI.sort!() { |x,y| y.site_name() <=> x.site_name() }
50
+ @@USED = @@WIKI + (@@ALL - @@WIKI)
51
+
52
+ def Plugins.all_plugins()
53
+ return @@ALL
54
+ end
55
+
56
+ def Plugins.all_names()
57
+ return @@ALL.collect(){ |plugin| plugin.plugin_name() }
58
+ end
59
+
60
+ def Plugins.used_plugins()
61
+ return @@USED
62
+ end
63
+
64
+ def Plugins.used_names()
65
+ return @@USED.collect(){ |plugin| plugin.plugin_name() }
66
+ end
67
+
68
+ def Plugins.used_names=( names )
69
+ @@USED = []
70
+ names.uniq().each() do |name|
71
+ @@ALL.each do |plugin|
72
+ if plugin.plugin_name() == name
73
+ @@USED << plugin
74
+ break
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def Plugins.wiki_plugins()
81
+ return @@WIKI
82
+ end
83
+
84
+ def Plugins.wiki_names()
85
+ return @@WIKI.collect(){ |plugin| plugin.plugin_name() }
86
+ end
87
+
88
+ def Plugins.plugin_by_name( name )
89
+ @@ALL.each() do |plugin|
90
+ return plugin if plugin.plugin_name() == name
91
+ end
92
+ return nil
93
+ end
94
+
95
+ end
@@ -0,0 +1,578 @@
1
+ #!/usr/bin/env ruby
2
+ $VERBOSE = nil
3
+
4
+ # Copyright (C) 2006-2008 by Sergio Pistone
5
+ # sergio_pistone@yahoo.com.ar
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the
19
+ # Free Software Foundation, Inc.,
20
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
+
22
+ require File.expand_path( File.dirname( __FILE__ ) + "/../utils/strings" )
23
+ require File.expand_path( File.dirname( __FILE__ ) + "/../utils/xmlhash" )
24
+ require File.expand_path( File.dirname( __FILE__ ) + "/../utils/logger" )
25
+ require File.expand_path( File.dirname( __FILE__ ) + "/../utils/kde" )
26
+ require File.expand_path( File.dirname( __FILE__ ) + "/../i18n/i18n" )
27
+ require File.expand_path( File.dirname( __FILE__ ) + "/../gui/gui" )
28
+ require File.expand_path( File.dirname( __FILE__ ) + "/../lyrics" )
29
+ require File.expand_path( File.dirname( __FILE__ ) + "/amarok" )
30
+ require File.expand_path( File.dirname( __FILE__ ) + "/plugins" )
31
+
32
+ require "cgi"
33
+ require "thread"
34
+
35
+ $TOOLKIT_PRIORITY = ["qt3", "qt4", "gtk", "tk"]
36
+ $LOG_FILEPATH = "#{ENV["HOME"]}/.wikilyrics.log"
37
+ $LOG_TRUNCATE = 2000
38
+
39
+ def debug( message )
40
+ Amarok.popup( message.to_s() )
41
+ end
42
+
43
+ class Application
44
+
45
+ @@MENU_ENTRY_SEARCH_CURRENT = I18n.get( "amarok.application.search.current" )
46
+ @@MENU_ENTRY_SEARCH_SELECTED = I18n.get( "amarok.application.search.selected" )
47
+ @@MENU_ENTRY_CLEAR_LYRICS_CACHE = I18n.get( "amarok.application.clearlyricscache" )
48
+
49
+ @@SCRIPT_NAME = "Wiki-Lyrics"
50
+ @@CONFIG_FILE = "wikilyrics.xml"
51
+
52
+ def initialize()
53
+ self.logger = nil
54
+ self.cleanup_lyrics = true
55
+ self.single_threaded = false
56
+ end
57
+
58
+ def finalize()
59
+ @logger.finalize() if @logger
60
+ end
61
+
62
+ def log?()
63
+ return @logger != nil
64
+ end
65
+
66
+ def logger=( logger )
67
+ @logger = logger
68
+ Plugins.all_plugins().each() { |plugin| plugin.logger = @logger }
69
+ end
70
+
71
+ def log( message, new_lines=1 )
72
+ @logger.log( message, new_lines ) if @logger
73
+ end
74
+
75
+ def cleanup_lyrics?()
76
+ return @cleanup_lyrics
77
+ end
78
+
79
+ def cleanup_lyrics=( cleanup_lyrics )
80
+ @cleanup_lyrics = cleanup_lyrics
81
+ Plugins.all_plugins().each() { |plugin| plugin.cleanup_lyrics = cleanup_lyrics }
82
+ end
83
+
84
+ def single_threaded?()
85
+ return @single_threaded
86
+ end
87
+
88
+ def single_threaded=( single_threaded )
89
+ @single_threaded = single_threaded
90
+ end
91
+
92
+ def notify( message )
93
+ Amarok.notify( "<b>[" + @@SCRIPT_NAME + "]</b> " + message.gsub( "\n", " " ) )
94
+ end
95
+
96
+ def popup( message )
97
+ Amarok.popup( "<b>[" + @@SCRIPT_NAME + "]</b><br/>" + message )
98
+ end
99
+
100
+ def add_custom_menu_item( menu_item )
101
+ Amarok.add_custom_menu_item( @@SCRIPT_NAME, menu_item )
102
+ end
103
+
104
+ def remove_custom_menu_item( menu_item )
105
+ Amarok.remove_custom_menu_item( @@SCRIPT_NAME, menu_item )
106
+ end
107
+
108
+ def read_config()
109
+ values = {
110
+ "cleanup_lyrics" => cleanup_lyrics?(),
111
+ "single_threaded" => single_threaded?(),
112
+ "write_log" => log?,
113
+ "used_plugins" => Plugins.used_names()
114
+ }
115
+ XMLHash.read( @@CONFIG_FILE, values )
116
+ self.logger = values["write_log"].to_s() == "true" ? Logger.new( $LOG_FILEPATH, $LOG_TRUNCATE ) : nil
117
+ self.cleanup_lyrics = values["cleanup_lyrics"].to_s() == "true"
118
+ self.single_threaded = values["single_threaded"].to_s() == "true"
119
+ Plugins.used_names = values["used_plugins"]
120
+ Plugins.all_plugins().each() { |plugin| plugin.read_config( @@CONFIG_FILE ) }
121
+ end
122
+
123
+ def write_config()
124
+ values = {
125
+ "cleanup_lyrics" => cleanup_lyrics?(),
126
+ "single_threaded" => single_threaded?(),
127
+ "write_log" => log?,
128
+ "used_plugins" => Plugins.used_names()
129
+ }
130
+ XMLHash.write( @@CONFIG_FILE, values )
131
+ Plugins.all_plugins().each() { |plugin| plugin.write_config( @@CONFIG_FILE ) }
132
+ end
133
+
134
+
135
+ # EVENT HANDLERS
136
+
137
+ def on_start()
138
+ add_custom_menu_item( @@MENU_ENTRY_SEARCH_CURRENT )
139
+ add_custom_menu_item( @@MENU_ENTRY_SEARCH_SELECTED )
140
+ add_custom_menu_item( @@MENU_ENTRY_CLEAR_LYRICS_CACHE )
141
+ Plugins.all_plugins().each() { |plugin| plugin.on_start() }
142
+ end
143
+
144
+ def on_quit()
145
+ remove_custom_menu_item( @@MENU_ENTRY_SEARCH_CURRENT )
146
+ remove_custom_menu_item( @@MENU_ENTRY_SEARCH_SELECTED )
147
+ remove_custom_menu_item( @@MENU_ENTRY_CLEAR_LYRICS_CACHE )
148
+ Plugins.all_plugins().each() { |plugin| plugin.on_quit() }
149
+ end
150
+
151
+ def on_configure()
152
+
153
+ values = {
154
+ "cleanup_lyrics" => cleanup_lyrics?(),
155
+ "single_threaded" => single_threaded?(),
156
+ "write_log" => log?,
157
+ "used_plugins" => Plugins.used_names(),
158
+ "script_name" => @@SCRIPT_NAME,
159
+ "unused_plugins" => Plugins.all_names() - Plugins.used_names()
160
+ }
161
+ if GUI.show_plugins_manager_dialog( values )
162
+ self.logger = values["write_log"].to_s() == "true" ? Logger.new( $LOG_FILEPATH, $LOG_TRUNCATE ) : nil
163
+ self.cleanup_lyrics = values["cleanup_lyrics"].to_s() == "true"
164
+ self.single_threaded = values["single_threaded"].to_s() == "true"
165
+ Plugins.used_names = values["used_plugins"]
166
+ end
167
+ Plugins.all_plugins().each() { |plugin| plugin.configure() }
168
+
169
+ write_config()
170
+
171
+ end
172
+
173
+
174
+ def on_search_current_lyrics()
175
+ if Amarok.playing?()
176
+ song_data = Amarok.query_song_data( Amarok.get_current_path() )
177
+ if GUI.show_search_lyrics_dialog( song_data )
178
+ on_fetch_lyrics( Lyrics::Request.new(
179
+ song_data["artist"],
180
+ song_data["title"],
181
+ song_data["album"],
182
+ song_data["year"]
183
+ ) )
184
+ end
185
+ else
186
+ notify( I18n.get( "amarok.application.search.current.nosongplaying" ) )
187
+ end
188
+ end
189
+
190
+ def on_search_file_lyrics( file )
191
+ if (song_data = Amarok.query_song_data( file )).empty?
192
+ notify( I18n.get( "amarok.application.search.noinfofound" ) )
193
+ elsif GUI.show_search_lyrics_dialog( song_data )
194
+ request = Lyrics::Request.new( song_data["artist"], song_data["title"], song_data["album"], song_data["year"] )
195
+ response, plugin, suggestions = fetch_lyrics( request )
196
+ if response.lyrics
197
+ dialog_data = {
198
+ "artist" => response.artist,
199
+ "title" => response.title,
200
+ "lyrics" => response.lyrics,
201
+ "site_name" => plugin.site_name(),
202
+ }
203
+ GUI.show_lyrics_dialog( dialog_data )
204
+ else
205
+ notify( I18n.get( "amarok.application.search.nolyricsfound", song_data["title"], song_data["artist"] ) )
206
+ end
207
+ end
208
+ end
209
+
210
+ def on_custom_menu_item_selected( menu, item, urls )
211
+ if menu == @@SCRIPT_NAME
212
+ if item == @@MENU_ENTRY_SEARCH_CURRENT
213
+ on_search_current_lyrics()
214
+ elsif item == @@MENU_ENTRY_SEARCH_SELECTED
215
+ url = URI.parse( urls[0] )
216
+ return if url.scheme != "file"
217
+ on_search_file_lyrics( URI.decode( url.path ) )
218
+ elsif item == @@MENU_ENTRY_CLEAR_LYRICS_CACHE
219
+ if GUI.show_confirmation_dialog( I18n.get( "amarok.application.clearlyricscache.confirm" ), @@SCRIPT_NAME )
220
+ Amarok.query( "DELETE FROM lyrics WHERE url <> ''" )
221
+ notify( I18n.get( "amarok.application.clearlyricscache.done" ) )
222
+ end
223
+ end
224
+ elsif plugin = Plugins.plugin_by_name( menu )
225
+ plugin.on_custom_menu_item_selected( menu, item, urls )
226
+ end
227
+ end
228
+
229
+ def build_lyrics_data_hash( response, plugin=nil )
230
+ return {
231
+ "artist" => response.artist ? response.artist : response.request.artist,
232
+ "title" => response.title ? response.title : response.request.title,
233
+ "lyrics" => response.lyrics,
234
+ "url" => response.url,
235
+ "add_url" => plugin ? plugin.build_song_add_url( response.request ) : nil,
236
+ "site_name" => plugin ? plugin.site_name : nil,
237
+ "site_host" => plugin ? plugin.site_host : nil,
238
+ }
239
+ end
240
+ protected :build_lyrics_data_hash
241
+
242
+ def wikis_process_response( response, response_plugin, searched_wiki_plugins )
243
+ Plugins.wiki_plugins().each() do |plugin|
244
+ plugin.wiki_process_response( response, response_plugin, searched_wiki_plugins.include?( plugin ) )
245
+ end
246
+ end
247
+
248
+ def fetch_lyrics( request )
249
+
250
+ used_plugins = Plugins.used_plugins()
251
+
252
+ response = nil
253
+ response_plugin = nil
254
+ suggestions = []
255
+
256
+ if used_plugins.empty?
257
+ popup( I18n.get( "amarok.application.error.nopluginsselected" ) )
258
+ response = Lyrics::Response.new( request )
259
+ else
260
+ searched_wiki_plugins = []
261
+ used_plugins.each() do |plugin|
262
+ begin
263
+ searched_wiki_plugins << plugin if plugin.is_a?( MediaWikiLyrics )
264
+ response = plugin.lyrics_direct_search( request )
265
+ if response.lyrics
266
+ response_plugin = plugin
267
+ break
268
+ end
269
+ response, suggs = plugin.lyrics_from_suggestions( request )
270
+ if suggs.size > 0
271
+ suggestions << plugin.plugin_name()
272
+ suggestions.concat( suggs )
273
+ end
274
+ if response.lyrics
275
+ response_plugin = plugin
276
+ break
277
+ end
278
+ rescue TimeoutError
279
+ notify( I18n.get( "amarok.application.search.plugintimeout", plugin.plugin_name(), plugin.site_host() ) )
280
+ end
281
+ end
282
+ wikis_process_response( response, response_plugin, searched_wiki_plugins )
283
+ end
284
+
285
+ return response, response_plugin, suggestions
286
+
287
+ end
288
+
289
+ def on_fetch_lyrics( request )
290
+
291
+ # NOTE: we must check that the current song doesn't change while we're fetching
292
+ # the lyrics, otherwise Amarok will associate the lyrics with the wrong song
293
+ prev_artist = Strings.normalize!( Amarok.get_current_artist().to_s() )
294
+ prev_title = Strings.normalize!( Amarok.get_current_title().to_s() )
295
+
296
+ response, plugin, suggestions = fetch_lyrics( request )
297
+ lyrics_data = build_lyrics_data_hash( response, plugin )
298
+
299
+ return if Strings.normalize!( Amarok.get_current_artist().to_s() ) != prev_artist ||
300
+ Strings.normalize!( Amarok.get_current_title().to_s() ) != prev_title
301
+
302
+ if lyrics_data["lyrics"]
303
+ Amarok.show_lyrics(
304
+ lyrics_data["artist"],
305
+ lyrics_data["title"],
306
+ lyrics_data["lyrics"],
307
+ lyrics_data["site_name"],
308
+ lyrics_data["site_host"],
309
+ lyrics_data["url"],
310
+ lyrics_data["add_url"]
311
+ )
312
+ elsif suggestions.size > 0
313
+ Amarok.show_suggestions(
314
+ lyrics_data["artist"],
315
+ lyrics_data["title"],
316
+ suggestions,
317
+ lyrics_data["add_url"]
318
+ )
319
+ else
320
+ Amarok.show_not_found(
321
+ lyrics_data["artist"],
322
+ lyrics_data["title"],
323
+ lyrics_data["add_url"]
324
+ )
325
+ end
326
+
327
+ end
328
+
329
+ def fetch_lyrics_from_url( request, url )
330
+
331
+ used_plugins = Plugins.used_plugins()
332
+
333
+ response = nil
334
+ response_plugin = nil
335
+
336
+ if used_plugins.empty?
337
+ popup( I18n.get( "amarok.application.error.nopluginsselected" ) )
338
+ response = Lyrics::Response.new( request )
339
+ else
340
+ searched_wiki_plugins = []
341
+ used_plugins.each() do |plugin|
342
+ begin
343
+ next if ! plugin.known_url?( url )
344
+ searched_wiki_plugins << plugin if plugin.is_a?( MediaWikiLyrics )
345
+ response = plugin.lyrics_from_url( request, url )
346
+ if response.lyrics
347
+ response_plugin = plugin
348
+ break
349
+ end
350
+ rescue TimeoutError
351
+ notify( I18n.get( "amarok.application.search.plugintimeout", plugin.plugin_name(), plugin.site_host() ) )
352
+ end
353
+ end
354
+ wikis_process_response( response, response_plugin, searched_wiki_plugins )
355
+ end
356
+
357
+ return response, response_plugin
358
+ end
359
+
360
+ def on_fetch_lyrics_from_url( request, url )
361
+
362
+ # NOTE: we must check that the current song doesn't change while we're fetching
363
+ # the lyrics, otherwise Amarok will associate the lyrics with the wrong song
364
+ prev_artist = Strings.normalize!( Amarok.get_current_artist().to_s() )
365
+ prev_title = Strings.normalize!( Amarok.get_current_title().to_s() )
366
+
367
+ response, plugin = fetch_lyrics_from_url( request, url )
368
+ lyrics_data = build_lyrics_data_hash( response, plugin )
369
+
370
+ return if Strings.normalize!( Amarok.get_current_artist().to_s() ) != prev_artist ||
371
+ Strings.normalize!( Amarok.get_current_title().to_s() ) != prev_title
372
+
373
+ if lyrics_data["lyrics"]
374
+ Amarok.show_lyrics(
375
+ lyrics_data["artist"],
376
+ lyrics_data["title"],
377
+ lyrics_data["lyrics"],
378
+ lyrics_data["site_name"],
379
+ lyrics_data["site_host"],
380
+ lyrics_data["url"],
381
+ lyrics_data["add_url"]
382
+ )
383
+ else
384
+ Amarok.show_not_found(
385
+ lyrics_data["artist"],
386
+ lyrics_data["title"],
387
+ lyrics_data["add_url"]
388
+ )
389
+ end
390
+
391
+ end
392
+
393
+ def exec()
394
+
395
+ @threads = []
396
+ @messages = []
397
+
398
+ read_config()
399
+
400
+ begin
401
+
402
+ on_start()
403
+
404
+ single_threaded = self.single_threaded?
405
+
406
+ GUI.set_run_out_of_process( ! single_threaded )
407
+
408
+ exec_messages_loop( single_threaded )
409
+
410
+ rescue Errno::ECONNREFUSED => e
411
+ popup( I18n.get( "amarok.application.error.connectionrefused" ) )
412
+ popup( e.backtrace().join( "<br/>" ) )
413
+
414
+ rescue SocketError, TimeoutError => e
415
+ popup( I18n.get( "amarok.application.error.connectiondown" ) )
416
+
417
+ rescue Exception => e # unexpected error
418
+ if ! e.is_a?( SystemExit ) || e.status != 0
419
+ $stderr << e.message() << "\n"
420
+ $stderr << e.backtrace().join( "\n" )
421
+ log( e.message() )
422
+ log( e.backtrace().join( "\n" ), 0 )
423
+ exit( 1 )
424
+ end
425
+
426
+ ensure
427
+ write_config()
428
+ on_quit()
429
+
430
+ end
431
+
432
+ end
433
+
434
+ def exec_messages_loop( single_threaded )
435
+
436
+ loop do
437
+ message = get_message()
438
+ break if message == nil
439
+ command = /[A-Za-z]*/.match( message ).to_s()
440
+ case command
441
+ when "configure"
442
+ on_configure()
443
+ when "fetchLyrics"
444
+ artist = Amarok.get_current_artist()
445
+ title = Amarok.get_current_title()
446
+ if artist && title
447
+ if single_threaded
448
+ on_fetch_lyrics( Lyrics::Request.new(
449
+ Strings.cleanup_artist( artist, title ),
450
+ Strings.cleanup_title( title ),
451
+ Amarok.get_current_album(),
452
+ Amarok.get_current_year()
453
+ ) )
454
+ else
455
+ @threads << Thread.new() do
456
+ on_fetch_lyrics( Lyrics::Request.new(
457
+ Strings.cleanup_artist( artist, title ),
458
+ Strings.cleanup_title( title ),
459
+ Amarok.get_current_album(),
460
+ Amarok.get_current_year()
461
+ ) )
462
+ end
463
+ end
464
+ else
465
+ Amarok.show_error()
466
+ end
467
+ when "fetchLyricsByUrl"
468
+ args = message.split()
469
+ if args.length < 2
470
+ Amarok.show_error()
471
+ else
472
+ artist = Amarok.get_current_artist().to_s()
473
+ title = Amarok.get_current_title().to_s()
474
+ if single_threaded
475
+ on_fetch_lyrics_from_url(
476
+ Lyrics::Request.new(
477
+ Strings.cleanup_artist( artist, title ),
478
+ Strings.cleanup_title( title ),
479
+ Amarok.get_current_album(),
480
+ Amarok.get_current_year()
481
+ ),
482
+ args[1]
483
+ )
484
+ else
485
+ @threads << Thread.new() do
486
+ on_fetch_lyrics_from_url(
487
+ Lyrics::Request.new(
488
+ Strings.cleanup_artist( artist, title ),
489
+ Strings.cleanup_title( title ),
490
+ Amarok.get_current_album(),
491
+ Amarok.get_current_year()
492
+ ),
493
+ args[1]
494
+ )
495
+ end
496
+ end
497
+ end
498
+ when "customMenuClicked"
499
+ urls_start = message.index( /([a-zA-Z]+:\/\/[^: ]+){1,}/ )
500
+ next if urls_start == nil # unexpected message
501
+ menu_id = message.slice( "customMenuClicked:".size+1..urls_start-2 )
502
+ menu, item = Amarok.get_custom_menu_item( menu_id )
503
+ if menu && item
504
+ urls = message.slice( urls_start..-1 ).split( " " )
505
+ if single_threaded
506
+ on_custom_menu_item_selected( menu, item, urls )
507
+ else
508
+ @threads << Thread.new() { on_custom_menu_item_selected( menu, item, urls ) }
509
+ end
510
+ end
511
+ end
512
+ end
513
+ end
514
+ protected :exec_messages_loop
515
+
516
+ def get_message()
517
+ begin
518
+ loop do
519
+ begin
520
+ messages = $stdin.read_nonblock( 2048 ) # we read all there is to read
521
+ messages.chomp().split( "\n" ).each() { |message| store_message( message ) }
522
+ rescue Errno::EAGAIN # there was nothing to read
523
+ rescue EOFError
524
+ return nil # stdin was closed
525
+ end
526
+ if @messages.empty?
527
+ message = $stdin.gets() # wait for something to be written to stdin
528
+ store_message( message )
529
+ begin
530
+ messages = $stdin.read_nonblock( 2048 ) # if something else was written, we read it too
531
+ messages.chomp().split( "\n" ).each() { |message| store_message( message ) }
532
+ rescue Errno::EAGAIN # there was nothing to read
533
+ rescue EOFError
534
+ return nil # stdin was closed
535
+ end
536
+ end
537
+ return @messages.shift() if ! @messages.empty?
538
+ end
539
+ rescue SignalException => e
540
+ return nil
541
+ end
542
+
543
+ end
544
+ protected :get_message
545
+
546
+ def store_message( message )
547
+ command = /[A-Za-z]*/.match( message ).to_s()
548
+ if command == "configure"
549
+ # prioritize configure messages:
550
+ @messages.insert( 0, "configure" ) if @messages[0] != "configure"
551
+ elsif ["fetchLyrics", "fetchLyricsByUrl", "customMenuClicked"].include?( command )
552
+ # we queue only two messages to try to stay in synch with Amarok current track
553
+ @messages[@messages.size < 2 ? @messages.size : 1 ] = message
554
+ end
555
+ end
556
+ protected :store_message
557
+
558
+ end
559
+
560
+ if $0 == __FILE__
561
+
562
+ if GUI.set_toolkit_priority( $TOOLKIT_PRIORITY ) == nil
563
+ Amarok.popup( I18n.get( "amarok.application.error.notoolkit" ) )
564
+ exit( 0 )
565
+ end
566
+
567
+ proxy_url, excluded_urls, reverse = KDE.get_proxy_settings()
568
+ HTTP.set_proxy_settings( proxy_url, excluded_urls, reverse )
569
+
570
+ app = Application.new()
571
+
572
+ begin
573
+ app.exec()
574
+ ensure
575
+ app.finalize()
576
+ end
577
+
578
+ end