comments.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. # -*- coding: utf-8 -*-
  2. import codecs
  3. import json
  4. import sys
  5. import time
  6. from socket import error as SocketError
  7. from socket import timeout
  8. from ssl import SSLError
  9. try:
  10. # py2
  11. from urllib2 import URLError
  12. from httplib import HTTPException
  13. except ImportError:
  14. # py3
  15. from urllib.error import URLError
  16. from http.client import HTTPException
  17. from .logger import log
  18. from .logger import seperator
  19. from instagram_private_api import ClientError
  20. """
  21. This feature of PyInstaLive was originally written by https://github.com/taengstagram
  22. The code below and in downloader.py that's related to the comment downloading
  23. feature is modified by https://github.com/notcammy
  24. """
  25. class CommentsDownloader(object):
  26. def __init__(self, api, broadcast, destination_file):
  27. self.api = api
  28. self.broadcast = broadcast
  29. self.destination_file = destination_file
  30. self.comments = []
  31. def get_live(self, first_comment_created_at=0):
  32. comments_collected = self.comments
  33. before_count = len(comments_collected)
  34. try:
  35. comments_res = self.api.broadcast_comments(
  36. self.broadcast.get('id'), last_comment_ts=first_comment_created_at)
  37. comments = comments_res.get('comments', [])
  38. first_comment_created_at = (
  39. comments[0]['created_at_utc'] if comments else int(time.time() - 5))
  40. comments_collected.extend(comments)
  41. after_count = len(comments_collected)
  42. if after_count > before_count:
  43. broadcast = self.broadcast.copy()
  44. broadcast.pop('segments', None) # save space
  45. broadcast['comments'] = comments_collected
  46. with open(self.destination_file, 'w') as outfile:
  47. json.dump(broadcast, outfile, indent=2)
  48. self.comments = comments_collected
  49. except (SSLError, timeout, URLError, HTTPException, SocketError) as e:
  50. log('[W] Comment downloading error: %s' % e, "YELLOW")
  51. except ClientError as e:
  52. if e.code == 500:
  53. log('[W] Comment downloading ClientError: %d %s' % (e.code, e.error_response), "YELLOW")
  54. elif e.code == 400 and not e.msg:
  55. log('[W] Comment downloading ClientError: %d %s' % (e.code, e.error_response), "YELLOW")
  56. else:
  57. raise e
  58. finally:
  59. try:
  60. time.sleep(4)
  61. except KeyboardInterrupt:
  62. return first_comment_created_at
  63. return first_comment_created_at
  64. def get_replay(self):
  65. comments_collected = []
  66. starting_offset = 0
  67. encoding_tag = self.broadcast.get('encoding_tag')
  68. while True:
  69. comments_res = self.api.replay_broadcast_comments(
  70. self.broadcast.get('id'), starting_offset=starting_offset, encoding_tag=encoding_tag)
  71. starting_offset = comments_res.get('ending_offset', 0)
  72. comments = comments_res.get('comments', [])
  73. comments_collected.extend(comments)
  74. if not comments_res.get('comments') or not starting_offset:
  75. break
  76. time.sleep(4)
  77. if comments_collected:
  78. self.broadcast['comments'] = comments_collected
  79. self.broadcast['initial_buffered_duration'] = 0
  80. with open(self.destination_file, 'w') as outfile:
  81. json.dump(self.broadcast, outfile, indent=2)
  82. self.comments = comments_collected
  83. def save(self):
  84. broadcast = self.broadcast.copy()
  85. broadcast.pop('segments', None)
  86. broadcast['comments'] = self.comments
  87. with open(self.destination_file, 'w') as outfile:
  88. json.dump(broadcast, outfile, indent=2)
  89. @staticmethod
  90. def generate_log(comments, download_start_time, log_file, comments_delay=10.0):
  91. python_version = sys.version.split(' ')[0]
  92. subtitles_timeline = {}
  93. wide_build = sys.maxunicode > 65536
  94. for i, c in enumerate(comments):
  95. if 'offset' in c:
  96. for k in c.get('comment').keys():
  97. c[k] = c.get('comment', {}).get(k)
  98. c['created_at_utc'] = download_start_time + c.get('offset')
  99. created_at_utc = str(2 * (c.get('created_at_utc') // 2))
  100. comment_list = subtitles_timeline.get(created_at_utc) or []
  101. comment_list.append(c)
  102. subtitles_timeline[created_at_utc] = comment_list
  103. if subtitles_timeline:
  104. timestamps = sorted(subtitles_timeline.keys())
  105. mememe = False
  106. subs = []
  107. for i, tc in enumerate(timestamps):
  108. t = subtitles_timeline[tc]
  109. clip_start = int(tc) - int(download_start_time) + int(comments_delay)
  110. if clip_start < 0:
  111. clip_start = 0
  112. log = ''
  113. for c in t:
  114. if python_version.startswith('3'):
  115. if (c.get('user', {}).get('is_verified')):
  116. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{} {}: {}'.format(c.get('user', {}).get('username'), "(v)", c.get('text')))
  117. else:
  118. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{}: {}'.format(c.get('user', {}).get('username'), c.get('text')))
  119. else:
  120. if not wide_build:
  121. if (c.get('user', {}).get('is_verified')):
  122. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{} {}: {}'.format(c.get('user', {}).get('username'), "(v)", c.get('text').encode('ascii', 'ignore')))
  123. else:
  124. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{}: {}'.format(c.get('user', {}).get('username'), c.get('text').encode('ascii', 'ignore')))
  125. else:
  126. if (c.get('user', {}).get('is_verified')):
  127. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{} {}: {}'.format(c.get('user', {}).get('username'), "(v)", c.get('text')))
  128. else:
  129. log += '{}{}\n\n'.format(time.strftime('%H:%M:%S\n', time.gmtime(clip_start)), '{}: {}'.format(c.get('user', {}).get('username'), c.get('text')))
  130. subs.append(log)
  131. with codecs.open(log_file, 'w', 'utf-8-sig') as log_outfile:
  132. if python_version.startswith('2') and not wide_build:
  133. log_outfile.write('This log was generated using Python {:s} without wide unicode support. This means characters such as emojis are not saved.\nUser comments without any text usually are comments that only had emojis.\nBuild Python 2 with the --enable-unicode=ucs4 flag or use Python 3 for full unicode support.\n\n'.format(python_version) + ''.join(subs))
  134. else:
  135. log_outfile.write(''.join(subs))