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