MythTV  master
lyricsmode.py
Go to the documentation of this file.
1 #-*- coding: UTF-8 -*-
2 import sys
3 
4 try:
5  from urllib import urlopen, quote_plus
6 except ImportError:
7  from urllib.request import urlopen
8  from urllib.parse import quote_plus
9 
10 import re
11 from optparse import OptionParser
12 from common import utilities
13 
14 __author__ = "Paul Harrison and ronie'"
15 __title__ = "LyricsMode"
16 __description__ = "Search http://www.lyricsmode.com for lyrics"
17 __priority__ = "170"
18 __version__ = "0.1"
19 __syncronized__ = False
20 
21 debug = False
22 
24  def __init__( self ):
25  self.clean_lyrics_regex = re.compile( "<.+?>" )
26  self.normalize_lyrics_regex = re.compile( "&#[x]*(?P<name>[0-9]+);*" )
27  self.clean_br_regex = re.compile( "<br[ /]*>[\s]*", re.IGNORECASE )
28  self.search_results_regex = re.compile("<a href=\"[^\"]+\">([^<]+)</a></td>[^<]+<td><a href=\"([^\"]+)\" class=\"b\">[^<]+</a></td>", re.IGNORECASE)
29  self.next_results_regex = re.compile("<A href=\"([^\"]+)\" class=\"pages\">next .</A>", re.IGNORECASE)
30 
31  def get_lyrics(self, lyrics):
32  utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
33 
34  artist = utilities.deAccent(lyrics.artist)
35  title = utilities.deAccent(lyrics.title)
36  try: # below is borowed from XBMC Lyrics
37  url = "http://www.lyricsmode.com/lyrics/%s/%s/%s.html" % (artist.lower()[:1], artist.lower().replace("&","and").replace(" ","_"), title.lower().replace("&","and").replace(" ","_"))
38  lyrics_found = False
39  while True:
40  utilities.log(debug, "%s: search url: %s" % (__title__, url))
41  song_search = urlopen(url).read()
42  if song_search.find("<p id=\"lyrics_text\" class=\"ui-annotatable\">") >= 0:
43  break
44 
45  if lyrics_found:
46  # if we're here, we found the lyrics page but it didn't
47  # contains the lyrics part (licensing issue or some bug)
48  return False
49 
50  # Let's try to use the research box if we didn't yet
51  if not 'search' in url:
52  url = "http://www.lyricsmode.com/search.php?what=songs&s=" + quote_plus(title.lower())
53  else:
54  # the search gave more than on result, let's try to find our song
55  url = ""
56  start = song_search.find('<!--output-->')
57  end = song_search.find('<!--/output-->', start)
58  results = self.search_results_regex.findall(song_search, start, end)
59 
60  for result in results:
61  if result[0].lower() in artist.lower():
62  url = "http://www.lyricsmode.com" + result[1]
63  lyrics_found = True
64  break
65 
66  if not url:
67  # Is there a next page of results ?
68  match = self.next_results_regex.search(song_search[end:])
69  if match:
70  url = "http://www.lyricsmode.com/search.php" + match.group(1)
71  else:
72  return False
73 
74  lyr = song_search.split("<p id=\"lyrics_text\" class=\"ui-annotatable\">")[1].split('</p><p id=\"lyrics_text_selected\">')[0]
75  lyr = self.clean_br_regex.sub( "\n", lyr ).strip()
76  lyr = self.clean_lyrics_regex.sub( "", lyr ).strip()
77  lyr = self.normalize_lyrics_regex.sub( lambda m: unichr( int( m.group( 1 ) ) ), lyr.decode("ISO-8859-1") )
78  lir = []
79  for line in lyr.splitlines():
80  line.strip()
81  if line.find("Lyrics from:") < 0:
82  lir.append(line)
83  lyr = u"\n".join( lir )
84  if lyr.startswith('These lyrics are missing'):
85  return False
86  lyrics.lyrics = lyr
87  return True
88  except:
89  utilities.log(True, "%s: %s::%s (%d) [%s]" % (
90  __title__, self.__class__.__name__,
91  sys.exc_info()[ 2 ].tb_frame.f_code.co_name,
92  sys.exc_info()[ 2 ].tb_lineno,
93  sys.exc_info()[ 1 ]
94  ))
95  return False
96 
98  found = False
99  lyrics = utilities.Lyrics()
100  lyrics.source = __title__
101  lyrics.syncronized = __syncronized__
102  lyrics.artist = 'Dire Straits'
103  lyrics.album = 'Brothers In Arms'
104  lyrics.title = 'Money For Nothing'
105 
106  fetcher = LyricsFetcher()
107  found = fetcher.get_lyrics(lyrics)
108 
109  if found:
110  utilities.log(True, "Everything appears in order.")
111  buildLyrics(lyrics)
112  sys.exit(0)
113 
114  utilities.log(True, "The lyrics for the test search failed!")
115  sys.exit(1)
116 
117 def buildLyrics(lyrics):
118  from lxml import etree
119  xml = etree.XML(u'<lyrics></lyrics>')
120  etree.SubElement(xml, "artist").text = lyrics.artist
121  etree.SubElement(xml, "album").text = lyrics.album
122  etree.SubElement(xml, "title").text = lyrics.title
123  etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
124  etree.SubElement(xml, "grabber").text = lyrics.source
125 
126  lines = lyrics.lyrics.splitlines()
127  for line in lines:
128  etree.SubElement(xml, "lyric").text = line
129 
130  utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
131  pretty_print=True, xml_declaration=True)))
132  sys.exit(0)
133 
135  from lxml import etree
136  version = etree.XML(u'<grabber></grabber>')
137  etree.SubElement(version, "name").text = __title__
138  etree.SubElement(version, "author").text = __author__
139  etree.SubElement(version, "command").text = 'lyricsmode.py'
140  etree.SubElement(version, "type").text = 'lyrics'
141  etree.SubElement(version, "description").text = __description__
142  etree.SubElement(version, "version").text = __version__
143  etree.SubElement(version, "priority").text = __priority__
144  etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
145 
146  utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
147  pretty_print=True, xml_declaration=True)))
148  sys.exit(0)
149 
150 def main():
151  global debug
152 
153  parser = OptionParser()
154 
155  parser.add_option('-v', "--version", action="store_true", default=False,
156  dest="version", help="Display version and author")
157  parser.add_option('-t', "--test", action="store_true", default=False,
158  dest="test", help="Test grabber with a know good search")
159  parser.add_option('-s', "--search", action="store_true", default=False,
160  dest="search", help="Search for lyrics.")
161  parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
162  dest="artist", help="Artist of track.")
163  parser.add_option('-b', "--album", metavar="ALBUM", default=None,
164  dest="album", help="Album of track.")
165  parser.add_option('-n', "--title", metavar="TITLE", default=None,
166  dest="title", help="Title of track.")
167  parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
168  dest="filename", help="Filename of track.")
169  parser.add_option('-d', '--debug', action="store_true", default=False,
170  dest="debug", help=("Show debug messages"))
171 
172  opts, args = parser.parse_args()
173 
174  lyrics = utilities.Lyrics()
175  lyrics.source = __title__
176  lyrics.syncronized = __syncronized__
177 
178  if opts.debug:
179  debug = True
180 
181  if opts.version:
182  buildVersion()
183 
184  if opts.test:
186 
187  if opts.artist:
188  lyrics.artist = opts.artist
189  if opts.album:
190  lyrics.album = opts.album
191  if opts.title:
192  lyrics.title = opts.title
193  if opts.filename:
194  lyrics.filename = opts.filename
195 
196  fetcher = LyricsFetcher()
197  if fetcher.get_lyrics(lyrics):
198  buildLyrics(lyrics)
199  sys.exit(0)
200  else:
201  utilities.log(True, "No lyrics found for this track")
202  sys.exit(1)
203 
204 
205 if __name__ == '__main__':
206  main()
lyricsmode.LyricsFetcher.clean_br_regex
clean_br_regex
Definition: lyricsmode.py:27
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
lyricsmode.LyricsFetcher
Definition: lyricsmode.py:23
lyricsmode.LyricsFetcher.__init__
def __init__(self)
Definition: lyricsmode.py:24
lyricsmode.buildLyrics
def buildLyrics(lyrics)
Definition: lyricsmode.py:117
lyricsmode.LyricsFetcher.search_results_regex
search_results_regex
Definition: lyricsmode.py:28
lyricsmode.buildVersion
def buildVersion()
Definition: lyricsmode.py:134
lyricsmode.performSelfTest
def performSelfTest()
Definition: lyricsmode.py:97
lyricsmode.LyricsFetcher.next_results_regex
next_results_regex
Definition: lyricsmode.py:29
lyricsmode.LyricsFetcher.normalize_lyrics_regex
normalize_lyrics_regex
Definition: lyricsmode.py:26
lyricsmode.LyricsFetcher.get_lyrics
def get_lyrics(self, lyrics)
Definition: lyricsmode.py:31
lyricsmode.LyricsFetcher.clean_lyrics_regex
clean_lyrics_regex
Definition: lyricsmode.py:25
lyricsmode.main
def main()
Definition: lyricsmode.py:150