downloader.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import sys
  2. import time
  3. import os
  4. import shutil
  5. import subprocess
  6. import threading
  7. from instagram_private_api_extensions import live, replay
  8. from instagram_private_api import ClientError
  9. from .logger import log, seperator
  10. class NoLivestreamException(Exception):
  11. pass
  12. class NoReplayException(Exception):
  13. pass
  14. def main(api_arg, record_arg, settings_arg):
  15. global api
  16. global record
  17. global broadcast
  18. global mpd_url
  19. global settings
  20. settings = settings_arg
  21. api = api_arg
  22. record = record_arg
  23. get_user_info(record)
  24. def run_script(file):
  25. try:
  26. FNULL = open(os.devnull, 'w')
  27. if sys.version.split(' ')[0].startswith('2'):
  28. subprocess.call(["python", file], stdout=FNULL, stderr=subprocess.STDOUT)
  29. else:
  30. subprocess.call(["python3", file], stdout=FNULL, stderr=subprocess.STDOUT)
  31. except OSError as e:
  32. pass
  33. def get_stream_duration(broadcast):
  34. try:
  35. started_mins, started_secs = divmod((int(time.time()) - broadcast['published_time']), 60)
  36. started_label = '%d minutes' % started_mins
  37. if started_secs:
  38. started_label += ' and %d seconds' % started_secs
  39. return started_label
  40. except:
  41. return "not available"
  42. def record_stream(broadcast):
  43. try:
  44. def print_status(sep=True):
  45. heartbeat_info = api.broadcast_heartbeat_and_viewercount(broadcast['id'])
  46. viewers = broadcast.get('viewer_count', 0)
  47. if sep:
  48. seperator("GREEN")
  49. log('[I] Viewers : ' + str(int(viewers)) + " watching", "GREEN")
  50. log('[I] Airing time : ' + get_stream_duration(broadcast).title(), "GREEN")
  51. log('[I] Status : ' + heartbeat_info['broadcast_status'].title(), "GREEN")
  52. return heartbeat_info['broadcast_status'] not in ['active', 'interrupted']
  53. mpd_url = (broadcast.get('dash_manifest')
  54. or broadcast.get('dash_abr_playback_url')
  55. or broadcast['dash_playback_url'])
  56. output_dir = settings.save_path + '{}_{}_{}_{}_live_downloads'.format(settings.current_date, record, broadcast['id'], settings.current_time)
  57. dl = live.Downloader(
  58. mpd=mpd_url,
  59. output_dir=output_dir,
  60. user_agent=api.user_agent,
  61. max_connection_error_retry=3,
  62. duplicate_etag_retry=30,
  63. callback_check=print_status,
  64. mpd_download_timeout=5,
  65. download_timeout=10)
  66. except Exception as e:
  67. log('[E] Could not start downloading livestream: ' + str(e), "RED")
  68. seperator("GREEN")
  69. sys.exit(1)
  70. try:
  71. log('[I] Livestream downloading started...', "GREEN")
  72. seperator("GREEN")
  73. log('[I] Username : ' + record, "GREEN")
  74. print_status(False)
  75. log('[I] MPD URL : ' + mpd_url, "GREEN")
  76. seperator("GREEN")
  77. log('[I] Downloading livestream... press [CTRL+C] to abort.', "GREEN")
  78. if (settings.run_at_start is not "None"):
  79. try:
  80. thread = threading.Thread(target=run_script, args=(settings.run_at_start,))
  81. thread.daemon = True
  82. thread.start()
  83. log("[I] Executed file to run at start.", "GREEN")
  84. except Exception as e:
  85. log('[W] Could not run file: ' + str(e), "YELLOW")
  86. comment_thread_worker = None
  87. if settings.save_comments.title() == "True":
  88. try:
  89. comments_json_file = settings.save_path + '{}_{}_{}_{}_live_comments.json'.format(settings.current_date, record, broadcast['id'], settings.current_time)
  90. comment_thread_worker = threading.Thread(target=get_live_comments, args=(api, broadcast, comments_json_file, dl,))
  91. comment_thread_worker.start()
  92. except Exception as e:
  93. log('[E] An error occurred while checking comments: ' + e, "RED")
  94. dl.run()
  95. seperator("GREEN")
  96. log('[I] The livestream has ended. (Duration: ' + get_stream_duration(broadcast) + ")", "GREEN")
  97. seperator("GREEN")
  98. stitch_video(dl, broadcast, comment_thread_worker)
  99. except KeyboardInterrupt:
  100. seperator("GREEN")
  101. log('[W] Download has been aborted by the user.', "YELLOW")
  102. seperator("GREEN")
  103. if not dl.is_aborted:
  104. dl.stop()
  105. stitch_video(dl, broadcast)
  106. def stitch_video(dl, broadcast, comment_thread_worker):
  107. if comment_thread_worker and comment_thread_worker.is_alive():
  108. log("[I] Ending comment saving process...", "GREEN")
  109. comment_thread_worker.join()
  110. if (settings.run_at_finish is not "None"):
  111. try:
  112. thread = threading.Thread(target=run_script, args=(settings.run_at_finish,))
  113. thread.daemon = True
  114. thread.start()
  115. log("[I] Executed file to run at finish.", "GREEN")
  116. except Exception as e:
  117. log('[W] Could not run file: ' + e, "YELLOW")
  118. log('[I] Stitching downloaded files into video...', "GREEN")
  119. output_file = settings.save_path + '{}_{}_{}_{}_live.mp4'.format(settings.current_date, record, broadcast['id'], settings.current_time)
  120. try:
  121. if settings.clear_temp_files.title() == "True":
  122. dl.stitch(output_file, cleartempfiles=True)
  123. else:
  124. dl.stitch(output_file, cleartempfiles=False)
  125. log('[I] Successfully stitched downloaded files into video.', "GREEN")
  126. seperator("GREEN")
  127. sys.exit(0)
  128. except Exception as e:
  129. log('[E] Could not stitch downloaded files: ' + str(e), "RED")
  130. seperator("GREEN")
  131. sys.exit(1)
  132. def get_user_info(record):
  133. try:
  134. log('[I] Checking user "' + record + '"...', "GREEN")
  135. user_res = api.username_info(record)
  136. user_id = user_res['user']['pk']
  137. except Exception as e:
  138. log('[E] Could not get user info: ' + str(e), "RED")
  139. seperator("GREEN")
  140. sys.exit(1)
  141. get_livestreams(user_id)
  142. if settings.save_replays.title() == "True":
  143. get_replays(user_id)
  144. else:
  145. seperator("GREEN")
  146. log("[I] Replay saving is disabled either with a flag or in the config file.", "BLUE")
  147. seperator("GREEN")
  148. sys.exit(0)
  149. def get_livestreams(user_id):
  150. try:
  151. seperator("GREEN")
  152. log('[I] Checking for ongoing livestreams...', "GREEN")
  153. broadcast = api.user_broadcast(user_id)
  154. if (broadcast is None):
  155. raise NoLivestreamException('There are no livestreams available.')
  156. else:
  157. record_stream(broadcast)
  158. except NoLivestreamException as e:
  159. log('[I] ' + str(e), "YELLOW")
  160. except Exception as e:
  161. if (e.__class__.__name__ is not NoLivestreamException):
  162. log('[E] Could not get livestreams info: ' + str(e), "RED")
  163. seperator("GREEN")
  164. sys.exit(1)
  165. def get_replays(user_id):
  166. try:
  167. seperator("GREEN")
  168. log('[I] Checking for available replays...', "GREEN")
  169. user_story_feed = api.user_story_feed(user_id)
  170. broadcasts = user_story_feed.get('post_live_item', {}).get('broadcasts', [])
  171. except Exception as e:
  172. log('[E] Could not get replay info: ' + str(e), "RED")
  173. seperator("GREEN")
  174. sys.exit(1)
  175. try:
  176. if (len(broadcasts) == 0):
  177. raise NoReplayException('There are no replays available.')
  178. else:
  179. log("[I] Available replays have been found to download, press [CTRL+C] to abort.", "GREEN")
  180. seperator("GREEN")
  181. for index, broadcast in enumerate(broadcasts):
  182. exists = False
  183. if sys.version.split(' ')[0].startswith('2'):
  184. directories = (os.walk(settings.save_path).next()[1])
  185. else:
  186. directories = (os.walk(settings.save_path).__next__()[1])
  187. for directory in directories:
  188. if (str(broadcast['id']) in directory) and ("_live_" not in directory):
  189. log("[W] Already downloaded a replay with ID '" + str(broadcast['id']) + "', skipping...", "GREEN")
  190. exists = True
  191. if exists is False:
  192. current = index + 1
  193. log("[I] Downloading replay " + str(current) + " of " + str(len(broadcasts)) + " with ID '" + str(broadcast['id']) + "'...", "GREEN")
  194. current_time = str(int(time.time()))
  195. output_dir = settings.save_path + '{}_{}_{}_{}_replay_downloads'.format(settings.current_date, record, broadcast['id'], settings.current_time)
  196. dl = replay.Downloader(
  197. mpd=broadcast['dash_manifest'],
  198. output_dir=output_dir,
  199. user_agent=api.user_agent)
  200. if settings.clear_temp_files.title() == "True":
  201. replay_saved = dl.download(settings.save_path + '{}_{}_{}_{}_replay.mp4'.format(settings.current_date, record, broadcast['id'], settings.current_time), cleartempfiles=True)
  202. else:
  203. replay_saved = dl.download(settings.save_path + '{}_{}_{}_{}_replay.mp4'.format(settings.current_date, record, broadcast['id'], settings.current_time), cleartempfiles=False)
  204. if (len(replay_saved) == 1):
  205. log("[I] Finished downloading replay " + str(current) + " of " + str(len(broadcasts)) + ".", "GREEN")
  206. seperator("GREEN")
  207. else:
  208. log("[W] No output video file was made, please merge the files manually.", "YELLOW")
  209. log("[W] Check if ffmpeg is available by running ffmpeg in your terminal.", "YELLOW")
  210. log("", "GREEN")
  211. if settings.save_comments.title() == "True":
  212. log("[I] Checking for available comments to save...", "GREEN")
  213. comments_json_file = settings.save_path + '{}_{}_{}_{}_replay_comments.json'.format(settings.current_date, record, broadcast['id'], settings.current_time)
  214. get_replay_comments(api, broadcast, comments_json_file, dl)
  215. log("[I] Finished downloading available replays.", "GREEN")
  216. seperator("GREEN")
  217. sys.exit(0)
  218. except NoReplayException as e:
  219. log('[I] ' + str(e), "YELLOW")
  220. seperator("GREEN")
  221. sys.exit(0)
  222. except Exception as e:
  223. log('[E] Could not save replay: ' + str(e), "RED")
  224. seperator("GREEN")
  225. sys.exit(1)
  226. except KeyboardInterrupt:
  227. seperator("GREEN")
  228. log('[W] Download has been aborted by the user.', "YELLOW")
  229. seperator("GREEN")
  230. try:
  231. shutil.rmtree(output_dir)
  232. except Exception as e:
  233. log("[E] Could not remove temp folder: " + str(e), "RED")
  234. sys.exit(1)
  235. sys.exit(0)