document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. #!/usr/bin/env python
  2. #
  3. # "blogava" - blogger avatar fetch, and gradient data URI generator
  4. # v1 2.11.2011
  5. # v2 ..
  6. # v3 11.11.2011
  7. # v4 14.11.2011
  8. #    ?c1=f8dd99&c2=eeaa00&w=1&h=20 create gradient png
  9. #    optional: ?download or ?js[=variablename]
  10. # v4.01 14.12.2011 -
  11. #    blogger new profile page default profile without server-part,
  12. #    added code to deal with that
  13. # v4.02 18.12.2001 -
  14. #    increased timeout 5->15 in blogger profile pages, handle downloaderrors
  15. #
  16. # fetch profile image from blogger user id. usage:
  17. # http://avafavico.appspot.com/?userid=01234567890
  18. #
  19. # See http://yabtb.blogspot.com/2011/11/google-app-engine-python-application.html
  20. #
  21. # This is like my first python app, so it may not be too shiny... but it works
  22. #
  23. #   - MS-potilas
  24. #
  25.  
  26. import sys
  27. import cgi
  28. import re
  29. import base64
  30. from google.appengine.api import images
  31. from google.appengine.api import urlfetch
  32. from google.appengine.api import memcache
  33. from google.appengine.runtime import DeadlineExceededError
  34.  
  35. #
  36. # png & gradient code based on:
  37. # http://jtauber.com/blog/2008/05/18/creating_gradients_programmatically_in_python/
  38. #
  39.  
  40. #########################################
  41.  
  42. def make_png(width, height, rgba_func):
  43.   import zlib
  44.   import struct
  45.   import array
  46.  
  47.   rotate = False
  48.   if height < width:
  49.     width, height = height, width
  50.     rotate = True
  51.  
  52.   def make_chunk(chunk_type, data):
  53.     chu = struct.pack(\'!I\', len(data)) + chunk_type + data
  54.     checksum = zlib.crc32(data, zlib.crc32(chunk_type))
  55.     chu += struct.pack(\'!I\', 0xFFFFFFFF & checksum)
  56.     return chu
  57.  
  58.   def frange(x):
  59.     for i in xrange(x):
  60.       yield i/float(x)
  61.  
  62.   def get_data(width, height, rgba_func):
  63.     data = array.array(\'B\')
  64.     for y in frange(height):
  65.       data.append(0)
  66.       for x in frange(width):
  67.         data.extend(int(round(v * 255)) for v in rgba_func(x, y))
  68.     return zlib.compress(data)
  69.  
  70.   out = array.array(\'B\', [137, 80, 78, 71, 13, 10, 26, 10]).tostring() # PNG signature
  71.   color_type = 6 if len(list(rgba_func(0,0))) == 4 else 2
  72.   out += make_chunk(\'IHDR\', struct.pack(\'!2I5B\', width, height, 8, color_type, 0, 0, 0))
  73.   out += make_chunk(\'IDAT\', get_data(width, height, rgba_func))
  74.   out += make_chunk(\'IEND\', \'\')
  75.   img = images.Image(out)
  76.    
  77.   img.rotate(270)     # rotate it around to optimize
  78.   if not rotate:      # not rotate -> do full circle
  79.     img.rotate(90)
  80.   result = img.execute_transforms(output_encoding=images.PNG)
  81.   return result
  82.  
  83. def linear_gradient(start_value, stop_value, start_offset=0.0, stop_offset=1.0):
  84.   return lambda offset: (start_value + ((offset - start_offset) / (stop_offset - start_offset) * (stop_value - start_value))) / 255.0
  85.  
  86. def gradient(segments):
  87.   def gradient_function(x, y):
  88.     segment_start = 0.0
  89.     for segment_end, start, end in segments:
  90.       if y < segment_end:
  91.         return (linear_gradient(start[i], end[i], segment_start, segment_end)(y) for i in xrange(len(start)))
  92.       segment_start = segment_end
  93.   return gradient_function
  94.  
  95. #########################################
  96.  
  97. def getTagAttr(html, tagname, findstring, attr):
  98. # from html, find tag tagname containing findstring, from that tag return attribute attr
  99.   found = re.search(\'<\'+tagname+\' [^>]*\'+findstring+\'[^>]*\'+attr+\'=[^>]*>\', html)
  100.   if found is None:
  101.     found = re.search(\'<\'+tagname+\' [^>]*\'+attr+\'=[^>]*\'+findstring+\'[^>]*>\', html)
  102.   if found is not None:
  103.     found = re.search(attr+\'=["\\\'](.+?)["\\\']\', found.group(0))
  104.     if found is not None:
  105.       return found.group(1)
  106.   return None
  107.  
  108. def getFavico(domain):
  109. # get favico for domain. first check cache
  110.   result = memcache.get(key=domain)
  111.   if result is None:
  112.     result = fetchUrl("http://www.google.com/s2/favicons?domain=" + domain)
  113.     memcache.add(key=domain, value=result, time=14400)
  114.   return result
  115.  
  116. def fetchUrl(url):
  117.   url = re.sub("^//", "http://", url);
  118.   dline = 5
  119.   if re.match("http:\\/\\/www\\.blogger.com\\/profile\\/.+", url):
  120.     dline = 15
  121.   try:
  122.     result = urlfetch.fetch(url, deadline=dline)
  123.   except urlfetch.DownloadError:
  124.     return None
  125.   if result.status_code == 200:
  126.     return result.content
  127.   return None
  128.  
  129. #########################################
  130.  
  131. def main():
  132.   form = cgi.FieldStorage(keep_blank_values = True)
  133.   c1 = form.getfirst("c1")
  134.   c2 = form.getfirst("c2")
  135.   w = form.getfirst("w")
  136.   h = form.getfirst("h")
  137.  
  138.   if w is None or not re.match(\'\\d+$\', w):
  139.     w = 0
  140.   if h is None or not re.match(\'\\d+$\', h):
  141.     h = 0
  142.   if int(w)*int(h) > 4096:
  143.     print "Status: 400 Bad Request";
  144.     print
  145.     print "dimensions (w x h) too large"
  146.     return 1
  147.  
  148.   if c1 is not None and c2 is not None:
  149.     if re.match(\'[a-f0-9]{6}$\', c1, re.I) and re.match(\'[a-f0-9]{6}$\', c2, re.I) and int(w) > 0 and int(h) > 0:
  150.       c1r = int(c1[:2],16)
  151.       c1g = int(c1[2:][:2],16)
  152.       c1b = int(c1[-2:],16)
  153.       c2r = int(c2[:2],16)
  154.       c2g = int(c2[2:][:2],16)
  155.       c2b = int(c2[-2:],16)
  156.     else:
  157.       print "Status: 400 Bad Request";
  158.       print
  159.       print "invalid parameters"
  160.       return 1
  161.  
  162.     img = make_png(int(w), int(h), gradient([ (1.0, (c1r, c1g, c1b), (c2r, c2g, c2b)), ]))
  163.  
  164.     if form.getfirst("download") is not None:
  165.       print "Content-Type: image/png"
  166.       print "Cache-Control: public, max-age=86400"
  167.       print
  168.       print img
  169.       return 0
  170.     imgenc = base64.b64encode(img);
  171.     if form.getfirst("js") is not None:
  172.       var = form.getfirst("js")
  173.       if not re.match(\'[a-zA-Z]+$\', var):
  174.         var = "result"
  175.      
  176.       print "Content-Type: text/javascript"
  177.       print "Cache-Control: public, max-age=86400"
  178.       print
  179.       print \'var \' + var + \'="\'+imgenc+\'";\'
  180.       return 0
  181.  
  182.     imgenc = \'data:image/png;base64,\' + imgenc
  183.     print "Content-Type: text/html"
  184.     print "Cache-Control: public, max-age=86400"
  185.     print
  186.     print \'<html><head><link rel="shortcut icon" href="/favicon.ico" /><title>Gradient PNG data URI generator</title></head><body style="text-align:center;">\'
  187.     print \'Gradient from #\' + c1 + \' to #\' + c2 + \', width=\'+w+\', height=\'+h+\':<br />\'
  188.     print \'<textarea id="pngarea" rows="\' + str(round(len(imgenc)/64+2))+ \'" cols="64">\'+imgenc+\'</textarea>\'
  189.     print \'<script>document.getElementById("pngarea").focus();document.getElementById("pngarea").select();</script>\'
  190.     print \'<div style="margin:10px"><a title="Click to open data URI" target="_top" href="\'+imgenc+\'"><img src="\'+imgenc+\'" style="padding:10px;border:1px solid #d0d0d0;" /></a></div>\'
  191.     print \'Use it for example like this in CSS:<br /><div style="margin-left:auto;margin-right:auto;width:520px;text-align:left;font-family:monospace;word-wrap:break-word;font-size:90%;margin-bottom:12px;">\'
  192.     print \'background:url(\'+imgenc+\') 100% 100%;\'
  193.     print \'</div>\'
  194.     print \'<hr />\'
  195.     print \'<div style="font-size:85%">by MS-potilas 2011, see <a target="_top" href="http://yabtb.blogspot.com/2011/11/gradient-png-data-uri-maker-reference.html">yabtb.blogspot.com</a>.</div>\'
  196.     print \'</body></html>\'
  197.     return 0
  198.  
  199.   userid = form.getfirst("userid")
  200.  
  201.   if userid is None:
  202.     print "Content-Type: text/html"
  203.     print "Cache-Control: public, max-age=14400"
  204.     print
  205.     print \'<html><head><link rel="shortcut icon" href="/favicon.ico" /><title>Fetch Blogger Avatar</title></head><body style="text-align:center;">Application to return small icon from Blogger profile. Usage: ?userid=USERID.<br /><hr /><div style="font-size:85%">by MS-potilas 2011, see <a href="http://yabtb.blogspot.com/2011/11/python-tool-to-get-blogger-avatar.html">yabtb.blogspot.com</a>.</div><br />\'
  206.     print \'</body></html>\'
  207.     return 0
  208.  
  209.   if not re.match(\'\\d+$\', userid):
  210.     print "Status: 400 Bad Request";
  211.     print
  212.     print "invalid userid"
  213.     return 1
  214.  
  215.   # first check cache
  216.   thedata = memcache.get(key=userid)
  217.   if thedata is None:
  218.     domain = "www.blogger.com" # fallback favico
  219.     url = "http://www.blogger.com/profile/"+userid
  220.     result = fetchUrl(url)
  221.     if result is not None:
  222.       found = getTagAttr(result, "img", \'class=[\\\'"](?:[-_\\d\\w]+\\s+)*photo(?:\\s+[-_\\d\\w]+)*[\\\'"]\', "src")
  223.       if found is not None and not re.match(\'http\', found):
  224.         found = "http://www.blogger.com" + found
  225.       if found is None:
  226.         found  = getTagAttr(result, "a", \'rel=[\\\'"]contributor-to\', "href")
  227.         if found is not None:
  228.           domain = re.search(\'(http://){0,1}(.+?)[$/]\', found).group(2)
  229.           found = None
  230.       if found is None:
  231.         result = getFavico(domain)
  232.       else:
  233.         result = fetchUrl(found)
  234.     # if loading has failed, fallback to domain\'s favico
  235.     if result is None:
  236.       result = getFavico(domain)
  237.     if result is not None:
  238.       img = images.Image(result)
  239.  
  240.       if img.width > img.height*1.67:
  241.         img.crop(0.2,0.0,0.8,1.0)
  242.       elif img.height > img.width*1.67:
  243.         img.crop(0.0,0.2,1.0,0.8)
  244.       elif img.width > img.height*1.25:
  245.         img.crop(0.1,0.0,0.9,1.0)
  246.       elif img.height > img.width*1.25:
  247.         img.crop(0.0,0.1,1.0,0.9)
  248.  
  249.       img.resize(32,32)
  250.       thedata=img.execute_transforms(output_encoding=images.PNG)
  251.       memcache.add(key=userid, value=thedata, time=14400)
  252.     else:
  253.       print "Status: 404 Not Found";
  254.       print
  255.       print "not found"
  256.       return 1 #all fetches failed
  257.  
  258.   print "Content-Type: image/png"
  259.   print "Cache-Control: public, max-age=14400"
  260.   print
  261.   print thedata
  262.   return 0
  263.  
  264. if __name__ == \'__main__\':
  265.   main()
');