avatar.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. # -*- coding: UTF-8 -*-
  2. from PIL import Image
  3. import io
  4. import glob
  5. import os
  6. import numpy as np
  7. import logging
  8. import sqlite3
  9. logger = logging.getLogger(__name__)
  10. from common.textutil import ensure_unicode, md5
  11. class AvatarReader(object):
  12. def __init__(self, res_dir, avt_db="avatar.index"):
  13. self.sfs_dir = os.path.join(res_dir, 'sfs')
  14. # new location of avatar, see #50
  15. self.avt_dir = os.path.join(res_dir, 'avatar')
  16. self.avt_db = avt_db
  17. self._use_avt = True
  18. if os.path.isdir(self.avt_dir) and len(os.listdir(self.avt_dir)):
  19. self.avt_use_db = False
  20. elif self.avt_db is not None \
  21. and os.path.isfile(self.avt_db) \
  22. and glob.glob(os.path.join(self.sfs_dir, 'avatar*')):
  23. self.avt_use_db = True
  24. else:
  25. logger.warn(
  26. "Cannot find avatar files. Will not use avatar!")
  27. self._use_avt = False
  28. def get_avatar(self, username):
  29. """ username: `username` field in db.rcontact"""
  30. if not self._use_avt:
  31. return None
  32. username = ensure_unicode(username).encode('utf-8')
  33. filename = md5(username)
  34. dir1, dir2 = filename[:2], filename[2:4]
  35. filename = os.path.join(dir1, dir2,
  36. "user_{}.png".format(filename))
  37. try:
  38. try:
  39. if self.avt_use_db:
  40. pos, size = self.query_index(filename)
  41. return self.read_img(pos, size)
  42. else:
  43. img_file = os.path.join(self.avt_dir, filename)
  44. if os.path.exists(img_file):
  45. return Image.open(img_file)
  46. else:
  47. return None
  48. except TypeError:
  49. logger.warning("Avatar for {} not found in avatar database.".format(username))
  50. return None
  51. except Exception as e:
  52. raise
  53. # logger.exception("Failed to retrieve avatar!")
  54. # return None
  55. def read_img(self, pos, size):
  56. file_idx = pos >> 32
  57. fname = os.path.join(self.sfs_dir,
  58. 'avatar.block.' + '{:05d}'.format(file_idx))
  59. # a 64-byte offset of each block file
  60. start_pos = pos - file_idx * (2**32) + 64
  61. try:
  62. with open(fname, 'rb') as f:
  63. f.seek(start_pos)
  64. data = f.read(size)
  65. im = Image.open(io.BytesIO(data))
  66. return im
  67. except IOError as e:
  68. logger.warn("Cannot read avatar from {}: {}".format(fname, str(e)))
  69. return None
  70. def query_index(self, filename):
  71. conn = sqlite3.connect(self.avt_db)
  72. cursor = conn.execute("select Offset,Size from Index_avatar where FileName='{}'".format(filename))
  73. pos, size = cursor.fetchone()
  74. return pos, size