initialize.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import argparse
  2. import configparser
  3. import logging
  4. import os.path
  5. import subprocess
  6. import sys
  7. import time
  8. import shutil
  9. import json
  10. from .auth import login
  11. from .downloader import main
  12. from .logger import log
  13. from .logger import seperator
  14. from .logger import supports_color
  15. from .settings import settings
  16. script_version = "2.5.3"
  17. python_version = sys.version.split(' ')[0]
  18. bool_values = {'True', 'False'}
  19. def check_ffmpeg():
  20. try:
  21. FNULL = open(os.devnull, 'w')
  22. subprocess.call(["ffmpeg"], stdout=FNULL, stderr=subprocess.STDOUT)
  23. return True
  24. except OSError as e:
  25. return False
  26. def check_config_validity(config):
  27. try:
  28. has_thrown_errors = False
  29. settings.username = config.get('pyinstalive', 'username')
  30. settings.password = config.get('pyinstalive', 'password')
  31. try:
  32. settings.show_cookie_expiry = config.get('pyinstalive', 'show_cookie_expiry').title()
  33. if not settings.show_cookie_expiry in bool_values:
  34. log("[W] Invalid or missing setting detected for 'show_cookie_expiry', using default value (True)", "YELLOW")
  35. settings.show_cookie_expiry = 'true'
  36. has_thrown_errors = True
  37. except:
  38. log("[W] Invalid or missing setting detected for 'show_cookie_expiry', using default value (True)", "YELLOW")
  39. settings.show_cookie_expiry = 'true'
  40. has_thrown_errors = True
  41. try:
  42. settings.clear_temp_files = config.get('pyinstalive', 'clear_temp_files').title()
  43. if not settings.clear_temp_files in bool_values:
  44. log("[W] Invalid or missing setting detected for 'clear_temp_files', using default value (True)", "YELLOW")
  45. settings.clear_temp_files = 'true'
  46. has_thrown_errors = True
  47. except:
  48. log("[W] Invalid or missing setting detected for 'clear_temp_files', using default value (True)", "YELLOW")
  49. settings.clear_temp_files = 'true'
  50. has_thrown_errors = True
  51. try:
  52. settings.save_replays = config.get('pyinstalive', 'save_replays').title()
  53. if not settings.save_replays in bool_values:
  54. log("[W] Invalid or missing setting detected for 'save_replays', using default value (True)", "YELLOW")
  55. settings.save_replays = 'true'
  56. has_thrown_errors = True
  57. except:
  58. log("[W] Invalid or missing setting detected for 'save_replays', using default value (True)", "YELLOW")
  59. settings.save_replays = 'true'
  60. has_thrown_errors = True
  61. try:
  62. settings.log_to_file = config.get('pyinstalive', 'log_to_file').title()
  63. if not settings.log_to_file in bool_values:
  64. log("[W] Invalid or missing setting detected for 'log_to_file', using default value (False)", "YELLOW")
  65. settings.log_to_file = 'False'
  66. has_thrown_errors = True
  67. except:
  68. log("[W] Invalid or missing setting detected for 'log_to_file', using default value (False)", "YELLOW")
  69. settings.log_to_file = 'False'
  70. has_thrown_errors = True
  71. try:
  72. settings.run_at_start = config.get('pyinstalive', 'run_at_start').replace("\\", "\\\\")
  73. if not settings.run_at_start:
  74. settings.run_at_start = "None"
  75. except:
  76. log("[W] Invalid or missing settings detected for 'run_at_start', using default value (None)", "YELLOW")
  77. settings.run_at_start = "None"
  78. has_thrown_errors = True
  79. try:
  80. settings.run_at_finish = config.get('pyinstalive', 'run_at_finish').replace("\\", "\\\\")
  81. if not settings.run_at_finish:
  82. settings.run_at_finish = "None"
  83. except:
  84. log("[W] Invalid or missing settings detected for 'run_at_finish', using default value (None)", "YELLOW")
  85. settings.run_at_finish = "None"
  86. has_thrown_errors = True
  87. try:
  88. settings.save_comments = config.get('pyinstalive', 'save_comments').title()
  89. wide_build = sys.maxunicode > 65536
  90. if sys.version.split(' ')[0].startswith('2') and settings.save_comments == "True" and not wide_build:
  91. log("[W] Your Python 2 build does not support wide unicode characters.\n[W] This means characters such as mojis will not be saved.", "YELLOW")
  92. has_thrown_errors = True
  93. else:
  94. if not settings.show_cookie_expiry in bool_values:
  95. log("[W] Invalid or missing setting detected for 'save_comments', using default value (False)", "YELLOW")
  96. settings.save_comments = 'false'
  97. has_thrown_errors = True
  98. except:
  99. log("[W] Invalid or missing setting detected for 'save_comments', using default value (False)", "YELLOW")
  100. settings.save_comments = 'false'
  101. has_thrown_errors = True
  102. try:
  103. settings.save_path = config.get('pyinstalive', 'save_path')
  104. if (os.path.exists(settings.save_path)):
  105. pass
  106. else:
  107. log("[W] Invalid or missing setting detected for 'save_path', falling back to path: " + os.getcwd(), "YELLOW")
  108. settings.save_path = os.getcwd()
  109. has_thrown_errors = True
  110. if not settings.save_path.endswith('/'):
  111. settings.save_path = settings.save_path + '/'
  112. except:
  113. log("[W] Invalid or missing setting detected for 'save_path', falling back to path: " + os.getcwd(), "YELLOW")
  114. settings.save_path = os.getcwd()
  115. has_thrown_errors = True
  116. if has_thrown_errors:
  117. seperator("GREEN")
  118. return True
  119. except Exception as e:
  120. print(str(e))
  121. return False
  122. def show_info(config):
  123. if os.path.exists('pyinstalive.ini'):
  124. try:
  125. config.read('pyinstalive.ini')
  126. except Exception:
  127. log("[E] Could not read configuration file.", "RED")
  128. seperator("GREEN")
  129. else:
  130. new_config()
  131. sys.exit(1)
  132. if not check_config_validity(config):
  133. log("[W] Config file is not valid, some information may be inaccurate.", "YELLOW")
  134. log("", "GREEN")
  135. cookie_files = []
  136. cookie_from_config = ''
  137. try:
  138. for file in os.listdir(os.getcwd()):
  139. if file.endswith(".json"):
  140. with open(file) as data_file:
  141. json_data = json.load(data_file)
  142. if (json_data.get('created_ts')):
  143. cookie_files.append(file)
  144. if settings.username == file.replace(".json", ''):
  145. cookie_from_config = file
  146. except Exception as e:
  147. log("[W] Could not check for cookie files: " + str(e), "YELLOW")
  148. log("", "ENDC")
  149. log("[I] To see all the available flags, use the -h flag.", "BLUE")
  150. log("", "GREEN")
  151. log("[I] PyInstaLive version: " + script_version, "GREEN")
  152. log("[I] Python version: " + python_version, "GREEN")
  153. if not check_ffmpeg():
  154. log("[E] FFmpeg framework: Not found", "RED")
  155. else:
  156. log("[I] FFmpeg framework: Available", "GREEN")
  157. if (len(cookie_from_config) > 0):
  158. log("[I] Cookie files: {:s} ({:s} matches config user)".format(str(len(cookie_files)), cookie_from_config), "GREEN")
  159. elif len(cookie_files) > 0:
  160. log("[I] Cookie files: {:s}".format(str(len(cookie_files))), "GREEN")
  161. else:
  162. log("[W] Cookie files: None found", "YELLOW")
  163. log("[I] CLI supports color: " + str(supports_color()[0]), "GREEN")
  164. log("[I] File to run at start: " + settings.run_at_start, "GREEN")
  165. log("[I] File to run at finish: " + settings.run_at_finish, "GREEN")
  166. log("", "GREEN")
  167. if os.path.exists('pyinstalive.ini'):
  168. log("[I] Config file:", "GREEN")
  169. log("", "GREEN")
  170. with open('pyinstalive.ini') as f:
  171. for line in f:
  172. log(" " + line.rstrip(), "YELLOW")
  173. else:
  174. log("[E] Config file: Not found", "RED")
  175. log("", "GREEN")
  176. log("[I] End of PyInstaLive information screen.", "GREEN")
  177. seperator("GREEN")
  178. def new_config():
  179. try:
  180. if os.path.exists('pyinstalive.ini'):
  181. log("[I] A configuration file is already present:", "GREEN")
  182. log("", "GREEN")
  183. with open('pyinstalive.ini') as f:
  184. for line in f:
  185. log(" " + line.rstrip(), "YELLOW")
  186. log("", "GREEN")
  187. log("[I] To create a default config file, delete 'pyinstalive.ini' and ", "GREEN")
  188. log(" run this script again.", "GREEN")
  189. seperator("GREEN")
  190. else:
  191. try:
  192. log("[W] Could not find configuration file, creating a default one...", "YELLOW")
  193. config_template = "[pyinstalive]\nusername = johndoe\npassword = grapefruits\nsave_path = " + os.getcwd() + "\nshow_cookie_expiry = true\nclear_temp_files = false\nsave_replays = true\nrun_at_start = \nrun_at_finish = \nsave_comments = false\nlog_to_file = false\n"
  194. config_file = open("pyinstalive.ini", "w")
  195. config_file.write(config_template)
  196. config_file.close()
  197. log("[W] Edit the created 'pyinstalive.ini' file and run this script again.", "YELLOW")
  198. seperator("GREEN")
  199. sys.exit(0)
  200. except Exception as e:
  201. log("[E] Could not create default config file: " + str(e), "RED")
  202. log("[W] You must manually create and edit it with the following template: ", "YELLOW")
  203. log("", "GREEN")
  204. log(config_template, "YELLOW")
  205. log("", "GREEN")
  206. log("[W] Save it as 'pyinstalive.ini' and run this script again.", "YELLOW")
  207. seperator("GREEN")
  208. except Exception as e:
  209. log("[E] An error occurred: " + str(e), "RED")
  210. log("[W] If you don't have a configuration file, you must", "YELLOW")
  211. log(" manually create and edit it with the following template: ", "YELLOW")
  212. log("", "GREEN")
  213. log(config_template, "YELLOW")
  214. log("", "GREEN")
  215. log("[W] Save it as 'pyinstalive.ini' and run this script again.", "YELLOW")
  216. seperator("GREEN")
  217. def clean_download_dir():
  218. dir_delcount = 0
  219. error_count = 0
  220. lock_count = 0
  221. log('[I] Cleaning up temporary files and folders...', "GREEN")
  222. try:
  223. if sys.version.split(' ')[0].startswith('2'):
  224. directories = (os.walk(settings.save_path).next()[1])
  225. files = (os.walk(settings.save_path).next()[2])
  226. else:
  227. directories = (os.walk(settings.save_path).__next__()[1])
  228. files = (os.walk(settings.save_path).__next__()[2])
  229. for directory in directories:
  230. if directory.endswith('_downloads'):
  231. if not any(filename.endswith('.lock') for filename in os.listdir(settings.save_path + directory)):
  232. try:
  233. shutil.rmtree(settings.save_path + directory)
  234. dir_delcount += 1
  235. except Exception as e:
  236. log("[E] Could not remove temp folder: {:s}".format(str(e)), "RED")
  237. error_count += 1
  238. else:
  239. lock_count += 1
  240. seperator("GREEN")
  241. log('[I] The cleanup has finished.', "YELLOW")
  242. if dir_delcount == 0 and error_count == 0 and lock_count == 0:
  243. log('[I] No folders were removed.', "YELLOW")
  244. seperator("GREEN")
  245. return
  246. log('[I] Folders removed: {:d}'.format(dir_delcount), "YELLOW")
  247. log('[I] Locked folders: {:d}'.format(lock_count), "YELLOW")
  248. log('[I] Errors: {:d}'.format(error_count), "YELLOW")
  249. seperator("GREEN")
  250. except KeyboardInterrupt as e:
  251. seperator("GREEN")
  252. log("[W] The cleanup has been aborted.", "YELLOW")
  253. if dir_delcount == 0 and error_count == 0 and lock_count == 0:
  254. log('[I] No folders were removed.', "YELLOW")
  255. seperator("GREEN")
  256. return
  257. log('[I] Folders removed: {:d}'.format(dir_delcount), "YELLOW")
  258. log('[I] Locked folders: {:d}'.format(lock_count), "YELLOW")
  259. log('[I] Errors: {:d}'.format(error_count), "YELLOW")
  260. seperator("GREEN")
  261. def run():
  262. logging.disable(logging.CRITICAL)
  263. config = configparser.ConfigParser()
  264. parser = argparse.ArgumentParser(description='You are running PyInstaLive ' + script_version + " using Python " + python_version)
  265. parser.add_argument('-u', '--username', dest='username', type=str, required=False, help="Instagram username to login with.")
  266. parser.add_argument('-p', '--password', dest='password', type=str, required=False, help="Instagram password to login with.")
  267. parser.add_argument('-r', '--record', dest='record', type=str, required=False, help="The username of the user whose livestream or replay you want to save.")
  268. parser.add_argument('-i', '--info', dest='info', action='store_true', help="View information about PyInstaLive.")
  269. parser.add_argument('-c', '--config', dest='config', action='store_true', help="Create a default configuration file if it doesn't exist.")
  270. parser.add_argument('-nr', '--noreplays', dest='noreplays', action='store_true', help="When used, do not check for any available replays.")
  271. parser.add_argument('-cl', '--clean', dest='clean', action='store_true', help="PyInstaLive will clean the current download folder of all leftover files.")
  272. # Workaround to 'disable' argument abbreviations
  273. parser.add_argument('--usernamx', help=argparse.SUPPRESS, metavar='IGNORE')
  274. parser.add_argument('--passworx', help=argparse.SUPPRESS, metavar='IGNORE')
  275. parser.add_argument('--recorx', help=argparse.SUPPRESS, metavar='IGNORE')
  276. parser.add_argument('--infx', help=argparse.SUPPRESS, metavar='IGNORE')
  277. parser.add_argument('--confix', help=argparse.SUPPRESS, metavar='IGNORE')
  278. parser.add_argument('--noreplayx', help=argparse.SUPPRESS, metavar='IGNORE')
  279. parser.add_argument('--cleax', help=argparse.SUPPRESS, metavar='IGNORE')
  280. parser.add_argument('-cx', help=argparse.SUPPRESS, metavar='IGNORE')
  281. args, unknown = parser.parse_known_args()
  282. try:
  283. config.read('pyinstalive.ini')
  284. settings.log_to_file = config.get('pyinstalive', 'log_to_file').title()
  285. if not settings.log_to_file in bool_values:
  286. settings.log_to_file = 'False'
  287. elif settings.log_to_file == "True":
  288. if args.record:
  289. settings.user_to_record = args.record
  290. else:
  291. settings.user_to_record = "log"
  292. try:
  293. with open("pyinstalive_{:s}.log".format(settings.user_to_record),"a+") as f:
  294. f.write("\n")
  295. f.close()
  296. except:
  297. pass
  298. except Exception as e:
  299. settings.log_to_file = 'False'
  300. pass # Pretend nothing happened
  301. seperator("GREEN")
  302. log('PYINSTALIVE (SCRIPT V{:s} - PYTHON V{:s}) - {:s}'.format(script_version, python_version, time.strftime('%I:%M:%S %p')), "GREEN")
  303. seperator("GREEN")
  304. if unknown:
  305. log("[E] The following invalid argument(s) were provided: ", "RED")
  306. log('', "GREEN")
  307. log(' '.join(unknown), "YELLOW")
  308. log('', "GREEN")
  309. if (supports_color()[1] == True):
  310. log("[I] \033[94mpyinstalive -h\033[92m can be used to display command help.", "GREEN")
  311. else:
  312. log("[I] pyinstalive -h can be used to display command help.", "GREEN")
  313. exit(1)
  314. if (args.info) or (not
  315. args.username and not
  316. args.password and not
  317. args.record and not
  318. args.info and not
  319. args.config and not
  320. args.noreplays and not
  321. args.clean):
  322. show_info(config)
  323. sys.exit(0)
  324. if (args.config):
  325. new_config()
  326. sys.exit(0)
  327. if os.path.exists('pyinstalive.ini'):
  328. try:
  329. config.read('pyinstalive.ini')
  330. except Exception:
  331. log("[E] Could not read configuration file.", "RED")
  332. seperator("GREEN")
  333. else:
  334. new_config()
  335. sys.exit(1)
  336. if check_config_validity(config):
  337. if (args.clean):
  338. clean_download_dir()
  339. sys.exit(0)
  340. if not check_ffmpeg():
  341. log("[E] Could not find ffmpeg, the script will now exit. ", "RED")
  342. seperator("GREEN")
  343. sys.exit(1)
  344. if (args.record == None):
  345. log("[E] Missing --record argument. Please specify an Instagram username.", "RED")
  346. seperator("GREEN")
  347. sys.exit(1)
  348. if (args.noreplays):
  349. settings.save_replays = "false"
  350. if (args.username is not None) and (args.password is not None):
  351. api = login(args.username, args.password, settings.show_cookie_expiry, True)
  352. elif (args.username is not None) or (args.password is not None):
  353. log("[W] Missing -u or -p arguments, falling back to config file...", "YELLOW")
  354. if (not len(settings.username) > 0) or (not len(settings.password) > 0):
  355. log("[E] Username or password are missing. Please check your configuration settings and try again.", "RED")
  356. seperator("GREEN")
  357. sys.exit(1)
  358. else:
  359. api = login(settings.username, settings.password, settings.show_cookie_expiry, False)
  360. else:
  361. if (not len(settings.username) > 0) or (not len(settings.password) > 0):
  362. log("[E] Username or password are missing. Please check your configuration settings and try again.", "RED")
  363. seperator("GREEN")
  364. sys.exit(1)
  365. else:
  366. api = login(settings.username, settings.password, settings.show_cookie_expiry, False)
  367. main(api, args.record, settings)
  368. else:
  369. log("[E] The configuration file is not valid. Please check your configuration settings and try again.", "RED")
  370. seperator("GREEN")
  371. sys.exit(1)