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