lyrics-ebook 0.1

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