lyrics 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +17 -0
  5. data/Rakefile +45 -0
  6. data/VERSION +1 -0
  7. data/bin/lyrics +66 -0
  8. data/lib/lyrics.rb +4 -0
  9. data/lib/lyrics/cli/application.rb +99 -0
  10. data/lib/lyrics/cli/optionsparser.rb +228 -0
  11. data/lib/lyrics/cli/pluginadapter.rb +56 -0
  12. data/lib/lyrics/cli/plugins.rb +79 -0
  13. data/lib/lyrics/cli/wikipluginadapter.rb +139 -0
  14. data/lib/lyrics/i18n/README +1 -0
  15. data/lib/lyrics/i18n/en.rb +181 -0
  16. data/lib/lyrics/i18n/es.rb +181 -0
  17. data/lib/lyrics/i18n/i18n.rb +126 -0
  18. data/lib/lyrics/i18n/sk.rb +174 -0
  19. data/lib/lyrics/itrans/COPYRIGHT +31 -0
  20. data/lib/lyrics/itrans/itrans +0 -0
  21. data/lib/lyrics/itrans/itrans.txt +8 -0
  22. data/lib/lyrics/itrans/lyric.txt +23 -0
  23. data/lib/lyrics/itrans/udvng.ifm +206 -0
  24. data/lib/lyrics/lyrics.rb +567 -0
  25. data/lib/lyrics/lyrics_AZLyrics.rb +113 -0
  26. data/lib/lyrics/lyrics_DarkLyrics.rb +124 -0
  27. data/lib/lyrics/lyrics_Giitaayan.rb +124 -0
  28. data/lib/lyrics/lyrics_Jamendo.rb +166 -0
  29. data/lib/lyrics/lyrics_LeosLyrics.rb +142 -0
  30. data/lib/lyrics/lyrics_LoudSongs.rb +135 -0
  31. data/lib/lyrics/lyrics_LyricWiki.rb +328 -0
  32. data/lib/lyrics/lyrics_LyricsDownload.rb +118 -0
  33. data/lib/lyrics/lyrics_LyricsMania.rb +141 -0
  34. data/lib/lyrics/lyrics_Lyriki.rb +286 -0
  35. data/lib/lyrics/lyrics_SeekLyrics.rb +108 -0
  36. data/lib/lyrics/lyrics_Sing365.rb +103 -0
  37. data/lib/lyrics/lyrics_TerraLetras.rb +126 -0
  38. data/lib/lyrics/mediawikilyrics.rb +1417 -0
  39. data/lib/lyrics/utils/formdata.rb +56 -0
  40. data/lib/lyrics/utils/htmlentities.rb +291 -0
  41. data/lib/lyrics/utils/http.rb +198 -0
  42. data/lib/lyrics/utils/itrans.rb +160 -0
  43. data/lib/lyrics/utils/logger.rb +123 -0
  44. data/lib/lyrics/utils/strings.rb +378 -0
  45. data/lib/lyrics/utils/xmlhash.rb +111 -0
  46. data/lyrics.gemspec +98 -0
  47. data/spec/lyrics_spec.rb +7 -0
  48. data/spec/spec.opts +1 -0
  49. data/spec/spec_helper.rb +9 -0
  50. metadata +137 -0
@@ -0,0 +1,108 @@
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
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
20
+
21
+ require "utils/strings"
22
+ require "lyrics"
23
+
24
+ class SeekLyrics < Lyrics
25
+
26
+ @@white_chars = "'\"¿?¡!()[].,;:-/& "
27
+
28
+ def SeekLyrics.site_host()
29
+ return "www.seeklyrics.com"
30
+ end
31
+
32
+ def SeekLyrics.site_name()
33
+ return "Seek Lyrics"
34
+ end
35
+
36
+ def SeekLyrics.lyrics_test_data()
37
+ return [
38
+ Request.new( "Nirvana", "Smells Like Teen Spirit", "Nevermind" ),
39
+ Request.new( "Radiohead", "Optimistic", "Kid A" ),
40
+ Request.new( "Massive Attack", "Protection", "Protection" ),
41
+ Request.new( "Portishead", "Wandering Star", "Dummy" ),
42
+ ]
43
+ end
44
+
45
+ def SeekLyrics.build_song_add_url( request )
46
+ return "http://#{site_host()}/submit.php"
47
+ end
48
+
49
+ def cleanup_token( token )
50
+ token = token.tr( @@white_chars, " " )
51
+ token.strip!()
52
+ token.tr!( " ", "-" )
53
+ return token
54
+ end
55
+
56
+ def build_lyrics_fetch_data( request )
57
+ artist = cleanup_token( request.artist.gsub( /^the /i, "" ) )
58
+ title = cleanup_token( request.title )
59
+ return FetchPageData.new( "http://#{site_host()}/lyrics/#{artist}/#{title}.html", nil, { "Cookie"=>"pass=deleted" } )
60
+ end
61
+
62
+ def parse_lyrics( response, page_body )
63
+
64
+ page_body = Strings.latin12utf8( page_body )
65
+ page_body.tr_s!( " \n\r\t", " " )
66
+ page_body.tr_s!( "’", "'" )
67
+
68
+ if (md = /<b><h2>([^<-]+) - ([^<]+) Lyrics<\/h2><\/b>/i.match( page_body ))
69
+ response.artist, response.title = md[1].strip(), md[2].strip()
70
+ end
71
+
72
+ return if ! page_body.sub!( /^(.*)<a href="http:\/\/www\.ringtonematcher\.com.*$/i, "\\1" )
73
+ return if ! page_body.sub!( /^.*<img src="\/images\/phone-right\.gif" [^<]+><\/a>/i, "" )
74
+
75
+ page_body.gsub!( /\ ?<br ?\/?> ?/i, "\n" )
76
+ page_body.strip!()
77
+
78
+ response.lyrics = page_body
79
+
80
+ end
81
+
82
+ def build_suggestions_fetch_data( request )
83
+ artist = cleanup_token( request.artist.gsub( /^the /i, "" ) )
84
+ return FetchPageData.new( "http://#{site_host()}/lyrics/#{artist}/showall.html", nil, { "Cookie"=>"pass=deleted" } )
85
+ end
86
+
87
+ def parse_suggestions( request, page_body, page_url )
88
+
89
+ page_body = Strings.latin12utf8( page_body )
90
+ page_body.tr_s!( " \n\r\t", " " )
91
+ page_body.tr_s!( "’", "'" )
92
+
93
+ suggestions = []
94
+
95
+ return suggestions if ! page_body.gsub!( /.*<tr><td width="50%"><\/td><td width="50%"><\/td><\/tr>/, "" )
96
+ return suggestions if ! page_body.gsub!( /<\/table>.*$/, "" )
97
+
98
+ page_body.split( /<td>&nbsp;-&nbsp;&nbsp;/i ).each() do |entry|
99
+ if (md = /<a href="([^"]+)"[^>]*>([^<]+)<\/a><\/td>/i.match( entry ))
100
+ suggestions << Suggestion.new( request.artist, md[2], "http://#{site_host()}#{md[1]}" )
101
+ end
102
+ end
103
+
104
+ return suggestions
105
+
106
+ end
107
+
108
+ end
@@ -0,0 +1,103 @@
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
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
20
+
21
+ require "lyrics"
22
+
23
+ class Sing365 < Lyrics
24
+
25
+ def Sing365.site_host()
26
+ return "www.sing365.com"
27
+ end
28
+
29
+ def Sing365.site_name()
30
+ return "Sing365"
31
+ end
32
+
33
+ def Sing365.lyrics_test_data()
34
+ return [
35
+ Request.new( "Nirvana", "Smells Like Teen Spirit", "Nevermind" ),
36
+ Request.new( "The Cranberries", "Linger", "Everybody Else Is Doing It, So Why Can't We?" ),
37
+ Request.new( "Pearl Jam", "Porch", "Ten" ),
38
+ Request.new( "The Smashing Pumpkins", "Mayonaise", "Siamese Dream" ),
39
+ ]
40
+ end
41
+
42
+ def Sing365.build_song_add_url( request )
43
+ return build_google_feeling_lucky_url( request.artist )
44
+ end
45
+
46
+ def build_lyrics_fetch_data( request )
47
+ return FetchPageData.new( build_google_feeling_lucky_url( request.artist, request.title ) )
48
+ end
49
+
50
+ def lyrics_page_valid?( request, page_body, page_url )
51
+ md = /<title>([^<]+) - ([^<]+) LYRICS<\/title>/i.match( page_body )
52
+ return md ?
53
+ Strings.normalize( request.artist ) == Strings.normalize( md[1] ) &&
54
+ Strings.normalize( request.title ) == Strings.normalize( md[2] ) :
55
+ false
56
+ end
57
+
58
+ def parse_lyrics( response, page_body )
59
+
60
+ page_body.tr_s!( " \n\r\t", " " )
61
+
62
+ if (md = /<meta name="Description" content="([^"]+) lyrics performed by ([^"]+)">/i.match( page_body ))
63
+ response.artist, response.title = md[1], md[2]
64
+ end
65
+
66
+ return if ! (md = /<img src="?http:\/\/#{site_host()}\/images\/phone2\.gif"? border="?0"?><br><br><\/div>(.*)<div align="?center"?><br><br><img src="?http:\/\/#{site_host()}\/images\/phone\.gif"?/i.match( page_body ))
67
+
68
+ page_body = md[1]
69
+ page_body.gsub!( /\ ?<br ?\/?> ?/i, "\n" )
70
+ page_body.gsub!( /\n{3,}/, "\n\n" )
71
+
72
+ response.lyrics = page_body
73
+
74
+ end
75
+
76
+ def build_suggestions_fetch_data( request )
77
+ return FetchPageData.new( build_google_feeling_lucky_url( request.artist ) )
78
+ end
79
+
80
+ def suggestions_page_valid?( request, page_body, page_url )
81
+ md = /<title>([^<]+) LYRICS<\/title>/i.match( page_body )
82
+ return md ? Strings.normalize( request.artist ) == Strings.normalize( md[1] ) : false
83
+ end
84
+
85
+ def parse_suggestions( request, page_body, page_url )
86
+
87
+ page_body.tr_s!( " \n\r\t", " " )
88
+
89
+ suggestions = []
90
+
91
+ return suggestions if ! (md = /<img src="?http:\/\/#{site_host()}\/images\/phone2\.gif"? border="?0"?><br><br><\/div>(.*)<\/lu><br><div align="?center"?><br><img src="?http:\/\/#{site_host()}\/images\/phone\.gif"?/i.match( page_body ))
92
+
93
+ md[1].split( "<li>" ).each() do |entry|
94
+ if (md = /<a href="([^"]+)"[^>]*>([^<]+) Lyrics<\/a>/i.match( entry ))
95
+ suggestions << Suggestion.new( request.artist, md[2], "http://#{site_host()}#{md[1]}" )
96
+ end
97
+ end
98
+
99
+ return suggestions
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,126 @@
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
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
20
+
21
+ require "utils/strings"
22
+ require "utils/htmlentities"
23
+ require "lyrics"
24
+
25
+ require "cgi"
26
+
27
+ class TerraLetras < Lyrics
28
+
29
+ def TerraLetras.site_host()
30
+ return "letras.terra.com.br"
31
+ end
32
+
33
+ def TerraLetras.site_name()
34
+ return "Terra Letras"
35
+ end
36
+
37
+ def TerraLetras.known_url?( url )
38
+ return url.index( "http://#{site_host}" ) == 0 || /^http:\/\/.*\.letras\.terra\.com\.br\/letras\/[0-9]+/.match( url )
39
+ end
40
+
41
+ def TerraLetras.lyrics_test_data()
42
+ return [
43
+ Request.new( "The Cranberries", "Linger", "Everybody Else Is Doing It, So Why Can't We?" ),
44
+ Request.new( "Radiohead", "Optimistic", "Kid A" ),
45
+ Request.new( "Mark Lanegan", "One Way Street", "Field Songs" ),
46
+ Request.new( "U2", "One", "Achtung Baby" ),
47
+ ]
48
+ end
49
+
50
+ def TerraLetras.build_song_add_url( request )
51
+ return "http://#{site_host()}/envie_artista.php"
52
+ end
53
+
54
+ def TerraLetras.build_google_feeling_lucky_url( artist, title=nil )
55
+ if title
56
+ query = Strings.google_search_quote( "#{title} letra" )
57
+ query << " "
58
+ query << Strings.google_search_quote( artist )
59
+ else
60
+ query = Strings.google_search_quote( "letras de #{artist}" )
61
+ query << " "
62
+ query << Strings.google_search_quote( "#{artist} letras" )
63
+ end
64
+ return Strings.build_google_feeling_lucky_url( query, site_host() )
65
+ end
66
+
67
+ def build_lyrics_fetch_data( request )
68
+ artist = Strings.utf82latin1( request.artist )
69
+ title = Strings.utf82latin1( request.title )
70
+ return FetchPageData.new( "http://#{site_host()}/winamp.php?musica=#{CGI.escape(title)}&artista=#{CGI.escape(artist)}" )
71
+ end
72
+
73
+ def parse_lyrics( response, page_body )
74
+
75
+ page_body = Strings.latin12utf8( page_body )
76
+ page_body.tr_s!( " \n\r\t", " " )
77
+
78
+ if response.url.index( "http://#{site_host}/winamp.php?" ) == 0 # direct fetch
79
+ if (md = /<h1><a href='[^']+' target='_blank'>([^<]+)<\/a><\/h1>/.match( page_body ))
80
+ response.title = md[1]
81
+ end
82
+ if (md = /<h2><a href='[^']+' target='_blank'>([^<]+)<\/a><\/h2>/.match( page_body ))
83
+ response.artist = md[1]
84
+ end
85
+ return if ! page_body.gsub!( /^.*corrigir letra<\/a><\/p><p>/, "" )
86
+ return if ! page_body.gsub!( /<\/p>.*$/, "" )
87
+ else # fetched from suggestions/url
88
+ if (md = /<h2>([^<]+)<\/h2> <h2 id='sz'>([^<]+)<\/h2>/.match( page_body ))
89
+ response.title, response.artist = md[1], md[2]
90
+ end
91
+ return if ! page_body.sub!( /^.*<p id='cmp'>[^<]*<\/p> <p>/, "" )
92
+ return if ! page_body.sub!( /<\/p>.*/, "" )
93
+ end
94
+
95
+ page_body.gsub!( /\ ?<br ?\/?> ?/i, "\n" )
96
+ page_body.gsub!( /\n{3,}/, "\n\n" )
97
+ response.lyrics = page_body
98
+
99
+ end
100
+
101
+ def build_suggestions_fetch_data( request )
102
+ return FetchPageData.new( build_google_feeling_lucky_url( request.artist ) )
103
+ end
104
+
105
+ def parse_suggestions( request, page_body, page_url )
106
+
107
+ page_body = Strings.latin12utf8( page_body )
108
+ page_body.tr_s!( " \n\r\t", " " )
109
+ HTMLEntities.decode!( page_body )
110
+
111
+ suggestions = []
112
+
113
+ return suggestions if ! page_body.gsub!( /^.*<ul class='top' id='bgn'>/, "" )
114
+ return suggestions if ! page_body.gsub!( /<\/ul>.*$/, "" )
115
+
116
+ page_body.split( /<\/li> ?<li>/ ).each do |entry|
117
+ if (md = /<a href="([^"]+)">([^<]+) - ([^<]+)<\/a>/.match( entry ))
118
+ suggestions << Suggestion.new( md[2], md[3], "http://#{site_host()}#{md[1]}" )
119
+ end
120
+ end
121
+
122
+ return suggestions
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,1417 @@
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
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
20
+ require "utils/http"
21
+ require "utils/xmlhash"
22
+ require "utils/formdata"
23
+ require "utils/strings"
24
+ require "utils/htmlentities"
25
+ require "i18n/i18n"
26
+ require "lyrics"
27
+
28
+ require "md5"
29
+ require "cgi"
30
+
31
+ class MediaWikiLyrics < Lyrics
32
+
33
+ class TrackData
34
+
35
+ attr_reader :artist, :title, :year, :length, :genre
36
+
37
+ def initialize( artist, title, length=nil, genre=nil )
38
+ @artist = artist.strip().freeze()
39
+ @title = title.strip().freeze()
40
+ @length = 0
41
+ if length
42
+ tokens = length.to_s().split( ":" ).reverse()
43
+ tokens.size.times() { |idx| @length += (60**idx) * tokens[idx].strip().to_i() }
44
+ end
45
+ @genre = genre ? genre.strip().freeze() : nil
46
+ end
47
+
48
+ end
49
+
50
+ class AlbumData
51
+
52
+ attr_reader :tracks, :artist, :title, :year, :month, :day, :length, :genres
53
+
54
+ # NOTE: the artist is obtained from the tracks data. If it's not
55
+ # the same for all tracks it would be varios_artists_name.
56
+ def initialize( tracks, title, year, month=0, day=0, various_artists_name="(Various Artists)" )
57
+
58
+ raise "tracks, title and year values can't be nil" if ! title || ! year || ! tracks
59
+ raise "tracks value can't be empty" if tracks.empty?
60
+
61
+ @tracks = tracks.clone().freeze()
62
+ @title = title.strip().freeze()
63
+ @year = year.kind_of?( String ) ? year.strip().to_i() : year.to_i()
64
+ @month = month.kind_of?( String ) ? month.strip().to_i() : month.to_i()
65
+ @day = day.kind_of?( String ) ? day.strip().to_i() : day.to_i()
66
+ @various_artists_name = various_artists_name.strip().freeze()
67
+
68
+ @artist = nil
69
+ @genres = []
70
+ genre = nil
71
+ normalized_artist = nil
72
+ @length = 0
73
+ @tracks.each() do |track|
74
+ if @artist == nil
75
+ @artist = track.artist
76
+ normalized_artist = Strings.normalize( @artist )
77
+ elsif normalized_artist != Strings.normalize( track.artist )
78
+ @artist = various_artists_name
79
+ end
80
+ if track.genre && ! @genres.include?( genre = String.capitalize( track.genre, true ) )
81
+ @genres.insert( -1, genre )
82
+ end
83
+ @length += track.length
84
+ end
85
+ @genres.freeze()
86
+
87
+ end
88
+
89
+ def various_artists?()
90
+ return @artist == @various_artists_name
91
+ end
92
+
93
+ end
94
+
95
+ class SongData
96
+
97
+ attr_reader :artist, :title, :lyrics, :album, :year, :credits, :lyricists
98
+
99
+ def initialize( artist, title, lyrics, album=nil, year=nil, credits=nil, lyricists=nil )
100
+
101
+ raise "artist and title values can't be nil" if ! artist || ! title
102
+
103
+ @artist = artist.strip().freeze()
104
+ @title = title.strip().freeze()
105
+ @lyrics = lyrics ? lyrics.strip().freeze() : nil
106
+ @album = album ? album.strip().freeze() : nil
107
+ @year = year.kind_of?( String ) ? year.strip().to_i() : year.to_i()
108
+
109
+ credits = credits.split( ";" ) if credits.is_a?( String )
110
+ @credits = []
111
+ credits.each() { |value| @credits << value.strip().freeze() } if credits.is_a?( Array )
112
+ @credits.freeze()
113
+
114
+ lyricists = lyricists.split( ";" ) if lyricists.is_a?( String )
115
+ @lyricists = []
116
+ lyricists.each() { |value| @lyricists << value.strip().freeze() } if lyricists.is_a?( Array )
117
+ @lyricists.freeze()
118
+
119
+ end
120
+
121
+ def instrumental?()
122
+ return @lyrics == nil
123
+ end
124
+
125
+ end
126
+
127
+
128
+ @@NAME = "WL".freeze()
129
+ @@VERSION = "0.13.4".freeze()
130
+ @@AUTH_INTERVAL = 60*60 # check authorization every hour (it's only really checked if the user tries to submit something)
131
+ @@REDIRECT_FOLLOWUPS = 3
132
+
133
+ @@SEARCH_RESULT_TITLE = "title".freeze()
134
+ @@SEARCH_RESULT_URL = "url".freeze()
135
+
136
+ def MediaWikiLyrics.version()
137
+ return @@VERSION
138
+ end
139
+
140
+ def version()
141
+ return self.class.version()
142
+ end
143
+
144
+ attr_reader :username, :password
145
+
146
+ def initialize( cleanup_lyrics=true, logger=nil, username=nil, password=nil )
147
+ super( cleanup_lyrics, logger )
148
+ @logged_in = false
149
+ @cookie = nil
150
+ @last_auth_check = 0 # last time we checked the control page
151
+ @authorized = false
152
+ @username = username ? username.clone().freeze() : nil
153
+ @password = password ? password.clone().freeze() : nil
154
+ end
155
+
156
+ def control_page()
157
+ return self.class.control_page()
158
+ end
159
+
160
+ def logged_in?()
161
+ return @logged_in
162
+ end
163
+
164
+ def build_lyrics_fetch_data( request )
165
+ return FetchPageData.new( build_song_rawdata_url( request.artist, request.title ) )
166
+ end
167
+
168
+ def page_error?( page_body )
169
+
170
+ # database error
171
+ db_error = /<!-- start content -->\s*(<p>|)A database query syntax error has occurred\.\s*This may indicate a bug in the software\.*/m.match( page_body )
172
+
173
+ return true if db_error != nil
174
+
175
+ # page title error
176
+ title_error = /<!-- start content -->\s*(<p>|)The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title\. It may contain one( or|) more characters which cannot be used in titles\.*/m.match( page_body )
177
+
178
+ return title_error != nil
179
+
180
+ end
181
+ protected :page_error?
182
+
183
+ def MediaWikiLyrics.fetch_content_page( page_url, follow_redirects=@@REDIRECT_FOLLOWUPS )
184
+
185
+ response, page_url = HTTP.fetch_page_get( page_url )
186
+ page_url = page_url.sub( "&action=raw", "" )
187
+
188
+ page_body = response ? response.body() : nil
189
+ return nil, page_url if page_body == nil
190
+
191
+ # work around redirects pages
192
+ count = 0
193
+ articles = [ parse_url( page_url ) ]
194
+ while ( count < follow_redirects && (md = /#redirect ?('''|)\[\[([^\]]+)\]\]('''|)/i.match( page_body )) )
195
+
196
+ article = cleanup_article( md[2] )
197
+ if articles.include?( article ) # circular redirect found
198
+ return nil, url
199
+ else
200
+ articles << article
201
+ end
202
+
203
+ response, page_url = HTTP.fetch_page_get( build_rawdata_url( article ) )
204
+ page_url = page_url.sub( "&action=raw", "" )
205
+
206
+ page_body = response ? response.body() : nil
207
+ return nil, page_url if page_body == nil
208
+
209
+ count += 1
210
+ end
211
+
212
+ page_body = "" if page_body.strip() == "/* Empty */"
213
+
214
+ return page_body, page_url
215
+
216
+ end
217
+
218
+ def fetch_content_page( url, follow_redirects=@@REDIRECT_FOLLOWUPS )
219
+ self.class.fetch_content_page( url, follow_redirects )
220
+ end
221
+
222
+ def fetch_lyrics_page( fetch_data )
223
+ return nil, nil if fetch_data.url == nil
224
+ log( "Fetching lyrics page... ", 0 )
225
+ page_body, page_url = fetch_content_page( fetch_data.url )
226
+ if page_body
227
+ log( "OK" )
228
+ log( response.body(), 2 ) if verbose_log?
229
+ else
230
+ log( "ERROR" )
231
+ end
232
+ return page_body, page_url
233
+ end
234
+
235
+ def lyrics_page_valid?( request, page_body, page_url )
236
+ return ! page_error?( page_body )
237
+ end
238
+
239
+ def build_suggestions_fetch_data( request )
240
+ return FetchPageData.new( build_song_search_url( request.artist, request.title ) )
241
+ end
242
+
243
+ def lyrics_from_url( request, url )
244
+ # we fetch and parse lyrics from raw content mode so we need the right url format
245
+ if ! url.index( "&action=raw" )
246
+ artist, title = parse_song_url( url )
247
+ url = build_song_rawdata_url( artist, title, false )
248
+ end
249
+ return super( request, url )
250
+ end
251
+
252
+ def parse_suggestions( request, page_body, page_url )
253
+ suggestions = []
254
+ parse_search_results( page_body, true ).each() do |result|
255
+ artist, title = parse_song_url( result[@@SEARCH_RESULT_URL] )
256
+ if ! Strings.empty?( artist ) && ! Strings.empty?( title ) && /\ \([0-9]{4,4}\)$/.match( title ) == nil
257
+ suggestions << Suggestion.new( artist, title, result[@@SEARCH_RESULT_URL] )
258
+ end
259
+ end
260
+ return suggestions
261
+ end
262
+
263
+ def login( username=@username, password=@password, force=false )
264
+
265
+ return true if ! force && @logged_in && username == @username && password == @password
266
+
267
+ @username, @password = username, password
268
+ notify = ! @logged_in || ! force
269
+
270
+ if ! @username || ! @password
271
+ notify( I18n.get( "wiki.login.error", @username ? @username : "<NONE>" ) ) if notify
272
+ return @logged_in = false
273
+ end
274
+
275
+ notify( I18n.get( "wiki.login.attempt", @username ) ) if notify
276
+
277
+ headers = { "Keep-Alive"=>"300", "Connection"=>"keep-alive" }
278
+ resp, url = HTTP.fetch_page_get( "http://#{site_host()}/index.php?title=Special:Userlogin", headers )
279
+ @cookie = resp.response["set-cookie"].split( "; " )[0]
280
+
281
+ params = { "wpName"=>@username, "wpPassword"=>@password, "wpLoginattempt"=>"Log In" }
282
+ headers.update( { "Cookie"=>@cookie } )
283
+ resp, url = HTTP.fetch_page_post( "http://#{site_host()}/index.php?title=Special:Userlogin&action=submitlogin", params, headers, -1 )
284
+
285
+ data = resp.body()
286
+ data.gsub!( /[ \t\n]+/, " " )
287
+
288
+ @logged_in = (/<h2>Login error:<\/h2>/.match( data ) == nil)
289
+ @logged_in = (/<h1 class="firstHeading">Login successful<\/h1>/.match( data ) != nil) if @logged_in # recheck
290
+
291
+ notify( I18n.get( "wiki.login." + (@logged_in ? "success" : "error"), @username ) ) if notify
292
+
293
+ return @logged_in
294
+
295
+ end
296
+
297
+ def logout()
298
+ @cookie = false
299
+ if @logged_in
300
+ @logged_in = false
301
+ notify( I18n.get( "wiki.logout", @username ) )
302
+ end
303
+ end
304
+
305
+ def authorized?()
306
+
307
+ # debug( "AUTHORIZED INITIAL VALUE: #{@authorized}" )
308
+
309
+ return @authorized if (Time.new().to_i() - @last_auth_check) < @@AUTH_INTERVAL
310
+
311
+ body, url = fetch_content_page( build_rawdata_url( control_page() ) )
312
+ return false if ! body
313
+
314
+ # debug( "CONTROL PAGE:\n#{body}" )
315
+
316
+ @last_auth_check = Time.new().to_i()
317
+
318
+ control_data = {}
319
+ body.split( "\n" ).each() do |line|
320
+ if (md = /^([^=]+)=(.*)$/.match( line ))
321
+ control_data[md[1].strip()] = md[2].strip()
322
+ end
323
+ end
324
+
325
+ if control_data["BlockedVersions"].to_s().strip().split( /\s*;\s*/ ).include?( version() )
326
+ notify( I18n.get( "wiki.control.versionblocked", version() ) )
327
+ @authorized = false
328
+ # debug( "VERSION BLOCKED" )
329
+ elsif control_data["BlockedUsers"].to_s().strip().downcase().split( /\s*;\s*/ ).include?( @username.to_s().downcase() )
330
+ notify( I18n.get( "wiki.control.userblocked", @username ) )
331
+ @authorized = false
332
+ # debug( "USER BLOCKED" )
333
+ else
334
+
335
+ if (last_version = control_data["LastVersion"].to_s().strip().split( "." )).size > 0
336
+ curr_version = self.version().split( "." )
337
+ curr_version.size.times() do |idx|
338
+ if curr_version[idx].to_i() > last_version[idx].to_i()
339
+ break
340
+ elsif curr_version[idx].to_i() < last_version[idx].to_i()
341
+ notify( I18n.get( "wiki.control.updated", control_data["LastVersion"] ) )
342
+ break
343
+ end
344
+ end
345
+ end
346
+
347
+ @authorized = true
348
+ # debug( "SUBMIT ALLOWED" )
349
+ end
350
+
351
+ return @authorized
352
+ end
353
+
354
+ def restore_session_params( username, password, cookie, last_auth_check=0, authorized=false )
355
+ if ! Strings.empty?( cookie ) && ! Strings.empty?( username )
356
+ @username = username
357
+ @password = password
358
+ @cookie = cookie
359
+ @last_auth_check = last_auth_check.to_i()
360
+ @authorized = authorized.to_s() == 'true'
361
+ @logged_in = true
362
+ notify( I18n.get( "wiki.session.restore.success", @username ) )
363
+ return true
364
+ else
365
+ notify( I18n.get( "wiki.session.restore.error", @username ) )
366
+ return false
367
+ end
368
+
369
+ end
370
+
371
+ def restore_session( session_file )
372
+ values = { "username" => nil, "password" => nil, "cookie" => nil, "last_auth_check" => nil, "authorized" => nil }
373
+ if XMLHash.read( session_file, values )
374
+ return restore_session_params(
375
+ Strings.descramble( values["username"] ),
376
+ Strings.descramble( values["password"] ),
377
+ values["cookie"],
378
+ values["last_auth_check"],
379
+ values["authorized"]
380
+ )
381
+ else
382
+ notify( I18n.get( "wiki.session.restore.notfound" ) )
383
+ return false
384
+ end
385
+ end
386
+
387
+ def get_session_params()
388
+ return (@username ? @username.clone() : nil), (@password ? @password.clone() : nil), (@cookie ? @cookie.clone() : nil), @last_auth_check, @authorized
389
+ end
390
+
391
+ def save_session( session_file )
392
+ if ! @logged_in
393
+ notify( I18n.get( "wiki.session.save.error.notloggedin" ) )
394
+ return false
395
+ end
396
+ values = {
397
+ "username" => Strings.scramble( @username ),
398
+ "password" => Strings.scramble( @password ),
399
+ "cookie" => @cookie,
400
+ "last_auth_check" => @last_auth_check,
401
+ "authorized" => @authorized
402
+ }
403
+ if XMLHash.write( session_file, values )
404
+ notify( I18n.get( "wiki.session.save.success", @username ) )
405
+ return true
406
+ else
407
+ notify( I18n.get( "wiki.session.save.error", @username ) )
408
+ return false
409
+ end
410
+ end
411
+
412
+
413
+ def fetch_page_edit_params( page_url )
414
+ headers = { "Keep-Alive"=>"300", "Connection"=>"keep-alive", "Referer"=>"#{page_url}", "Cookie"=>@cookie }
415
+ response, page_url = HTTP.fetch_page_get( "#{page_url}&action=edit", headers )
416
+
417
+ edit_params = {}
418
+ return edit_params if ! response || response.code != "200"
419
+
420
+ page_body = response.body()
421
+ page_body.tr_s!( " \n\r\t", " " )
422
+
423
+ if (md = /<input type=['"]hidden['"] value=['"]([a-fA-F0-9+\\]*)['"] name=['"]wpEditToken['"] ?\/>/.match( page_body ))
424
+ edit_params["edit_token"] = md[1]
425
+ if (md = /<input type=['"]hidden['"] value=['"]([0-9]+)['"] name=['"]wpEdittime['"] ?\/>/.match( page_body ))
426
+ edit_params["edit_time"] = md[1]
427
+ end
428
+ if (md = /<input type=['"]hidden['"] value=['"]([0-9]+)['"] name=['"]wpStarttime['"] ?\/>/.match( page_body ))
429
+ edit_params["start_time"] = md[1]
430
+ end
431
+ edit_params["logged_in"] = (/<li id="pt-logout">/.match( page_body ) != nil)
432
+ end
433
+
434
+ return edit_params
435
+ end
436
+ protected :fetch_page_edit_params
437
+
438
+ def submit_page( page_url, page_content, summary="", watch=true, retries=1, auto_login=true )
439
+
440
+ return false if (! logged_in? && ! auto_login) || ! authorized?
441
+
442
+ (retries+1).times do
443
+
444
+ begin
445
+
446
+ next if ! logged_in? && ! login()
447
+
448
+ # make sure we have the long url format
449
+ page_url = build_url( parse_url( page_url ) )
450
+
451
+ # Try to get the edit token for url, can't continue without it:
452
+ edit_params = fetch_page_edit_params( page_url )
453
+
454
+ if ! edit_params["logged_in"] # site says user is not logged in (session has expired), force relogin
455
+ next if ! login( @username, @password, true )
456
+ # after being successfully logged in, refetch the edit page
457
+ edit_params = fetch_page_edit_params( page_url )
458
+ next if ! edit_params["logged_in"]
459
+ end
460
+
461
+ next if ! edit_params["edit_token"]
462
+
463
+ params = [
464
+ MultipartFormData.text_param( "wpTextbox1", page_content ),
465
+ MultipartFormData.text_param( "wpSummary", summary ),
466
+ MultipartFormData.text_param( "wpWatchthis", watch ? "on" : "off" ),
467
+ MultipartFormData.text_param( "wpSave", "Save page" ),
468
+ MultipartFormData.text_param( "wpSection", "" ),
469
+ MultipartFormData.text_param( "wpStarttime", edit_params["start_time"].to_s() ), # the new revision time
470
+ MultipartFormData.text_param( "wpEdittime", edit_params["edit_time"].to_s() ), # the previous revision time
471
+ MultipartFormData.text_param( "wpEditToken", edit_params["edit_token"] ),
472
+ ]
473
+
474
+ headers = {
475
+ "Keep-Alive" => "300",
476
+ "Connection" => "keep-alive",
477
+ "Referer" => "http://#{site_host()}#{page_url}&action=edit",
478
+ "Cookie" => @cookie,
479
+ }
480
+
481
+ response, page_url = HTTP.fetch_page_post_form_multipart( "#{page_url}&action=submit", params, headers, -1 )
482
+
483
+ return true if response.code == "302" # we should have received a redirect code
484
+
485
+ rescue TimeoutError
486
+ end
487
+ end
488
+
489
+ return false
490
+ end
491
+
492
+ def submit_redirect_page( page_url, link, summary=nil )
493
+ if submit_page( page_url, "#REDIRECT #{link}", summary )
494
+ notify( I18n.get( "wiki.submitredirect.success", link ) )
495
+ return url
496
+ else
497
+ notify( I18n.get( "wiki.submitredirect.error", link ) )
498
+ return nil
499
+ end
500
+ end
501
+
502
+ def upload_file( src_file, dst_file, mime_type, description="", watch=true, auto_login=true )
503
+
504
+ if ! logged_in?
505
+ return false if ! auto_login || ! login()
506
+ end
507
+
508
+ begin
509
+ data = File.new( src_file ).read()
510
+ rescue Exception
511
+ return false
512
+ end
513
+
514
+ params = [
515
+ MultipartFormData.file_param( "wpUploadFile", File.basename( src_file ), mime_type, data ),
516
+ MultipartFormData.text_param( "wpDestFile", dst_file ),
517
+ MultipartFormData.text_param( "wpUploadDescription", description ),
518
+ MultipartFormData.text_param( "wpWatchthis", watch ? "true" : "false" ),
519
+ MultipartFormData.text_param( "wpUpload", "Upload file" ),
520
+ ]
521
+
522
+ headers = {
523
+ "Keep-Alive" => "300",
524
+ "Connection" => "keep-alive",
525
+ "Referer" => "http://#{site_host()}/index.php?title=Special:Upload&wpDestFile=#{CGI.escape(dst_file)}",
526
+ "Cookie" => @cookie,
527
+ }
528
+
529
+ begin
530
+
531
+ response, page_url = HTTP.fetch_page_post_form_multipart(
532
+ "http://#{site_host()}/index.php?title=Special:Upload",
533
+ params,
534
+ headers,
535
+ -1
536
+ )
537
+
538
+ # we have to receive a redirect code
539
+ return true if response.code == "302"
540
+
541
+ # error, possibly an expired session: relogin and try again
542
+ login( @username, @password, true )
543
+ response, page_url = HTTP.fetch_page_post_form_multipart(
544
+ "http://#{site_host()}/index.php?title=Special:Upload",
545
+ params,
546
+ headers,
547
+ -1
548
+ )
549
+
550
+ # again, we should have received a redirect
551
+ return response.code == "302"
552
+
553
+ rescue TimeoutError
554
+ return false
555
+ end
556
+
557
+ end
558
+
559
+ def upload_cover_image( image_path, artist, album, year, watch=true )
560
+
561
+ album_art_name = build_album_art_name( artist, album, year )
562
+ album_art_desc = build_album_art_description( artist, album, year )
563
+ image_path, mime_type = prepare_image_file( image_path )
564
+
565
+ if Strings.empty?( image_path ) || Strings.empty?( mime_type )
566
+ notify( I18n.get( "wiki.uploadcover.error.convert" ) )
567
+ else
568
+ notify( I18n.get( "wiki.uploadcover.uploading", album, artist ) )
569
+ if upload_file( image_path, album_art_name, mime_type, album_art_desc, watch )
570
+ notify( I18n.get( "wiki.uploadcover.success", album, artist ) )
571
+ else
572
+ notify( I18n.get( "wiki.uploadcover.error", album, artist ) )
573
+ end
574
+ end
575
+
576
+ end
577
+
578
+
579
+ def build_tracks( album_data )
580
+ return self.class.build_tracks( album_data )
581
+ end
582
+
583
+ def build_album_page( reviewed, artist, album, year, month, day, tracks, album_art )
584
+ return self.class.build_album_page( reviewed, artist, album, year, month, day, tracks, album_art )
585
+ end
586
+
587
+ def submittable_album_params?( artist, album, year )
588
+ if Strings.empty?( artist )
589
+ notify( I18n.get( "wiki.submitalbum.error.invalidartist" ) )
590
+ return false
591
+ elsif Strings.empty?( album )
592
+ notify( I18n.get( "wiki.submitalbum.error.invalidalbum" ) )
593
+ return false
594
+ elsif year <= 1900
595
+ notify( I18n.get( "wiki.submitalbum.error.invalidyear" ) )
596
+ return false
597
+ else
598
+ return true
599
+ end
600
+ end
601
+ protected :submittable_album_params?
602
+
603
+ # use skip_initial_page_search when you know the page doesn't exists (i.e. when you have already searched it)
604
+ def submit_album_page( album_data, image_path=nil, allow_page_overwrite=true, skip_initial_page_search=false, show_review_dialog=true, must_review=false )
605
+
606
+ show_review_dialog = true if must_review
607
+
608
+ if ! allow_page_overwrite && ! skip_initial_page_search
609
+ notify( I18n.get( "wiki.submitalbum.searchingpage", album_data.title, album_data.artist ) )
610
+ if find_album_page_url( album_data.artist, album_data.title, album_data.year )
611
+ notify( I18n.get( "wiki.submitalbum.pagefound", album_data.title, album_data.artist ) )
612
+ return nil, nil
613
+ else
614
+ notify( I18n.get( "wiki.submitalbum.nopagefound", album_data.title, album_data.artist ) )
615
+ end
616
+ end
617
+
618
+ page_data = {
619
+ "site_name" => site_name(),
620
+ "artist" => album_data.artist,
621
+ "year" => album_data.year,
622
+ "month" => album_data.month,
623
+ "day" => album_data.day,
624
+ "album" => cleanup_title_token( album_data.title ),
625
+ "tracks" => build_tracks( album_data ),
626
+ "reviewed" => false
627
+ }
628
+
629
+ page_data["album_art_name"] = find_album_art_name( page_data["artist"], page_data["album"], page_data["year"] )
630
+ if page_data["album_art_name"] == nil # album art not found, we'll attempt to upload it
631
+ page_data["image_path"] = image_path.to_s()
632
+ attempt_upload = true
633
+ else
634
+ attempt_upload = false
635
+ end
636
+
637
+ # check if the album parameters are "submitteable" content
638
+ return nil, nil if ! submittable_album_params?( page_data["artist"], page_data["album"], page_data["year"] )
639
+
640
+ page_url = build_album_url( page_data["artist"], page_data["album"], page_data["year"], false )
641
+
642
+ page_content = build_album_page(
643
+ page_data["reviewed"],
644
+ page_data["artist"],
645
+ page_data["album"],
646
+ page_data["year"],
647
+ page_data["month"],
648
+ page_data["day"],
649
+ page_data["tracks"],
650
+ page_data["album_art_name"] ?
651
+ page_data["album_art_name"] :
652
+ build_album_art_name( page_data["artist"], page_data["album"], page_data["year"] )
653
+ )
654
+
655
+ if attempt_upload && ! Strings.empty?( page_data["image_path"] )
656
+ upload_cover_image( page_data["image_path"], page_data["artist"], page_data["album"], page_data["year"] )
657
+ end
658
+
659
+ summary = "#{page_data["reviewed"] ? "" : "autogen. "}album page (#{@@NAME}v#{@@VERSION})"
660
+ if submit_page( page_url, page_content, summary )
661
+ notify( I18n.get( "wiki.submitalbum.success", page_data["album"], page_data["artist"] ) )
662
+ return page_url, page_data
663
+ else
664
+ notify( I18n.get( "wiki.submitalbum.error", page_url ) )
665
+ return nil, page_data
666
+ end
667
+
668
+ end
669
+
670
+ def build_song_page( reviewed, artist, title, album, year, credits, lyricist, lyrics )
671
+ self.class.build_song_page( reviewed, artist, title, album, year, credits, lyricist, lyrics )
672
+ end
673
+
674
+
675
+ def submittable_song_params?( artist, song, lyrics, instrumental )
676
+ if Strings.empty?( artist )
677
+ notify( I18n.get( "wiki.submitsong.error.invalidartist" ) )
678
+ return false
679
+ elsif Strings.empty?( song )
680
+ notify( I18n.get( "wiki.submitsong.error.invalidsong" ) )
681
+ return false
682
+ elsif Strings.empty?( lyrics ) && ! instrumental
683
+ notify( I18n.get( "wiki.submitsong.error.nolyrics" ) )
684
+ return false
685
+ else
686
+ return true
687
+ end
688
+ end
689
+ protected :submittable_song_params?
690
+
691
+ # if edit_url is not nil, it's assumed that we're trying to overwrite (edit) a page, otherwise
692
+ # it's assummed that we're trying to create a new page and so overwritting won't be allowed
693
+ # use skip_initial_page_search when you know the page doesn't exists (i.e. when you have already searched it)
694
+ def submit_song_page( song_data, edit_url=nil, skip_initial_page_search=false, show_review_dialog=true, must_review=false )
695
+
696
+ show_review_dialog = true if must_review
697
+
698
+ if ! edit_url && ! skip_initial_page_search
699
+ notify( I18n.get( "wiki.submitsong.searchingpage", song_data.title, song_data.artist ) )
700
+ if find_song_page_url( song_data.artist, song_data.title )
701
+ notify( I18n.get( "wiki.submitsong.pagefound", song_data.title, song_data.artist ) )
702
+ return nil, nil
703
+ else
704
+ notify( I18n.get( "wiki.submitsong.nopagefound", song_data.title, song_data.artist ) )
705
+ end
706
+ end
707
+
708
+ page_data = {
709
+ "site_name" => site_name(),
710
+ "edit_mode" => edit_url != nil,
711
+ "artist" => cleanup_title_token( song_data.artist ),
712
+ "year" => song_data.year,
713
+ "album" => cleanup_title_token( song_data.album.to_s() ),
714
+ "title" => cleanup_title_token( song_data.title ),
715
+ "lyrics" => Strings.cleanup_lyrics( song_data.lyrics.to_s() ),
716
+ "instrumental" => song_data.instrumental?,
717
+ "credits" => song_data.credits.join( "; " ),
718
+ "lyricist" => song_data.lyricists.join( "; " ),
719
+ "reviewed" => false
720
+ }
721
+
722
+ # check if the song parameters are "submitteable" content
723
+ return nil, nil if ! submittable_song_params?( page_data["artist"], page_data["title"], page_data["lyrics"], page_data["instrumental"] )
724
+
725
+ page_url = edit_url ? edit_url : build_song_url( page_data["artist"], page_data["title"], false )
726
+
727
+ page_content = build_song_page(
728
+ page_data["reviewed"],
729
+ page_data["artist"],
730
+ page_data["title"],
731
+ page_data["album"],
732
+ page_data["year"],
733
+ page_data["credits"],
734
+ page_data["lyricist"],
735
+ page_data["lyrics"]
736
+ )
737
+
738
+ summary = "#{page_data["reviewed"] ? "" : "autogen. "}song page (#{@@NAME}v#{@@VERSION})"
739
+ if submit_page( page_url, page_content, summary )
740
+ notify( I18n.get( "wiki.submitsong.success", page_data["title"], page_data["artist"] ) )
741
+ return page_url, page_data
742
+ else
743
+ notify( I18n.get( "wiki.submitsong.error", page_url ) )
744
+ return nil, page_data
745
+ end
746
+
747
+ end
748
+
749
+ def MediaWikiLyrics.parse_search_results( page_body, content_matches=false )
750
+
751
+ results = []
752
+ return results if page_body == nil
753
+
754
+ page_body.tr_s!( " \n\r\t", " " )
755
+ page_body.gsub!( /\ ?<\/?span[^>]*> ?/, "" )
756
+
757
+ return results if ! page_body.sub!( /^.*<a name="(Article|Page)_title_matches">/, "" ) &&
758
+ ! page_body.sub!( /^.*<a name="No_(article|page)_title_matches">/, "" )
759
+
760
+ page_body.sub!( /<a name="No_(article|page)_text_matches">.*$/, "" ) if ! content_matches
761
+
762
+ return results if ! page_body.gsub!( /<form id="powersearch" method="get" action="[^"]+">.*$/, "" )
763
+ page_body.gsub!( /<\/[uo]l> ?<p( [^>]*|)>View \(previous .*$/, "" )
764
+
765
+ page_body.split( "<li>" ).each() do |entry|
766
+ if (md = /<a href="([^"]*index\.php\/|[^"]*index\.php\?title=|\/)([^"]*)" title="([^"]+)"/.match( entry ))
767
+ result = {
768
+ @@SEARCH_RESULT_URL => "http://#{site_host()}/index.php?title=#{md[2]}",
769
+ @@SEARCH_RESULT_TITLE => md[3]
770
+ }
771
+ results << result if ! content_matches || ! results.include?( result )
772
+ end
773
+ end
774
+
775
+ return results
776
+ end
777
+
778
+ def parse_search_results( page_body, content_matches=false )
779
+ self.class.parse_search_results( page_body, content_matches )
780
+ end
781
+
782
+ @@FIND_TEMPLATE_START = 0
783
+ @@FIND_TEMPLATE_NAME = 1
784
+ @@FIND_TEMPLATE_PARAM = 2
785
+ @@FIND_TEMPLATE_PARAM_VALUE = 3
786
+
787
+ # NOTE: nested templates are only supported in named parameters values
788
+ def MediaWikiLyrics.parse_template_rec( template_text, first_index )
789
+
790
+ template_data = { "name" => "", "params" => {} }
791
+
792
+ operation = @@FIND_TEMPLATE_START
793
+
794
+ param_number = 1
795
+ param_name = nil
796
+ aux_index = -1
797
+
798
+ link = false
799
+
800
+ chr_index = first_index - 1
801
+ chrs_array = template_text.is_a?( Array ) ? template_text : template_text.unpack( "U*" )
802
+ max_chr_index = chrs_array.size-2
803
+
804
+ while ( chr_index <= max_chr_index )
805
+
806
+ chr_index += 1
807
+ chr = [chrs_array[chr_index]].pack( "U" )
808
+
809
+ if operation == @@FIND_TEMPLATE_START
810
+ if chr == "{"
811
+ aux_index = chr_index + 2 # start of template name
812
+ operation = @@FIND_TEMPLATE_NAME
813
+ end
814
+ elsif operation == @@FIND_TEMPLATE_NAME
815
+ if chr == "|"
816
+ template_data["name"] = chrs_array[aux_index..chr_index-1].pack( "U*" ).strip()
817
+ aux_index = chr_index + 1 # start of first parameter (if there is one)
818
+ operation = @@FIND_TEMPLATE_PARAM
819
+ elsif chr == "}" # we may have arrived to the template end (in which case the template has no parameters)
820
+ next_chr = chrs_array[chr_index+1]
821
+ if next_chr && [next_chr].pack( "U" ) == "}" # }} indicates the template end
822
+ template_data["name"] = chrs_array[aux_index..chr_index-1].pack( "U*" ).strip()
823
+ chr_index += 1
824
+ break
825
+ end
826
+ end
827
+ elsif operation == @@FIND_TEMPLATE_PARAM
828
+ if chr == "="
829
+ param_name = chrs_array[aux_index..chr_index-1].pack( "U*" ).strip()
830
+ param_name = param_name.to_i() if param_name.to_i() > 0
831
+ template_data["params"].delete( param_name )
832
+ aux_index = chr_index + 1 # start of parameter value
833
+ operation = @@FIND_TEMPLATE_PARAM_VALUE
834
+ elsif link
835
+ if chr == "]" # we may be at the end of a link (we no longer ignore | and } characters in that case)
836
+ next_chr = chrs_array[chr_index+1]
837
+ if next_chr && [next_chr].pack( "U" ) == "]" # ]] indicates the link end
838
+ link = false
839
+ chr_index += 1
840
+ end
841
+ end
842
+ else
843
+ if chr == "[" # we may be at the start of a link (we ignore the | and } characters in that case)
844
+ next_chr = chrs_array[chr_index+1]
845
+ if next_chr && [next_chr].pack( "U" ) == "[" # [[ indicates the link start
846
+ link = true
847
+ chr_index += 1
848
+ end
849
+ elsif chr == "|" # we arrived to the parameter end (the parameter has no name)
850
+ template_data["params"][param_number] = chrs_array[aux_index..chr_index-1].pack( "U*" )
851
+ HTMLEntities.decode!( template_data["params"][param_number] )
852
+ param_number += 1
853
+ aux_index = chr_index + 1 # start of next parameter (if there is one)
854
+ elsif chr == "}" # we may have arrived to the template end (in which case the parameter has no name)
855
+ next_chr = chrs_array[chr_index+1]
856
+ if next_chr && [next_chr].pack( "U" ) == "}" # }} indicates the template end
857
+ template_data["params"][param_number] = chrs_array[aux_index..chr_index-1].pack( "U*" )
858
+ HTMLEntities.decode!( template_data["params"][param_number] )
859
+ param_number += 1
860
+ chr_index += 1
861
+ break
862
+ end
863
+ end
864
+ end
865
+ elsif operation == @@FIND_TEMPLATE_PARAM_VALUE
866
+ if chr == "{" # we may have arrived at a nested template case (the only one
867
+ # supported: template inserted in a named parameter value)
868
+ next_chr = chrs_array[chr_index+1]
869
+ if next_chr && [next_chr].pack( "U" ) == "{" # {{ indicates the template start
870
+ template_data["params"][param_name] = [] if ! template_data["params"][param_name]
871
+ param_array = template_data["params"][param_name]
872
+ prev_text = chrs_array[aux_index..chr_index-1].pack( "U*" )
873
+ HTMLEntities.decode!( prev_text )
874
+ prev_text.lstrip!() if param_array.empty?
875
+ param_array.insert( -1, prev_text ) if ! prev_text.empty?
876
+ chr_index, nested_template_data = parse_template_rec( chrs_array, chr_index )
877
+ param_array.insert( -1, nested_template_data )
878
+ aux_index = chr_index + 1
879
+ end
880
+ elsif link
881
+ if chr == "]" # we may be at the end of a link (we no longer ignore | and } characters in that case)
882
+ next_chr = chrs_array[chr_index+1]
883
+ if next_chr && [next_chr].pack( "U" ) == "]" # ]] indicates the link end
884
+ link = false
885
+ chr_index += 1
886
+ end
887
+ end
888
+ else
889
+ if chr == "[" # we may be at the start of a link (we ignore the | and } characters in that case)
890
+ next_chr = chrs_array[chr_index+1]
891
+ if next_chr && [next_chr].pack( "U" ) == "[" # [[ indicates the link start
892
+ link = true
893
+ chr_index += 1
894
+ end
895
+ elsif chr == "|" # we arrived to the parameter end
896
+ last_text = chrs_array[aux_index..chr_index-1].pack( "U*" )
897
+ HTMLEntities.decode!( last_text )
898
+ if template_data["params"][param_name]
899
+ param_array = template_data["params"][param_name]
900
+ last_text.rstrip!()
901
+ param_array.insert( -1, last_text ) if ! last_text.empty?
902
+ else
903
+ last_text.strip!()
904
+ template_data["params"][param_name] = last_text
905
+ end
906
+ aux_index = chr_index + 1
907
+ operation = @@FIND_TEMPLATE_PARAM
908
+ elsif chr == "}" # we may have arrived to the template end
909
+ next_chr = chrs_array[chr_index+1]
910
+ if next_chr && [next_chr].pack( "U" ) == "}" # }} indicates the template end
911
+ last_text = chrs_array[aux_index..chr_index-1].pack( "U*" )
912
+ HTMLEntities.decode!( last_text )
913
+ if template_data["params"][param_name]
914
+ param_array = template_data["params"][param_name]
915
+ last_text.rstrip!()
916
+ param_array.insert( -1, last_text ) if ! last_text.empty?
917
+ else
918
+ last_text.strip!()
919
+ template_data["params"][param_name] = last_text
920
+ end
921
+ chr_index += 1
922
+ break
923
+ end
924
+ end
925
+ end
926
+ end
927
+
928
+ end
929
+
930
+ return chr_index, template_data
931
+
932
+ end
933
+ private_class_method :parse_template_rec
934
+
935
+ def MediaWikiLyrics.parse_template( template_text )
936
+ index, template_data = parse_template_rec( template_text, 0 )
937
+ return template_data
938
+ end
939
+
940
+ def parse_template( template )
941
+ return self.class.parse_template( template )
942
+ end
943
+
944
+ def MediaWikiLyrics.prepare_image_file( image_path, size_limit=153600 )
945
+ 4.times() do |trynumb|
946
+ system( "convert", "-quality", (100-trynumb*10).to_s(), image_path, "/tmp/AlbumArt.jpg" )
947
+ return nil, nil if $? != 0
948
+ size = FileTest.size?( "/tmp/AlbumArt.jpg" )
949
+ return "/tmp/AlbumArt.jpg", "image/jpeg" if (size ? size : 0) <= size_limit
950
+ end
951
+ return nil, nil
952
+ end
953
+
954
+ def prepare_image_file( image_path, size_limit=153600 )
955
+ return self.class.prepare_image_file( image_path, size_limit )
956
+ end
957
+
958
+
959
+
960
+ def MediaWikiLyrics.cleanup_title_token( title, downcase=false )
961
+ return cleanup_title_token!( String.new( title ), downcase )
962
+ end
963
+
964
+ def cleanup_title_token( title, downcase=false )
965
+ return self.class.cleanup_title_token( title, downcase )
966
+ end
967
+
968
+ def cleanup_title_token!( title, downcase=false )
969
+ return self.class.cleanup_title_token!( title, downcase )
970
+ end
971
+
972
+ def MediaWikiLyrics.get_sort_name( title )
973
+ return get_sort_name!( String.new( title ) )
974
+ end
975
+
976
+ def get_sort_name( title )
977
+ return self.class.get_sort_name( title )
978
+ end
979
+
980
+ def MediaWikiLyrics.get_sort_name!( title )
981
+
982
+ title.gsub!( /\[[^\]\[]*\]/, "" )
983
+
984
+ Strings.downcase!( title )
985
+
986
+ title.gsub!( /[·\.,;:"`´¿\?¡!\(\)\[\]{}<>#\$\+\*%\^]/, "" )
987
+ title.gsub!( /[\\\/_-]/, " " )
988
+ title.gsub!( "&", "and" )
989
+ title.squeeze!( " " )
990
+ title.strip!()
991
+
992
+ title.gsub!( /^a /, "" ) # NOTE: only valid for English
993
+ title.gsub!( /^an /, "" )
994
+ title.gsub!( /^the /, "" )
995
+ title.gsub!( /^el /, "" )
996
+ title.gsub!( /^le /, "" )
997
+ title.gsub!( /^la /, "" )
998
+ title.gsub!( /^l'([aeiou])/, "\\1" )
999
+ title.gsub!( /^los /, "" )
1000
+ title.gsub!( /^las /, "" )
1001
+ title.gsub!( /^les /, "" )
1002
+ title.gsub!( "'", "" )
1003
+
1004
+ Strings.remove_vocal_accents!( title )
1005
+ Strings.titlecase!( title, false, false )
1006
+
1007
+ return title
1008
+ end
1009
+
1010
+ def get_sort_name!( title )
1011
+ return self.class.get_sort_name!( title )
1012
+ end
1013
+
1014
+ def MediaWikiLyrics.get_sort_letter( title )
1015
+ title = get_sort_name( title )
1016
+ title = title.strip()
1017
+ if title.index( /^[0-9]/ ) == 0
1018
+ return "0-9"
1019
+ else
1020
+ # return title.slice( 0, 1 )
1021
+ return title.unpack( "U*" ).slice( 0, 1 ).pack( "U*" )
1022
+ end
1023
+ end
1024
+
1025
+ def get_sort_letter( title )
1026
+ return self.class.get_sort_letter( title )
1027
+ end
1028
+
1029
+
1030
+
1031
+ # GENERAL FUNCTIONS
1032
+
1033
+ def MediaWikiLyrics.cleanup_article( article, capitalize=true )
1034
+ article = article.gsub( "_", " " )
1035
+ article.strip!()
1036
+ article.squeeze!( " " )
1037
+ Strings.capitalize!( article, false, true ) if capitalize
1038
+ return article
1039
+ end
1040
+
1041
+ def cleanup_article( article )
1042
+ return self.class.cleanup_article( article )
1043
+ end
1044
+
1045
+ def MediaWikiLyrics.parse_link( link )
1046
+
1047
+ if (md = /^ *\[\[([^\|\]]+)\]\] *$/.match( link ))
1048
+ article = cleanup_article( md[1] )
1049
+ return article, article if ! article.empty?
1050
+ end
1051
+
1052
+ if (md = /^ *\[\[([^\|\]]+)\|([^\]]*)\]\] *$/.match( link ))
1053
+ article = cleanup_article( md[1] )
1054
+ if ! article.empty?
1055
+ display = cleanup_article( md[2], false )
1056
+ return article, display if ! display.empty?
1057
+ end
1058
+ end
1059
+
1060
+ return nil, nil
1061
+ end
1062
+
1063
+ def parse_link( link )
1064
+ return self.class.parse_link( link )
1065
+ end
1066
+
1067
+ def MediaWikiLyrics.build_link( article, display=nil )
1068
+ return display ? "[[#{article}|#{display}]]" : "[[#{article}]]"
1069
+ end
1070
+
1071
+ def build_link( article, display=nil )
1072
+ return self.class.build_link( article, display )
1073
+ end
1074
+
1075
+ def MediaWikiLyrics.parse_url( url )
1076
+ if (md = /(https?:\/\/#{site_host()}\/|)(index.php\?title=|wiki\/|)([^&]+)(&.*|)$/.match( url ))
1077
+ return cleanup_article( CGI.unescape( md[3] ) ) # article title
1078
+ else
1079
+ return nil
1080
+ end
1081
+ end
1082
+
1083
+ def parse_url( url )
1084
+ return self.class.parse_url( url )
1085
+ end
1086
+
1087
+ def MediaWikiLyrics.build_url( article )
1088
+ # return "http://#{site_host()}/index.php?title=#{CGI.escape( article )}"
1089
+ return "http://#{site_host()}/index.php?title=#{CGI.escape( article.gsub( " ", "_" ) )}"
1090
+ end
1091
+
1092
+ def build_url( article )
1093
+ return self.class.build_url( article )
1094
+ end
1095
+
1096
+ def MediaWikiLyrics.build_rawdata_url( article )
1097
+ return build_url( article ) + "&action=raw"
1098
+ end
1099
+
1100
+ def build_rawdata_url( article )
1101
+ return self.class.build_rawdata_url( article )
1102
+ end
1103
+
1104
+
1105
+
1106
+
1107
+ # SONG FUNCTIONS
1108
+
1109
+ def MediaWikiLyrics.parse_song_link( link )
1110
+ article, display = parse_link( link )
1111
+ return nil, nil if article == nil
1112
+ if (md = /^([^:]+):(.+)$/.match( article ))
1113
+ return md[1], md[2]
1114
+ else
1115
+ return nil, nil
1116
+ end
1117
+ end
1118
+
1119
+ def parse_song_link( link )
1120
+ return self.class.parse_song_link( link )
1121
+ end
1122
+
1123
+ def MediaWikiLyrics.build_song_link( artist, title, cleanup=true )
1124
+ if cleanup
1125
+ artist = cleanup_title_token( artist )
1126
+ title = cleanup_title_token( title )
1127
+ end
1128
+ return build_link( "#{artist}:#{title}" )
1129
+ end
1130
+
1131
+ def build_song_link( artist, title, cleanup=true )
1132
+ return self.class.build_song_link( artist, title, cleanup )
1133
+ end
1134
+
1135
+ def MediaWikiLyrics.parse_song_url( url )
1136
+ article = parse_url( url )
1137
+ return nil, nil if article == nil
1138
+ if (md = /^([^:]+):(.+)$/.match( article ))
1139
+ return md[1], md[2] # artist, song title
1140
+ else
1141
+ return nil, nil
1142
+ end
1143
+ end
1144
+
1145
+ def parse_song_url( url )
1146
+ return self.class.parse_song_url( url )
1147
+ end
1148
+
1149
+ def MediaWikiLyrics.build_song_url( artist, title, cleanup=true )
1150
+ if cleanup
1151
+ artist = cleanup_title_token( artist )
1152
+ title = cleanup_title_token( title )
1153
+ end
1154
+ return build_url( "#{artist}:#{title}" )
1155
+ end
1156
+
1157
+ def build_song_url( artist, title, cleanup=true )
1158
+ return self.class.build_song_url( artist, title, cleanup )
1159
+ end
1160
+
1161
+ def MediaWikiLyrics.build_song_rawdata_url( artist, title, cleanup=true )
1162
+ return build_song_url( artist, title, cleanup ) + "&action=raw"
1163
+ end
1164
+
1165
+ def build_song_rawdata_url( artist, title, cleanup=true )
1166
+ return self.class.build_song_rawdata_url( artist, title, cleanup )
1167
+ end
1168
+
1169
+ def MediaWikiLyrics.build_song_edit_url( artist, title, cleanup=true )
1170
+ return build_song_url( artist, title, cleanup ) + "&action=edit"
1171
+ end
1172
+
1173
+ def build_song_edit_url( artist, title, cleanup=true )
1174
+ return self.class.build_song_edit_url( artist, title, cleanup )
1175
+ end
1176
+
1177
+ def MediaWikiLyrics.build_song_add_url( request, cleanup=true )
1178
+ return build_song_url( request.artist, request.title, cleanup ) + "&action=edit"
1179
+ end
1180
+
1181
+ def build_song_add_url( request, cleanup=true )
1182
+ return self.class.build_song_add_url( request, cleanup )
1183
+ end
1184
+
1185
+ def MediaWikiLyrics.build_song_search_url( artist, title )
1186
+ artist = cleanup_title_token( artist )
1187
+ title = cleanup_title_token( title )
1188
+ search_string = CGI.escape( "#{artist}:#{title}" )
1189
+ return "http://#{site_host()}/index.php?redirs=1&search=#{search_string}&fulltext=Search&limit=500"
1190
+ end
1191
+
1192
+ def build_song_search_url( artist, title )
1193
+ return self.class.build_song_search_url( artist, title )
1194
+ end
1195
+
1196
+ def MediaWikiLyrics.find_song_page_url( artist, title )
1197
+
1198
+ url = build_song_rawdata_url( artist, title )
1199
+ body, url = fetch_content_page( url )
1200
+
1201
+ if ! Strings.empty?( body ) # page exists
1202
+ return url
1203
+ else
1204
+ artist = Strings.normalize( cleanup_title_token( artist ) )
1205
+ title = Strings.normalize( cleanup_title_token( title ) )
1206
+ normalized_target = "#{artist}:#{title}"
1207
+
1208
+ response, url = HTTP.fetch_page_get( build_song_search_url( artist, title ) )
1209
+ return nil if response == nil
1210
+ parse_search_results( response.body(), true ).each() do |result|
1211
+ normalized_result = result[@@SEARCH_RESULT_TITLE].split( ":" ).each() do |token|
1212
+ Strings.normalize!( token )
1213
+ end.join( ":" )
1214
+ return result[@@SEARCH_RESULT_URL] if normalized_target == normalized_result
1215
+ end
1216
+
1217
+ return nil
1218
+ end
1219
+
1220
+ end
1221
+
1222
+ def find_song_page_url( artist, title )
1223
+ return self.class.find_song_page_url( artist, title )
1224
+ end
1225
+
1226
+
1227
+
1228
+ # ALBUM FUNCTIONS
1229
+
1230
+ def MediaWikiLyrics.parse_album_link( link )
1231
+ article, display = parse_link( link )
1232
+ return nil, nil, nil if article == nil
1233
+ if (md = /^([^:]+):(.+) \(([0-9]{4,4})\)$/.match( article ))
1234
+ return md[1], md[2], md[3]
1235
+ else
1236
+ return nil, nil, nil
1237
+ end
1238
+ end
1239
+
1240
+ def parse_album_link( link )
1241
+ return self.class.parse_album_link( link )
1242
+ end
1243
+
1244
+ def MediaWikiLyrics.build_album_link( artist, album, year, cleanup=true )
1245
+ if cleanup
1246
+ artist = cleanup_title_token( artist )
1247
+ album = cleanup_title_token( album )
1248
+ end
1249
+ return build_link( "#{artist}:#{album} (#{year})" )
1250
+ end
1251
+
1252
+ def build_album_link( artist, album, year, cleanup=true )
1253
+ return self.class.build_album_link( artist, album, year, cleanup )
1254
+ end
1255
+
1256
+ def MediaWikiLyrics.parse_album_url( url )
1257
+ article = parse_url( url )
1258
+ return nil, nil, nil if article == nil
1259
+ if (md = /^([^:]+):(.+) \(([0-9]{4,4})\)$/.match( article ))
1260
+ return md[1], md[2], md[3]
1261
+ else
1262
+ return nil, nil, nil
1263
+ end
1264
+ end
1265
+
1266
+ def parse_album_url( url )
1267
+ return self.class.parse_album_url( url )
1268
+ end
1269
+
1270
+ def MediaWikiLyrics.build_album_url( artist, album, year, cleanup=true )
1271
+ if cleanup
1272
+ artist = cleanup_title_token( artist )
1273
+ album = cleanup_title_token( album )
1274
+ end
1275
+ return build_url( "#{artist}:#{album} (#{year})" )
1276
+ end
1277
+
1278
+ def build_album_url( artist, album, year, cleanup=true )
1279
+ return self.class.build_album_url( artist, album, year, cleanup )
1280
+ end
1281
+
1282
+ def MediaWikiLyrics.build_album_rawdata_url( artist, album, year, cleanup=true )
1283
+ return build_album_url( artist, album, year, cleanup ) + "&action=raw"
1284
+ end
1285
+
1286
+ def build_album_rawdata_url( artist, album, year, cleanup=true )
1287
+ return self.class.build_album_rawdata_url( artist, album, year, cleanup )
1288
+ end
1289
+
1290
+ def MediaWikiLyrics.build_album_edit_url( artist, album, year, cleanup=true )
1291
+ return build_album_url( artist, album, year, cleanup ) + "&action=edit"
1292
+ end
1293
+
1294
+ def build_album_edit_url( artist, album, year, cleanup=true )
1295
+ return self.class.build_album_edit_url( artist, album, year, cleanup )
1296
+ end
1297
+
1298
+ def MediaWikiLyrics.build_album_search_url( artist, album, year )
1299
+ artist = cleanup_title_token( artist )
1300
+ album = cleanup_title_token( album )
1301
+ search_string = CGI.escape( "#{artist}:#{album} (#{year})" )
1302
+ return "http://#{site_host()}/index.php?redirs=1&search=#{search_string}&fulltext=Search&limit=500"
1303
+ end
1304
+
1305
+ def build_album_search_url( artist, album, year )
1306
+ return self.class.build_album_search_url( artist, album, year )
1307
+ end
1308
+
1309
+ def MediaWikiLyrics.find_album_page_url( artist, album, year )
1310
+
1311
+ url = build_album_rawdata_url( artist, album, year )
1312
+ body, url = fetch_content_page( url )
1313
+
1314
+ if ! Strings.empty?( body ) # page exists
1315
+ return url
1316
+ else
1317
+ artist = Strings.normalize( cleanup_title_token( artist ) )
1318
+ album = Strings.normalize( cleanup_title_token( album ) )
1319
+ normalized_target = "#{artist}:#{album} #{year}" # NOTE: normalize removes the parenthesis
1320
+
1321
+ response, url = HTTP.fetch_page_get( build_album_search_url( artist, album, year ) )
1322
+ return nil if response == nil || response.body() == nil
1323
+ parse_search_results( response.body(), true ).each() do |result|
1324
+ normalized_result = result[@@SEARCH_RESULT_TITLE].split( ":" ).each() do |token|
1325
+ Strings.normalize!( token )
1326
+ end.join( ":" )
1327
+ return result[@@SEARCH_RESULT_URL] if normalized_target == normalized_result
1328
+ end
1329
+ return nil
1330
+ end
1331
+
1332
+ end
1333
+
1334
+ def find_album_page_url( artist, album, year )
1335
+ return self.class.find_album_page_url( artist, album, year )
1336
+ end
1337
+
1338
+
1339
+ # ALBUM ART FUNCTIONS
1340
+
1341
+ def build_album_art_name( artist, album, year, extension="jpg", cleanup=true )
1342
+ return self.class.build_album_art_name( artist, album, year, extension, cleanup )
1343
+ end
1344
+
1345
+ def build_album_art_description( artist, album, year, cleanup=true )
1346
+ return self.class.build_album_art_description( artist, album, year, cleanup )
1347
+ end
1348
+
1349
+ def find_album_art_name( artist, album, year )
1350
+ return self.class.find_album_art_name( artist, album, year )
1351
+ end
1352
+
1353
+ def MediaWikiLyrics.find_album_art_url( artist, album, year )
1354
+ if album_art_name = find_album_art_name( artist, album, year )
1355
+ album_art_name.gsub!( " ", "_" )
1356
+ return "http://#{site_host()}/index.php?title=Image:#{CGI.escape(album_art_name)}"
1357
+ else
1358
+ return nil
1359
+ end
1360
+ end
1361
+
1362
+ def find_album_art_url( artist, album, year )
1363
+ return self.class.find_album_art_url( artist, album, year )
1364
+ end
1365
+
1366
+
1367
+ # DATE FUNCTIONS
1368
+
1369
+ @@months = [
1370
+ "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY",
1371
+ "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"
1372
+ ]
1373
+
1374
+ def MediaWikiLyrics.parse_date( date )
1375
+
1376
+ day, month, year = nil, nil, nil
1377
+
1378
+ if (md = /^(#{@@months.join( "|" )})( \d\d?|), (\d\d\d\d)$/i.match( date ))
1379
+ month, day, year = @@months.index( Strings.upcase( md[1] ) ) + 1, md[2].strip().to_i(), md[3].to_i()
1380
+ elsif (md = /^(\d\d? |)(#{@@months.join( "|" )}) (\d\d\d\d)$/i.match( date ))
1381
+ day, month, year = md[1].strip().to_i(), @@months.index( Strings.upcase( md[2] ) ) + 1, md[3].to_i()
1382
+ elsif /^(\d\d\d\d)-(\d\d)-(\d\d)$/.match( date )
1383
+ year, month, day = md[1].to_i(), md[2].to_i(), md[3].to_i()
1384
+ elsif (md = /^(\d\d\d\d)$/.match( date ))
1385
+ year = md[1].to_i()
1386
+ end
1387
+
1388
+ year = nil if year == 0
1389
+ month = nil if month == 0
1390
+ day = nil if day == 0
1391
+
1392
+ return year, month, day
1393
+
1394
+ end
1395
+
1396
+ def MediaWikiLyrics.build_date( year, month, day )
1397
+
1398
+ year, month, day = year.to_i(), month.to_i(), day.to_i()
1399
+
1400
+ if month != 0
1401
+ month = @@months[month-1].to_s()
1402
+ month = month.slice( 0..0 ).upcase() + month.slice( 1..-1 ).downcase()
1403
+ end
1404
+
1405
+ if day != 0 && month != 0 && year != 0
1406
+ return "#{month} #{day}, #{year}"
1407
+ elsif month != 0 && year != 0
1408
+ return "#{month}, #{year}"
1409
+ elsif year != 0
1410
+ return year.to_s()
1411
+ else
1412
+ return ""
1413
+ end
1414
+
1415
+ end
1416
+
1417
+ end