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