MythTV  master
embedlrc.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: UTF-8 -*-
3 # ----------------------
4 """
5 Scraper for embedded lyrics
6 """
7 
8 import sys, os, re, chardet
9 import xml.dom.minidom as xml
10 from optparse import OptionParser
11 from common import utilities
12 
13 __author__ = "Paul Harrison and 'ronin'"
14 __title__ = "EmbeddedLyrics"
15 __description__ = "Search tracks tag for embedded lyrics"
16 __version__ = "0.2"
17 __priority__ = "100"
18 __syncronized__ = True
19 
20 debug = False
21 
22 def getLyrics3(filename):
23  #Get lyrics embed with Lyrics3/Lyrics3V2 format
24  #See: http://id3.org/Lyrics3
25  #http://id3.org/Lyrics3v2
26 
27  utilities.log(debug, "%s: trying %s" % (__title__, "lyrics embed with Lyrics3/Lyrics3V2 format"))
28 
29  f = File(filename)
30  f.seek(-128-9, os.SEEK_END)
31  buf = f.read(9)
32  if (buf != "LYRICS200" and buf != "LYRICSEND"):
33  f.seek(-9, os.SEEK_END)
34  buf = f.read(9)
35  if (buf == "LYRICSEND"):
36  """ Find Lyrics3v1 """
37  f.seek(-5100-9-11, os.SEEK_CUR)
38  buf = f.read(5100+11)
39  f.close();
40  start = buf.find("LYRICSBEGIN")
41  elif (buf == "LYRICS200"):
42  """ Find Lyrics3v2 """
43  f.seek(-9-6, os.SEEK_CUR)
44  size = int(f.read(6))
45  f.seek(-size-6, os.SEEK_CUR)
46  buf = f.read(11)
47  if(buf == "LYRICSBEGIN"):
48  buf = f.read(size-11)
49  tags=[]
50  while buf!= '':
51  tag = buf[:3]
52  length = int(buf[3:8])
53  content = buf[8:8+length]
54  if (tag == 'LYR'):
55  return content
56  buf = buf[8+length:]
57  f.close();
58  return None
59 
60 def endOfString(string, utf16=False):
61  if (utf16):
62  pos = 0
63  while True:
64  pos += string[pos:].find('\x00\x00') + 1
65  if (pos % 2 == 1):
66  return pos - 1
67  else:
68  return string.find('\x00')
69 
70 def ms2timestamp(ms):
71  mins = "0%s" % int(ms/1000/60)
72  sec = "0%s" % int((ms/1000)%60)
73  msec = "0%s" % int((ms%1000)/10)
74  timestamp = "[%s:%s.%s]" % (mins[-2:],sec[-2:],msec[-2:])
75  return timestamp
76 
77 # Uses the high level interface in taglib to find the lyrics
78 # should work with all the tag formats supported by taglib that can have lyrics
79 def getLyricsGeneric(filename):
80  try:
81  import taglib
82  except:
83  utilities.log(True, "Failed to import taglib. This grabber requires "
84  "pytaglib TagLib bindings for Python. "
85  "https://github.com/supermihi/pytaglib")
86  return None
87 
88  try:
89  utilities.log(debug, "%s: trying to open %s" % (__title__, filename))
90  f = taglib.File(filename)
91 
92  # see if we can find a lyrics tag
93  for tag in f.tags:
94  if tag.startswith('LYRICS'):
95  return f.tags[tag][0]
96 
97  return None
98  except:
99  return None
100 
101 # Get USLT/SYLT/TXXX lyrics embed with ID3v2 format
102 # See: http://id3.org/id3v2.3.0
103 def getID3Lyrics(filename):
104  utilities.log(debug, "%s: trying %s" % (__title__, "lyrics embed with ID3v2 format"))
105 
106  # just use the generic taglib method for now
107  return getLyricsGeneric(filename)
108 
109 def getFlacLyrics(filename):
110  utilities.log(debug, "%s: trying %s" % (__title__, "lyrics embed with Flac format"))
111 
112  # just use the generic taglib method for now
113  return getLyricsGeneric(filename)
114 
115 def getMP4Lyrics(filename):
116  utilities.log(debug, "%s: trying %s" % (__title__, "lyrics embed with MP4 format"))
117 
118  # just use the generic taglib method for now
119  return getLyricsGeneric(filename)
120 
122 
123 
124  def get_lyrics(self, lyrics):
125  utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
126 
127  if utilities.IS_PY2:
128  filename = lyrics.filename.decode("utf-8")
129  else:
130  filename = lyrics.filename
131 
132  ext = os.path.splitext(filename)[1].lower()
133  lry = None
134 
135  try:
136  if ext == '.mp3':
137  lry = getLyrics3(filename)
138  except:
139  pass
140 
141  if lry:
142  enc = chardet.detect(lry)
143  lyrics.lyrics = lry.decode(enc['encoding'])
144  else:
145  if ext == '.mp3':
146  lry = getID3Lyrics(filename)
147  elif ext == '.flac':
148  lry = getFlacLyrics(filename)
149  elif ext == '.m4a':
150  lry = getMP4Lyrics(filename)
151  if not lry:
152  return False
153  lyrics.lyrics = lry
154 
155  return True
156 
157 
159  try:
160  import taglib
161  except:
162  utilities.log(True, "Failed to import taglib. This grabber requires "
163  "pytaglib ? TagLib bindings for Python. "
164  "https://github.com/supermihi/pytaglib")
165  sys.exit(1)
166 
167  found = False
168  lyrics = utilities.Lyrics()
169  lyrics.source = __title__
170  lyrics.syncronized = __syncronized__
171  lyrics.artist = 'Robb Benson'
172  lyrics.album = 'Demo Tracks'
173  lyrics.title = 'Lone Rock'
174  lyrics.filename = os.path.dirname(os.path.abspath(__file__)) + '/examples/taglyrics.mp3'
175  fetcher = LyricsFetcher()
176  found = fetcher.get_lyrics(lyrics)
177 
178  if found:
179  utilities.log(True, "Everything appears in order.")
180  buildLyrics(lyrics)
181  sys.exit(0)
182 
183  utilities.log(True, "The lyrics for the test search failed!")
184  sys.exit(1)
185 
186 def buildLyrics(lyrics):
187  from lxml import etree
188  xml = etree.XML(u'<lyrics></lyrics>')
189  etree.SubElement(xml, "artist").text = lyrics.artist
190  etree.SubElement(xml, "album").text = lyrics.album
191  etree.SubElement(xml, "title").text = lyrics.title
192  etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
193  etree.SubElement(xml, "grabber").text = lyrics.source
194 
195  lines = lyrics.lyrics.splitlines()
196  for line in lines:
197  etree.SubElement(xml, "lyric").text = line
198 
199  utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
200  pretty_print=True, xml_declaration=True)))
201  sys.exit(0)
202 
204  from lxml import etree
205  version = etree.XML(u'<grabber></grabber>')
206  etree.SubElement(version, "name").text = __title__
207  etree.SubElement(version, "author").text = __author__
208  etree.SubElement(version, "command").text = 'alsong.py'
209  etree.SubElement(version, "type").text = 'lyrics'
210  etree.SubElement(version, "description").text = __description__
211  etree.SubElement(version, "version").text = __version__
212  etree.SubElement(version, "priority").text = __priority__
213  etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
214 
215  utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
216  pretty_print=True, xml_declaration=True)))
217  sys.exit(0)
218 
219 def main():
220  global debug
221 
222  parser = OptionParser()
223 
224  parser.add_option('-v', "--version", action="store_true", default=False,
225  dest="version", help="Display version and author")
226  parser.add_option('-t', "--test", action="store_true", default=False,
227  dest="test", help="Perform self-test for dependencies.")
228  parser.add_option('-s', "--search", action="store_true", default=False,
229  dest="search", help="Search for lyrics.")
230  parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
231  dest="artist", help="Artist of track.")
232  parser.add_option('-b', "--album", metavar="ALBUM", default=None,
233  dest="album", help="Album of track.")
234  parser.add_option('-n', "--title", metavar="TITLE", default=None,
235  dest="title", help="Title of track.")
236  parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
237  dest="filename", help="Filename of track.")
238  parser.add_option('-d', '--debug', action="store_true", default=False,
239  dest="debug", help=("Show debug messages"))
240 
241  opts, args = parser.parse_args()
242 
243  lyrics = utilities.Lyrics()
244  lyrics.source = __title__
245  lyrics.syncronized = __syncronized__
246 
247  if opts.debug:
248  debug = True
249 
250  if opts.version:
251  buildVersion()
252 
253  if opts.test:
255 
256  if opts.artist:
257  lyrics.artist = opts.artist
258  if opts.album:
259  lyrics.album = opts.album
260  if opts.title:
261  lyrics.title = opts.title
262  if opts.filename:
263  lyrics.filename = opts.filename
264 
265  fetcher = LyricsFetcher()
266  if fetcher.get_lyrics(lyrics):
267  buildLyrics(lyrics)
268  sys.exit(0)
269  else:
270  utilities.log(True, "No lyrics found for this track")
271  sys.exit(1)
272 
273 if __name__ == '__main__':
274  main()
def getMP4Lyrics(filename)
Definition: embedlrc.py:115
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
def getLyricsGeneric(filename)
Definition: embedlrc.py:79
if(query.exec() &&query.next())
def buildLyrics(lyrics)
Definition: embedlrc.py:186
def main()
Definition: embedlrc.py:219
def ms2timestamp(ms)
Definition: embedlrc.py:70
def performSelfTest()
Definition: embedlrc.py:158
def getID3Lyrics(filename)
Definition: embedlrc.py:103
def getLyrics3(filename)
Definition: embedlrc.py:22
def buildVersion()
Definition: embedlrc.py:203
def endOfString(string, utf16=False)
Definition: embedlrc.py:60
def getFlacLyrics(filename)
Definition: embedlrc.py:109
def get_lyrics(self, lyrics)
Definition: embedlrc.py:124