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  filename = lyrics.filename.decode("utf-8")
128  ext = os.path.splitext(filename)[1].lower()
129  lry = None
130 
131  try:
132  if ext == '.mp3':
133  lry = getLyrics3(filename)
134  except:
135  pass
136 
137  if lry:
138  enc = chardet.detect(lry)
139  lyrics.lyrics = lry.decode(enc['encoding'])
140  else:
141  if ext == '.mp3':
142  lry = getID3Lyrics(filename)
143  elif ext == '.flac':
144  lry = getFlacLyrics(filename)
145  elif ext == '.m4a':
146  lry = getMP4Lyrics(filename)
147  if not lry:
148  return False
149  lyrics.lyrics = lry
150 
151  return True
152 
153 
155  try:
156  import taglib
157  except:
158  utilities.log(True, "Failed to import taglib. This grabber requires "
159  "pytaglib ? TagLib bindings for Python. "
160  "https://github.com/supermihi/pytaglib")
161  sys.exit(1)
162 
163  found = False
164  lyrics = utilities.Lyrics()
165  lyrics.source = __title__
166  lyrics.syncronized = __syncronized__
167  lyrics.artist = 'Robb Benson'
168  lyrics.album = 'Demo Tracks'
169  lyrics.title = 'Lone Rock'
170  lyrics.filename = os.path.dirname(os.path.abspath(__file__)) + '/examples/taglyrics.mp3'
171  fetcher = LyricsFetcher()
172  found = fetcher.get_lyrics(lyrics)
173 
174  if found:
175  utilities.log(True, "Everything appears in order.")
176  sys.exit(0)
177 
178  utilities.log(True, "The lyrics for the test search failed!")
179  sys.exit(1)
180 
181 def buildLyrics(lyrics):
182  from lxml import etree
183  xml = etree.XML(u'<lyrics></lyrics>')
184  etree.SubElement(xml, "artist").text = lyrics.artist
185  etree.SubElement(xml, "album").text = lyrics.album
186  etree.SubElement(xml, "title").text = lyrics.title
187  etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
188  etree.SubElement(xml, "grabber").text = lyrics.source
189 
190  lines = lyrics.lyrics.splitlines()
191  for line in lines:
192  etree.SubElement(xml, "lyric").text = line
193 
194  utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
195  xml_declaration=True))
196  sys.exit(0)
197 
199  from lxml import etree
200  version = etree.XML(u'<grabber></grabber>')
201  etree.SubElement(version, "name").text = __title__
202  etree.SubElement(version, "author").text = __author__
203  etree.SubElement(version, "command").text = 'alsong.py'
204  etree.SubElement(version, "type").text = 'lyrics'
205  etree.SubElement(version, "description").text = __description__
206  etree.SubElement(version, "version").text = __version__
207  etree.SubElement(version, "priority").text = __priority__
208  etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
209 
210  utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
211  xml_declaration=True))
212  sys.exit(0)
213 
214 def main():
215  global debug
216 
217  parser = OptionParser()
218 
219  parser.add_option('-v', "--version", action="store_true", default=False,
220  dest="version", help="Display version and author")
221  parser.add_option('-t', "--test", action="store_true", default=False,
222  dest="test", help="Perform self-test for dependencies.")
223  parser.add_option('-s', "--search", action="store_true", default=False,
224  dest="search", help="Search for lyrics.")
225  parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
226  dest="artist", help="Artist of track.")
227  parser.add_option('-b', "--album", metavar="ALBUM", default=None,
228  dest="album", help="Album of track.")
229  parser.add_option('-n', "--title", metavar="TITLE", default=None,
230  dest="title", help="Title of track.")
231  parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
232  dest="filename", help="Filename of track.")
233  parser.add_option('-d', '--debug', action="store_true", default=False,
234  dest="debug", help=("Show debug messages"))
235 
236  opts, args = parser.parse_args()
237 
238  lyrics = utilities.Lyrics()
239  lyrics.source = __title__
240  lyrics.syncronized = __syncronized__
241 
242  if opts.debug:
243  debug = True
244 
245  if opts.version:
246  buildVersion()
247 
248  if opts.test:
250 
251  if opts.artist:
252  lyrics.artist = opts.artist
253  if opts.album:
254  lyrics.album = opts.album
255  if opts.title:
256  lyrics.title = opts.title
257  if opts.filename:
258  lyrics.filename = opts.filename
259 
260  fetcher = LyricsFetcher()
261  if fetcher.get_lyrics(lyrics):
262  buildLyrics(lyrics)
263  sys.exit(0)
264  else:
265  utilities.log(True, "No lyrics found for this track")
266  sys.exit(1)
267 
268 if __name__ == '__main__':
269  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
def buildLyrics(lyrics)
Definition: embedlrc.py:181
def main()
Definition: embedlrc.py:214
def ms2timestamp(ms)
Definition: embedlrc.py:70
def performSelfTest()
Definition: embedlrc.py:154
def getID3Lyrics(filename)
Definition: embedlrc.py:103
def getLyrics3(filename)
Definition: embedlrc.py:22
def buildVersion()
Definition: embedlrc.py:198
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