lyrics-ebook 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. data/LICENSE +340 -0
  2. data/README +46 -0
  3. data/bin/lyrics-ebook +47 -0
  4. data/lib/eeepub/LICENSE +20 -0
  5. data/lib/eeepub/README.md +77 -0
  6. data/lib/eeepub/Rakefile +49 -0
  7. data/lib/eeepub/VERSION +1 -0
  8. data/lib/eeepub/examples/files/bar.html +42 -0
  9. data/lib/eeepub/examples/files/foo.html +42 -0
  10. data/lib/eeepub/examples/simple_epub.rb +25 -0
  11. data/lib/eeepub/lib/eeepub.rb +17 -0
  12. data/lib/eeepub/lib/eeepub/container_item.rb +108 -0
  13. data/lib/eeepub/lib/eeepub/easy.rb +100 -0
  14. data/lib/eeepub/lib/eeepub/maker.rb +127 -0
  15. data/lib/eeepub/lib/eeepub/ncx.rb +68 -0
  16. data/lib/eeepub/lib/eeepub/ocf.rb +112 -0
  17. data/lib/eeepub/lib/eeepub/opf.rb +148 -0
  18. data/lib/eeepub/spec/eeepub/easy_spec.rb +66 -0
  19. data/lib/eeepub/spec/eeepub/maker_spec.rb +124 -0
  20. data/lib/eeepub/spec/eeepub/ncx_spec.rb +80 -0
  21. data/lib/eeepub/spec/eeepub/ocf_spec.rb +43 -0
  22. data/lib/eeepub/spec/eeepub/opf_spec.rb +262 -0
  23. data/lib/eeepub/spec/eeepub_spec.rb +4 -0
  24. data/lib/eeepub/spec/spec_helper.rb +12 -0
  25. data/lib/lyrics_ebook.rb +71 -0
  26. data/lib/lyrics_ebook/cache2html.rb +33 -0
  27. data/lib/lyrics_ebook/cache_manager.rb +77 -0
  28. data/lib/lyrics_ebook/lyric.rb +32 -0
  29. data/lib/lyrics_ebook/lyric_download.rb +50 -0
  30. data/lib/lyrics_ebook/lyrics2html.rb +106 -0
  31. data/lib/lyrics_ebook/mini_lyrics_parser.rb +26 -0
  32. data/lib/lyrics_ebook/mp3_parser.rb +37 -0
  33. data/lib/wiki_lyrics/amarok/COPYING +340 -0
  34. data/lib/wiki_lyrics/amarok/README +51 -0
  35. data/lib/wiki_lyrics/amarok/amarok.rb +355 -0
  36. data/lib/wiki_lyrics/amarok/pluginadapter.rb +113 -0
  37. data/lib/wiki_lyrics/amarok/plugins.rb +95 -0
  38. data/lib/wiki_lyrics/amarok/wikilyrics.rb +578 -0
  39. data/lib/wiki_lyrics/amarok/wikilyrics.spec +4 -0
  40. data/lib/wiki_lyrics/amarok/wikipluginadapter.rb +392 -0
  41. data/lib/wiki_lyrics/cli/optionsparser.rb +228 -0
  42. data/lib/wiki_lyrics/cli/pluginadapter.rb +56 -0
  43. data/lib/wiki_lyrics/cli/plugins.rb +79 -0
  44. data/lib/wiki_lyrics/cli/wikilyrics.rb +178 -0
  45. data/lib/wiki_lyrics/cli/wikipluginadapter.rb +140 -0
  46. data/lib/wiki_lyrics/docs/COPYING +340 -0
  47. data/lib/wiki_lyrics/docs/ChangeLog +293 -0
  48. data/lib/wiki_lyrics/docs/HOWTO.pdf +0 -0
  49. data/lib/wiki_lyrics/docs/HOWTO.tex +151 -0
  50. data/lib/wiki_lyrics/docs/HOWTO.txt +139 -0
  51. data/lib/wiki_lyrics/docs/README +45 -0
  52. data/lib/wiki_lyrics/docs/TODO +12 -0
  53. data/lib/wiki_lyrics/gui/gui-gtk.rb +945 -0
  54. data/lib/wiki_lyrics/gui/gui-qt3.rb +785 -0
  55. data/lib/wiki_lyrics/gui/gui-qt4.rb +845 -0
  56. data/lib/wiki_lyrics/gui/gui-tk.rb +1104 -0
  57. data/lib/wiki_lyrics/gui/gui.rb +268 -0
  58. data/lib/wiki_lyrics/gui/test.rb +74 -0
  59. data/lib/wiki_lyrics/i18n/README +1 -0
  60. data/lib/wiki_lyrics/i18n/en.rb +181 -0
  61. data/lib/wiki_lyrics/i18n/es.rb +181 -0
  62. data/lib/wiki_lyrics/i18n/i18n.rb +126 -0
  63. data/lib/wiki_lyrics/i18n/sk.rb +174 -0
  64. data/lib/wiki_lyrics/itrans/COPYRIGHT +31 -0
  65. data/lib/wiki_lyrics/itrans/itrans +0 -0
  66. data/lib/wiki_lyrics/itrans/itrans.txt +8 -0
  67. data/lib/wiki_lyrics/itrans/lyric.txt +23 -0
  68. data/lib/wiki_lyrics/itrans/udvng.ifm +206 -0
  69. data/lib/wiki_lyrics/lyrics.rb +567 -0
  70. data/lib/wiki_lyrics/lyrics_AZLyrics.rb +113 -0
  71. data/lib/wiki_lyrics/lyrics_DarkLyrics.rb +124 -0
  72. data/lib/wiki_lyrics/lyrics_Giitaayan.rb +124 -0
  73. data/lib/wiki_lyrics/lyrics_Jamendo.rb +166 -0
  74. data/lib/wiki_lyrics/lyrics_LeosLyrics.rb +142 -0
  75. data/lib/wiki_lyrics/lyrics_LoudSongs.rb +135 -0
  76. data/lib/wiki_lyrics/lyrics_LyricWiki.rb +329 -0
  77. data/lib/wiki_lyrics/lyrics_LyricsDownload.rb +118 -0
  78. data/lib/wiki_lyrics/lyrics_LyricsMania.rb +141 -0
  79. data/lib/wiki_lyrics/lyrics_Lyriki.rb +287 -0
  80. data/lib/wiki_lyrics/lyrics_SeekLyrics.rb +108 -0
  81. data/lib/wiki_lyrics/lyrics_Sing365.rb +103 -0
  82. data/lib/wiki_lyrics/lyrics_TerraLetras.rb +126 -0
  83. data/lib/wiki_lyrics/mediawikilyrics.rb +1464 -0
  84. data/lib/wiki_lyrics/tests/plugin_tests.rb +161 -0
  85. data/lib/wiki_lyrics/tests/tests.rb +74 -0
  86. data/lib/wiki_lyrics/utils/formdata.rb +56 -0
  87. data/lib/wiki_lyrics/utils/htmlentities.rb +291 -0
  88. data/lib/wiki_lyrics/utils/http.rb +198 -0
  89. data/lib/wiki_lyrics/utils/itrans.rb +160 -0
  90. data/lib/wiki_lyrics/utils/kde.rb +88 -0
  91. data/lib/wiki_lyrics/utils/logger.rb +123 -0
  92. data/lib/wiki_lyrics/utils/strings.rb +378 -0
  93. data/lib/wiki_lyrics/utils/xmlhash.rb +111 -0
  94. data/lib/wiki_lyrics/wikilyrics.kdevelop +152 -0
  95. data/lib/wiki_lyrics/wikilyrics.kdevses +18 -0
  96. data/lib/wiki_lyrics/win/gtk_fix.bat +3 -0
  97. data/lib/wiki_lyrics/win/tk_fix.bat +3 -0
  98. data/lib/wiki_lyrics/win/wikilyrics.bat +4 -0
  99. data/lib/wiki_lyrics/win/win_fix.rb +52 -0
  100. data/templates/art_.erb.xhtml +29 -0
  101. data/templates/artists.erb.xhtml +19 -0
  102. data/templates/cover.erb.xhtml +22 -0
  103. data/test/cache_manager_test.rb +73 -0
  104. data/test/data/artist name/title name.lyric +6 -0
  105. data/test/lyric_test.rb +41 -0
  106. metadata +194 -0
@@ -0,0 +1,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