startup.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import argparse
  2. import configparser
  3. import os
  4. import logging
  5. import platform
  6. import subprocess
  7. try:
  8. import urlparse
  9. import pil
  10. import auth
  11. import logger
  12. import helpers
  13. import downloader
  14. import assembler
  15. import dlfuncs
  16. from constants import Constants
  17. except ImportError:
  18. from urllib.parse import urlparse
  19. from . import pil
  20. from . import auth
  21. from . import logger
  22. from . import helpers
  23. from . import downloader
  24. from . import assembler
  25. from . import dlfuncs
  26. from .constants import Constants
  27. def validate_inputs(config, args, unknown_args):
  28. error_arr = []
  29. try:
  30. config.read(pil.config_path)
  31. if args.download:
  32. pil.dl_user = args.download
  33. if args.downloadfollowing or args.batchfile:
  34. logger.banner()
  35. logger.warn("Please use only one download method. Use -h for more information.")
  36. logger.separator()
  37. return False
  38. elif not args.clean and not args.info and not args.assemble and not args.downloadfollowing and not args.batchfile:
  39. logger.banner()
  40. logger.error("Please use a download method. Use -h for more information.")
  41. logger.separator()
  42. return False
  43. if helpers.bool_str_parse(config.get('pyinstalive', 'log_to_file')) == "Invalid":
  44. pil.log_to_file = True
  45. error_arr.append(['log_to_file', 'True'])
  46. elif helpers.bool_str_parse(config.get('pyinstalive', 'log_to_file')):
  47. pil.log_to_file = True
  48. else:
  49. pil.log_to_file = False
  50. logger.banner()
  51. if args.batchfile:
  52. if os.path.isfile(args.batchfile):
  53. pil.dl_batchusers = [user.rstrip('\n') for user in open(args.batchfile)]
  54. if not pil.dl_batchusers:
  55. logger.error("The specified file is empty.")
  56. logger.separator()
  57. return False
  58. else:
  59. logger.info("Downloading {:d} users from batch file.".format(len(pil.dl_batchusers)))
  60. logger.separator()
  61. else:
  62. logger.error('The specified file does not exist.')
  63. logger.separator()
  64. return False
  65. if unknown_args:
  66. pil.uargs = unknown_args
  67. logger.warn("The following unknown argument(s) were provided and will be ignored: ")
  68. logger.warn(' ' + ' '.join(unknown_args))
  69. logger.separator()
  70. pil.ig_user = config.get('pyinstalive', 'username')
  71. pil.ig_pass = config.get('pyinstalive', 'password')
  72. pil.dl_path = config.get('pyinstalive', 'download_path')
  73. pil.run_at_start = config.get('pyinstalive', 'run_at_start')
  74. pil.run_at_finish = config.get('pyinstalive', 'run_at_finish')
  75. pil.ffmpeg_path = config.get('pyinstalive', 'ffmpeg_path')
  76. pil.args = args
  77. pil.config = config
  78. pil.proxy = config.get('pyinstalive', 'proxy')
  79. if args.configpath:
  80. pil.config_path = args.configpath
  81. if not os.path.isfile(pil.config_path):
  82. pil.config_path = os.path.join(os.getcwd(), "pyinstalive.ini")
  83. logger.warn("Custom config path is invalid, falling back to default path: {:s}".format(pil.config_path))
  84. logger.separator()
  85. if args.dlpath:
  86. pil.dl_path = args.dlpath
  87. if helpers.bool_str_parse(config.get('pyinstalive', 'show_cookie_expiry')) == "Invalid":
  88. pil.show_cookie_expiry = False
  89. error_arr.append(['show_cookie_expiry', 'False'])
  90. elif helpers.bool_str_parse(config.get('pyinstalive', 'show_cookie_expiry')):
  91. pil.show_cookie_expiry = True
  92. else:
  93. pil.show_cookie_expiry = False
  94. if helpers.bool_str_parse(config.get('pyinstalive', 'use_locks')) == "Invalid":
  95. pil.use_locks = False
  96. error_arr.append(['use_locks', 'False'])
  97. elif helpers.bool_str_parse(config.get('pyinstalive', 'use_locks')):
  98. pil.use_locks = True
  99. else:
  100. pil.use_locks = False
  101. if helpers.bool_str_parse(config.get('pyinstalive', 'clear_temp_files')) == "Invalid":
  102. pil.clear_temp_files = False
  103. error_arr.append(['clear_temp_files', 'False'])
  104. elif helpers.bool_str_parse(config.get('pyinstalive', 'clear_temp_files')):
  105. pil.clear_temp_files = True
  106. else:
  107. pil.clear_temp_files = False
  108. if helpers.bool_str_parse(config.get('pyinstalive', 'do_heartbeat')) == "Invalid":
  109. pil.do_heartbeat = True
  110. error_arr.append(['do_heartbeat', 'True'])
  111. if helpers.bool_str_parse(config.get('pyinstalive', 'do_heartbeat')):
  112. pil.do_heartbeat = True
  113. if args.noheartbeat or not helpers.bool_str_parse(config.get('pyinstalive', 'do_heartbeat')):
  114. pil.do_heartbeat = False
  115. logger.warn("Getting livestream heartbeat is disabled, this may cause degraded performance.")
  116. logger.separator()
  117. if not args.nolives and helpers.bool_str_parse(config.get('pyinstalive', 'download_lives')) == "Invalid":
  118. pil.dl_lives = True
  119. error_arr.append(['download_lives', 'True'])
  120. elif helpers.bool_str_parse(config.get('pyinstalive', 'download_lives')):
  121. pil.dl_lives = True
  122. else:
  123. pil.dl_lives = False
  124. if not args.noreplays and helpers.bool_str_parse(config.get('pyinstalive', 'download_replays')) == "Invalid":
  125. pil.dl_replays = True
  126. error_arr.append(['download_replays', 'True'])
  127. elif helpers.bool_str_parse(config.get('pyinstalive', 'download_replays')):
  128. pil.dl_replays = True
  129. else:
  130. pil.dl_replays = False
  131. if helpers.bool_str_parse(config.get('pyinstalive', 'download_comments')) == "Invalid":
  132. pil.dl_comments = True
  133. error_arr.append(['download_comments', 'True'])
  134. elif helpers.bool_str_parse(config.get('pyinstalive', 'download_comments')):
  135. pil.dl_comments = True
  136. else:
  137. pil.dl_comments = False
  138. if args.nolives:
  139. pil.dl_lives = False
  140. if args.noreplays:
  141. pil.dl_replays = False
  142. if not pil.dl_lives and not pil.dl_replays:
  143. logger.error("You have disabled both livestream and replay downloading.")
  144. logger.error("Please enable at least one of them and try again.")
  145. logger.separator()
  146. return False
  147. if pil.ffmpeg_path:
  148. if not os.path.isfile(pil.ffmpeg_path):
  149. pil.ffmpeg_path = None
  150. cmd = "where" if platform.system() == "Windows" else "which"
  151. logger.warn("Custom ffmpeg binary path is invalid, falling back to default path: {:s}".format(
  152. subprocess.check_output([cmd, 'ffmpeg']).decode('UTF-8').rstrip()))
  153. else:
  154. logger.binfo("Overriding ffmpeg binary path: {:s}".format(pil.ffmpeg_path))
  155. if not pil.ig_user or not len(pil.ig_user):
  156. raise Exception("Invalid value for 'username'. This value is required.")
  157. if not pil.ig_pass or not len(pil.ig_pass):
  158. raise Exception("Invalid value for 'password'. This value is required.")
  159. if not pil.dl_path.endswith('/'):
  160. pil.dl_path = pil.dl_path + '/'
  161. if not pil.dl_path or not os.path.exists(pil.dl_path):
  162. pil.dl_path = os.getcwd()
  163. if not args.dlpath:
  164. error_arr.append(['download_path', os.getcwd()])
  165. else:
  166. logger.warn("Custom config path is invalid, falling back to default path: {:s}".format(pil.dl_path))
  167. logger.separator()
  168. if pil.proxy and pil.proxy != '':
  169. parsed_url = urlparse(pil.proxy)
  170. if not parsed_url.netloc or not parsed_url.scheme:
  171. error_arr.append(['proxy', 'None'])
  172. pil.proxy = None
  173. if error_arr:
  174. for error in error_arr:
  175. logger.warn("Invalid value for '{:s}'. Using default value: {:s}".format(error[0], error[1]))
  176. logger.separator()
  177. if args.info:
  178. helpers.show_info()
  179. return False
  180. elif args.clean:
  181. helpers.clean_download_dir()
  182. return False
  183. elif args.assemble:
  184. pil.assemble_arg = args.assemble
  185. assembler.assemble()
  186. return False
  187. return True
  188. except Exception as e:
  189. logger.error("An error occurred: {:s}".format(str(e)))
  190. logger.error("Make sure the config file and given arguments are valid and try again.")
  191. logger.separator()
  192. return False
  193. def run():
  194. pil.initialize()
  195. logging.disable(logging.CRITICAL)
  196. config = configparser.ConfigParser()
  197. parser = argparse.ArgumentParser(
  198. description="You are running PyInstaLive {:s} using Python {:s}".format(Constants.SCRIPT_VER,
  199. Constants.PYTHON_VER))
  200. parser.add_argument('-u', '--username', dest='username', type=str, required=False,
  201. help="Instagram username to login with.")
  202. parser.add_argument('-p', '--password', dest='password', type=str, required=False,
  203. help="Instagram password to login with.")
  204. parser.add_argument('-d', '--download', dest='download', type=str, required=False,
  205. help="The username of the user whose livestream or replay you want to save.")
  206. parser.add_argument('-b,', '--batch-file', dest='batchfile', type=str, required=False,
  207. help="Read a text file of usernames to download livestreams or replays from.")
  208. parser.add_argument('-i', '--info', dest='info', action='store_true', help="View information about PyInstaLive.")
  209. parser.add_argument('-nr', '--no-replays', dest='noreplays', action='store_true',
  210. help="When used, do not check for any available replays.")
  211. parser.add_argument('-nl', '--no-lives', dest='nolives', action='store_true',
  212. help="When used, do not check for any available livestreams.")
  213. parser.add_argument('-cl', '--clean', dest='clean', action='store_true',
  214. help="PyInstaLive will clean the current download folder of all leftover files.")
  215. parser.add_argument('-cp', '--config-path', dest='configpath', type=str, required=False,
  216. help="Path to a PyInstaLive configuration file.")
  217. parser.add_argument('-dp', '--download-path', dest='dlpath', type=str, required=False,
  218. help="Path to folder where PyInstaLive should save livestreams and replays.")
  219. parser.add_argument('-as', '--assemble', dest='assemble', type=str, required=False,
  220. help="Path to json file required by the assembler to generate a video file from the segments.")
  221. parser.add_argument('-df', '--download-following', dest='downloadfollowing', action='store_true',
  222. help="PyInstaLive will check for available livestreams and replays from users the account "
  223. "used to login follows.")
  224. parser.add_argument('-nhb', '--no-heartbeat', dest='noheartbeat', action='store_true', help="Disable heartbeat "
  225. "check for "
  226. "livestreams.")
  227. # Workaround to 'disable' argument abbreviations
  228. parser.add_argument('--usernamx', help=argparse.SUPPRESS, metavar='IGNORE')
  229. parser.add_argument('--passworx', help=argparse.SUPPRESS, metavar='IGNORE')
  230. parser.add_argument('--infx', help=argparse.SUPPRESS, metavar='IGNORE')
  231. parser.add_argument('--noreplayx', help=argparse.SUPPRESS, metavar='IGNORE')
  232. parser.add_argument('--cleax', help=argparse.SUPPRESS, metavar='IGNORE')
  233. parser.add_argument('--downloadfollowinx', help=argparse.SUPPRESS, metavar='IGNORE')
  234. parser.add_argument('--configpatx', help=argparse.SUPPRESS, metavar='IGNORE')
  235. parser.add_argument('--confix', help=argparse.SUPPRESS, metavar='IGNORE')
  236. parser.add_argument('-cx', help=argparse.SUPPRESS, metavar='IGNORE')
  237. parser.add_argument('-nx', help=argparse.SUPPRESS, metavar='IGNORE')
  238. parser.add_argument('-dx', help=argparse.SUPPRESS, metavar='IGNORE')
  239. args, unknown_args = parser.parse_known_args() # Parse arguments
  240. if not os.path.exists(pil.config_path): # Create new config if it doesn't exist
  241. logger.banner()
  242. helpers.new_config()
  243. return
  244. if validate_inputs(config, args, unknown_args):
  245. if not args.username and not args.password:
  246. pil.ig_api = auth.authenticate(username=pil.ig_user, password=pil.ig_pass)
  247. elif (args.username and not args.password) or (args.password and not args.username):
  248. logger.warn("Missing --username or --password argument. Falling back to config file.")
  249. logger.separator()
  250. pil.ig_api = auth.authenticate(username=pil.ig_user, password=pil.ig_pass)
  251. elif args.username and args.password:
  252. pil.ig_api = auth.authenticate(username=args.username, password=args.password, force_use_login_args=True)
  253. if pil.ig_api:
  254. if pil.dl_user or pil.args.downloadfollowing:
  255. downloader.start()
  256. elif pil.dl_batchusers:
  257. if not helpers.command_exists("pyinstalive"):
  258. logger.error("PyInstaLive must be properly installed when using the -b argument.")
  259. logger.separator()
  260. else:
  261. dlfuncs.iterate_users(pil.dl_batchusers)