MythTV  master
ttplayer.py
Go to the documentation of this file.
1 # -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
2 """
3 Scraper for http://lrcct2.ttplayer.com/
4 
5 taxigps
6 """
7 
8 import os
9 import sys
10 import socket
11 
12 try:
13  from urllib import urlopen
14 except ImportError:
15  from urllib.request import urlopen
16 
17 import re
18 import chardet
19 import random
20 import difflib
21 from optparse import OptionParser
22 from common import utilities
23 
24 __author__ = "Paul Harrison and 'taxigps'"
25 __title__ = "TTPlayer"
26 __description__ = "Search http://lrcct2.ttplayer.com for lyrics"
27 __priority__ = "110"
28 __version__ = "0.1"
29 __syncronized__ = True
30 
31 debug = False
32 
33 socket.setdefaulttimeout(10)
34 
35 LYRIC_TITLE_STRIP=["\‍(live[^\‍)]*\‍)", "\‍(acoustic[^\‍)]*\‍)",
36  "\‍([^\‍)]*mix\‍)", "\‍([^\‍)]*version\‍)",
37  "\‍([^\‍)]*edit\‍)", "\‍(feat[^\‍)]*\‍)"]
38 LYRIC_TITLE_REPLACE=[("/", "-"),(" & ", " and ")]
39 LYRIC_ARTIST_REPLACE=[("/", "-"),(" & ", " and ")]
40 
41 class ttpClient(object):
42  '''
43  privide ttplayer specific function, such as encoding artist and title,
44  generate a Id code for server authorizition.
45  (see http://ttplyrics.googlecode.com/svn/trunk/crack)
46  '''
47  @staticmethod
48  def CodeFunc(Id, data):
49  '''
50  Generate a Id Code
51  These code may be ugly coz it is translated
52  from C code which is translated from asm code
53  grabed by ollydbg from ttp_lrcs.dll.
54  (see http://ttplyrics.googlecode.com/svn/trunk/crack)
55  '''
56  length = len(data)
57 
58  tmp2=0
59  tmp3=0
60 
61  tmp1 = (Id & 0x0000FF00) >> 8 #??8???x0000015F
62 
63  #tmp1 0x0000005F
64  if ( (Id & 0x00FF0000) == 0 ):
65  tmp3 = 0x000000FF & ~tmp1 #CL 0x000000E7
66  else:
67  tmp3 = 0x000000FF & ((Id & 0x00FF0000) >> 16) #??16??x00000001
68 
69  tmp3 = tmp3 | ((0x000000FF & Id) << 8) #tmp3 0x00001801
70  tmp3 = tmp3 << 8 #tmp3 0x00180100
71  tmp3 = tmp3 | (0x000000FF & tmp1) #tmp3 0x0018015F
72  tmp3 = tmp3 << 8 #tmp3 0x18015F00
73  if ( (Id & 0xFF000000) == 0 ) :
74  tmp3 = tmp3 | (0x000000FF & (~Id)) #tmp3 0x18015FE7
75  else :
76  tmp3 = tmp3 | (0x000000FF & (Id >> 24)) #??24???0x00000000
77 
78  #tmp3 18015FE7
79 
80  i=length-1
81  while(i >= 0):
82  char = ord(data[i])
83  if char >= 0x80:
84  char = char - 0x100
85  tmp1 = (char + tmp2) & 0x00000000FFFFFFFF
86  tmp2 = (tmp2 << (i%2 + 4)) & 0x00000000FFFFFFFF
87  tmp2 = (tmp1 + tmp2) & 0x00000000FFFFFFFF
88  #tmp2 = (ord(data[i])) + tmp2 + ((tmp2 << (i%2 + 4)) & 0x00000000FFFFFFFF)
89  i -= 1
90 
91  #tmp2 88203cc2
92  i=0
93  tmp1=0
94  while(i<=length-1):
95  char = ord(data[i])
96  if char >= 128:
97  char = char - 256
98  tmp7 = (char + tmp1) & 0x00000000FFFFFFFF
99  tmp1 = (tmp1 << (i%2 + 3)) & 0x00000000FFFFFFFF
100  tmp1 = (tmp1 + tmp7) & 0x00000000FFFFFFFF
101  #tmp1 = (ord(data[i])) + tmp1 + ((tmp1 << (i%2 + 3)) & 0x00000000FFFFFFFF)
102  i += 1
103 
104  #EBX 5CC0B3BA
105 
106  #EDX = EBX | Id
107  #EBX = EBX | tmp3
108  tmp1 = (((((tmp2 ^ tmp3) & 0x00000000FFFFFFFF) + (tmp1 | Id)) & 0x00000000FFFFFFFF) * (tmp1 | tmp3)) & 0x00000000FFFFFFFF
109  tmp1 = (tmp1 * (tmp2 ^ Id)) & 0x00000000FFFFFFFF
110 
111  if tmp1 > 0x80000000:
112  tmp1 = tmp1 - 0x100000000
113  return tmp1
114 
115  @staticmethod
116  def EncodeArtTit(mystr):
117  rtn = ''
118  if utilities.IS_PY2:
119  uni = unicode(mystr, 'UTF-8')
120  mystr = uni.encode('UTF-16')[2:]
121  for i in range(len(mystr)):
122  rtn += '%02x' % ord(mystr[i])
123  else:
124  uni = str(mystr)
125  mystr = uni.encode('UTF-16')[2:]
126  for i in range(len(mystr)):
127  rtn += '%02x' % (mystr[i])
128  return rtn
129 
130 
132  def __init__( self ):
133  self.LIST_URL = 'http://ttlrccnc.qianqian.com/dll/lyricsvr.dll?sh?Artist=%s&Title=%s&Flags=0'
134  self.LYRIC_URL = 'http://ttlrccnc.qianqian.com/dll/lyricsvr.dll?dl?Id=%d&Code=%d&uid=01&mac=%012x'
135 
136  def get_lyrics(self, lyrics):
137  utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
138 
139  # replace ampersands and the like
140  for exp in LYRIC_ARTIST_REPLACE:
141  p = re.compile(exp[0])
142  artist = p.sub(exp[1], lyrics.artist)
143  for exp in LYRIC_TITLE_REPLACE:
144  p = re.compile(exp[0])
145  title = p.sub(exp[1], lyrics.title)
146 
147  # strip things like "(live at Somewhere)", "(accoustic)", etc
148  for exp in LYRIC_TITLE_STRIP:
149  p = re.compile(exp)
150  title = p.sub('', title)
151 
152  # compress spaces
153  title = title.strip().replace('`','').replace('/','')
154  artist = artist.strip().replace('`','').replace('/','')
155 
156  try:
157  url = self.LIST_URL %(ttpClient.EncodeArtTit(artist.replace(' ','').lower()), ttpClient.EncodeArtTit(title.replace(' ','').lower()))
158  f = urlopen(url)
159  Page = f.read().decode('utf-8')
160  except:
161  utilities.log(True, "%s: %s::%s (%d) [%s]" % (
162  __title__, self.__class__.__name__,
163  sys.exc_info()[ 2 ].tb_frame.f_code.co_name,
164  sys.exc_info()[ 2 ].tb_lineno,
165  sys.exc_info()[ 1 ]
166  ))
167  return False
168 
169  links_query = re.compile('<lrc id=\"(.*?)\" artist=\"(.*?)\" title=\"(.*?)\"></lrc>')
170  urls = re.findall(links_query, Page)
171  links = []
172  for x in urls:
173  if (difflib.SequenceMatcher(None, artist.lower(), x[1].lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), x[2].lower()).ratio() > 0.8):
174  links.append( ( x[1] + ' - ' + x[2], x[0], x[1], x[2] ) )
175  if len(links) == 0:
176  return False
177  elif len(links) > 1:
178  lyrics.list = links
179  for link in links:
180  lyr = self.get_lyrics_from_list(link)
181  if lyr and lyr.startswith(b'['):
182  enc = chardet.detect(lyr)
183  lyr = lyr.decode(enc['encoding'], 'ignore')
184  lyrics.lyrics = lyr
185  return True
186  return False
187 
188  def get_lyrics_from_list(self, link):
189  title,Id,artist,song = link
190  utilities.log(debug, '%s %s %s' %(Id, artist, song))
191  try:
192  url = self.LYRIC_URL %(int(Id),ttpClient.CodeFunc(int(Id), artist + song), random.randint(0,0xFFFFFFFFFFFF))
193  f = urlopen(url)
194  Page = f.read()
195  except:
196  utilities.log(True, "%s: %s::%s (%d) [%s]" % (
197  __title__, self.__class__.__name__,
198  sys.exc_info()[ 2 ].tb_frame.f_code.co_name,
199  sys.exc_info()[ 2 ].tb_lineno,
200  sys.exc_info()[ 1 ]
201  ))
202  return None
203  if Page.startswith(b'['):
204  return Page
205  return ''
206 
208  found = False
209  lyrics = utilities.Lyrics()
210  lyrics.source = __title__
211  lyrics.syncronized = __syncronized__
212  lyrics.artist = 'Dire Straits'
213  lyrics.album = 'Brothers In Arms'
214  lyrics.title = 'Money For Nothing'
215 
216  fetcher = LyricsFetcher()
217  found = fetcher.get_lyrics(lyrics)
218 
219  if found:
220  utilities.log(True, "Everything appears in order.")
221  buildLyrics(lyrics)
222  sys.exit(0)
223 
224  utilities.log(True, "The lyrics for the test search failed!")
225  sys.exit(1)
226 
227 def buildLyrics(lyrics):
228  from lxml import etree
229  xml = etree.XML(u'<lyrics></lyrics>')
230  etree.SubElement(xml, "artist").text = lyrics.artist
231  etree.SubElement(xml, "album").text = lyrics.album
232  etree.SubElement(xml, "title").text = lyrics.title
233  etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
234  etree.SubElement(xml, "grabber").text = lyrics.source
235 
236  lines = lyrics.lyrics.splitlines()
237  for line in lines:
238  etree.SubElement(xml, "lyric").text = line
239 
240  utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
241  pretty_print=True, xml_declaration=True)))
242  sys.exit(0)
243 
245  from lxml import etree
246  version = etree.XML(u'<grabber></grabber>')
247  etree.SubElement(version, "name").text = __title__
248  etree.SubElement(version, "author").text = __author__
249  etree.SubElement(version, "command").text = 'ttplayer.py'
250  etree.SubElement(version, "type").text = 'lyrics'
251  etree.SubElement(version, "description").text = __description__
252  etree.SubElement(version, "version").text = __version__
253  etree.SubElement(version, "priority").text = __priority__
254  etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
255 
256  utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
257  pretty_print=True, xml_declaration=True)))
258  sys.exit(0)
259 
260 def main():
261  global debug
262 
263  parser = OptionParser()
264 
265  parser.add_option('-v', "--version", action="store_true", default=False,
266  dest="version", help="Display version and author")
267  parser.add_option('-t', "--test", action="store_true", default=False,
268  dest="test", help="Test grabber with a know good search")
269  parser.add_option('-s', "--search", action="store_true", default=False,
270  dest="search", help="Search for lyrics.")
271  parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
272  dest="artist", help="Artist of track.")
273  parser.add_option('-b', "--album", metavar="ALBUM", default=None,
274  dest="album", help="Album of track.")
275  parser.add_option('-n', "--title", metavar="TITLE", default=None,
276  dest="title", help="Title of track.")
277  parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
278  dest="filename", help="Filename of track.")
279  parser.add_option('-d', '--debug', action="store_true", default=False,
280  dest="debug", help=("Show debug messages"))
281 
282  opts, args = parser.parse_args()
283 
284  lyrics = utilities.Lyrics()
285  lyrics.source = __title__
286  lyrics.syncronized = __syncronized__
287 
288  if opts.debug:
289  debug = True
290 
291  if opts.version:
292  buildVersion()
293 
294  if opts.test:
296 
297  if opts.artist:
298  lyrics.artist = opts.artist
299  if opts.album:
300  lyrics.album = opts.album
301  if opts.title:
302  lyrics.title = opts.title
303  if opts.filename:
304  lyrics.filename = opts.filename
305 
306  fetcher = LyricsFetcher()
307  if fetcher.get_lyrics(lyrics):
308  buildLyrics(lyrics)
309  sys.exit(0)
310  else:
311  utilities.log(True, "No lyrics found for this track")
312  sys.exit(1)
313 
314 if __name__ == '__main__':
315  main()
ttplayer.LyricsFetcher.LYRIC_URL
LYRIC_URL
Definition: ttplayer.py:134
ttplayer.performSelfTest
def performSelfTest()
Definition: ttplayer.py:207
ttplayer.LyricsFetcher.LIST_URL
LIST_URL
Definition: ttplayer.py:133
ttplayer.LyricsFetcher.get_lyrics
def get_lyrics(self, lyrics)
Definition: ttplayer.py:136
ttplayer.ttpClient
Definition: ttplayer.py:41
ttplayer.LyricsFetcher.get_lyrics_from_list
def get_lyrics_from_list(self, link)
Definition: ttplayer.py:188
decode
static int decode(unsigned char *vbiline, int scale0, int scale1)
Definition: cc.cpp:69
ttplayer.main
def main()
Definition: ttplayer.py:260
musicbrainzngs.compat.unicode
unicode
Definition: compat.py:50
ttplayer.buildVersion
def buildVersion()
Definition: ttplayer.py:244
ttplayer.buildLyrics
def buildLyrics(lyrics)
Definition: ttplayer.py:227
ttplayer.ttpClient.EncodeArtTit
def EncodeArtTit(mystr)
Definition: ttplayer.py:116
ttplayer.ttpClient.CodeFunc
def CodeFunc(Id, data)
Definition: ttplayer.py:48
ttplayer.LyricsFetcher.__init__
def __init__(self)
Definition: ttplayer.py:132
ttplayer.LyricsFetcher
Definition: ttplayer.py:131