lyrics-ebook 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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