def merge_segments(): try: if pil.run_at_finish: try: thread = threading.Thread(target=helpers.run_command, args=(pil.run_at_finish, )) thread.daemon = True thread.start() logger.binfo("Launched finish command: {:s}".format( pil.run_at_finish)) except Exception as e: logger.warn('Could not execute command: {:s}'.format(str(e))) live_mp4_file = '{}{}_{}_{}_{}_live.mp4'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) live_segments_path = os.path.normpath( pil.broadcast_downloader.output_dir) if pil.segments_json_thread_worker and pil.segments_json_thread_worker.is_alive( ): pil.segments_json_thread_worker.join() if pil.comment_thread_worker and pil.comment_thread_worker.is_alive(): logger.info("Waiting for comment downloader to finish.") pil.comment_thread_worker.join() logger.info('Merging downloaded files into video.') try: pil.broadcast_downloader.stitch( live_mp4_file, cleartempfiles=pil.clear_temp_files) logger.info('Successfully merged downloaded files into video.') if pil.clear_temp_files: helpers.remove_temp_folder() helpers.remove_lock() except ValueError as e: logger.separator() logger.error('Could not merge downloaded files: {:s}'.format( str(e))) if os.listdir(live_segments_path): logger.separator() logger.binfo( "Segment directory is not empty. Trying to merge again.") logger.separator() pil.assemble_arg = live_mp4_file.replace( ".mp4", "_downloads.json") assembler.assemble(user_called=False) else: logger.separator() logger.error( "Segment directory is empty. There is nothing to merge.") logger.separator() helpers.remove_lock() except Exception as e: logger.error('Could not merge downloaded files: {:s}'.format( str(e))) helpers.remove_lock() except KeyboardInterrupt: logger.binfo('Aborted merging process, no video was created.') helpers.remove_lock()
def start(): if pil.args.downloadfollowing: if not pil.dl_lives: logger.binfo("Livestream downloading is disabled either with an argument or in the config file.") logger.separator() if not pil.dl_replays: logger.binfo("Replay downloading is disabled either with an argument or in the config file.") logger.separator() if not helpers.command_exists("pyinstalive"): logger.error("PyInstaLive must be properly installed when using the -df argument.") logger.separator() else: dlfuncs.download_following() else: if not helpers.download_folder_has_lockfile(): helpers.create_lock_user() checking_self = pil.dl_user == pil.ig_api.authenticated_user_name if dlfuncs.get_broadcasts_info(): if pil.dl_lives: if checking_self: logger.warn("Login with a different account to download your own livestreams.") elif pil.livestream_obj: logger.info("Livestream available, starting download.") dlfuncs.download_livestream() else: logger.info('There are no available livestreams.') else: logger.binfo("Livestream downloading is disabled either with an argument or in the config file.") logger.separator() if pil.dl_replays: if pil.replays_obj: logger.info( '{:s} available, beginning download.'.format("Replays" if len( pil.replays_obj) > 1 else "Replay")) dlfuncs.download_replays() else: logger.info('There are no available replays{:s}.'.format(" saved on your account" if checking_self else "")) else: logger.binfo("Replay downloading is disabled either with an argument or in the config file.") helpers.remove_lock() logger.separator() else: logger.warn("Lock file is already present for this user, there is probably another download ongoing.") logger.warn("If this is not the case, manually delete the file '{:s}' and try again.".format( pil.dl_user + '.lock')) logger.separator()
def download_replays(): try: try: logger.info('Amount of replays : {:s}'.format( str(len(pil.replays_obj)))) for replay_index, replay_obj in enumerate(pil.replays_obj): bc_dash_manifest = parseString(replay_obj.get( 'dash_manifest')).getElementsByTagName('Period') bc_duration_raw = bc_dash_manifest[0].getAttribute("duration") bc_minutes = (bc_duration_raw.split("H"))[1].split("M")[0] bc_seconds = (( bc_duration_raw.split("M"))[1].split("S")[0]).split('.')[0] logger.info( 'Replay {:s} duration : {:s} minutes and {:s} seconds'. format(str(replay_index + 1), bc_minutes, bc_seconds)) except Exception as e: logger.warn( "An error occurred while getting replay duration information: {:s}" .format(str(e))) logger.separator() logger.info("Downloading replays, press [CTRL+C] to abort.") logger.separator() for replay_index, replay_obj in enumerate(pil.replays_obj): exists = False pil.livestream_obj = replay_obj if Constants.PYTHON_VER[0][0] == '2': directories = (os.walk(pil.dl_path).next()[1]) else: directories = (os.walk(pil.dl_path).__next__()[1]) for directory in directories: if (str(replay_obj.get('id')) in directory) and ("_live_" not in directory): logger.binfo( "Already downloaded a replay with ID '{:s}'.".format( str(replay_obj.get('id')))) exists = True if not exists: current = replay_index + 1 logger.info( "Downloading replay {:s} of {:s} with ID '{:s}'.".format( str(current), str(len(pil.replays_obj)), str(replay_obj.get('id')))) pil.live_folder_path = '{}{}_{}_{}_{}_replay_downloads'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) broadcast_downloader = replay.Downloader( mpd=replay_obj.get('dash_manifest'), output_dir=pil.live_folder_path, user_agent=pil.ig_api.user_agent, ffmpeg_binary=pil.ffmpeg_path) if pil.use_locks: helpers.create_lock_folder() replay_mp4_file = '{}{}_{}_{}_{}_replay.mp4'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) comments_json_file = '{}{}_{}_{}_{}_replay_comments.json'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) pil.comment_thread_worker = threading.Thread( target=get_replay_comments, args=(comments_json_file, )) broadcast_downloader.download( replay_mp4_file, cleartempfiles=pil.clear_temp_files) if pil.clear_temp_files: helpers.remove_temp_folder() if pil.dl_comments: logger.info("Downloading replay comments.") try: get_replay_comments(comments_json_file) except Exception as e: logger.error( 'An error occurred while downloading comments: {:s}' .format(str(e))) logger.info("Finished downloading replay {:s} of {:s}.".format( str(current), str(len(pil.replays_obj)))) helpers.remove_lock() if current != len(pil.replays_obj): logger.separator() logger.separator() logger.info("Finished downloading all available replays.") helpers.remove_lock() except Exception as e: logger.error('Could not save replay: {:s}'.format(str(e))) helpers.remove_lock() except KeyboardInterrupt: logger.separator() logger.binfo('The download has been aborted by the user, exiting.') logger.separator() helpers.remove_temp_folder() helpers.remove_lock()
def download_livestream(): try: def print_status(sep=True): if pil.do_heartbeat: heartbeat_info = pil.ig_api.broadcast_heartbeat_and_viewercount( pil.livestream_obj.get('id')) viewers = pil.livestream_obj.get('viewer_count', 0) if sep: logger.separator() else: logger.info('Username : {:s}'.format(pil.dl_user)) logger.info('Viewers : {:s} watching'.format(str( int(viewers)))) logger.info('Airing time : {:s}'.format(get_stream_duration(0))) if pil.do_heartbeat: logger.info('Status : {:s}'.format( heartbeat_info.get('broadcast_status').title())) return heartbeat_info.get('broadcast_status') not in [ 'active', 'interrupted' ] else: return None mpd_url = (pil.livestream_obj.get('dash_manifest') or pil.livestream_obj.get('dash_abr_playback_url') or pil.livestream_obj.get('dash_playback_url')) pil.live_folder_path = '{}{}_{}_{}_{}_live_downloads'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) pil.broadcast_downloader = live.Downloader( mpd=mpd_url, output_dir=pil.live_folder_path, user_agent=pil.ig_api.user_agent, max_connection_error_retry=3, duplicate_etag_retry=30, callback_check=print_status, mpd_download_timeout=3, download_timeout=3, ffmpeg_binary=pil.ffmpeg_path) except Exception as e: logger.error('Could not start downloading livestream: {:s}'.format( str(e))) logger.separator() helpers.remove_lock() try: broadcast_owner = pil.livestream_obj.get('broadcast_owner', {}).get('username') try: broadcast_guest = pil.livestream_obj.get('cobroadcasters', {})[0].get('username') except Exception: broadcast_guest = None if broadcast_owner != pil.dl_user: logger.binfo( 'This livestream is a dual-live, the owner is "{}".'.format( broadcast_owner)) broadcast_guest = None if broadcast_guest: logger.binfo( 'This livestream is a dual-live, the current guest is "{}".'. format(broadcast_guest)) pil.has_guest = broadcast_guest logger.separator() print_status(False) logger.separator() helpers.create_lock_folder() pil.segments_json_thread_worker = threading.Thread( target=helpers.generate_json_segments) pil.segments_json_thread_worker.start() logger.info('Downloading livestream, press [CTRL+C] to abort.') if pil.run_at_start: try: thread = threading.Thread(target=helpers.run_command, args=(pil.run_at_start, )) thread.daemon = True thread.start() logger.binfo("Launched start command: {:s}".format( pil.run_at_start)) except Exception as e: logger.warn('Could not launch command: {:s}'.format(str(e))) if pil.dl_comments: try: comments_json_file = '{}{}_{}_{}_{}_live_comments.json'.format( pil.dl_path, pil.datetime_compat, pil.dl_user, pil.livestream_obj.get('id'), pil.epochtime) pil.comment_thread_worker = threading.Thread( target=get_live_comments, args=(comments_json_file, )) pil.comment_thread_worker.start() except Exception as e: logger.error( 'An error occurred while downloading comments: {:s}'. format(str(e))) pil.broadcast_downloader.run() logger.separator() logger.info("The livestream has been ended by the user.") logger.separator() logger.info('Airtime duration : {}'.format(get_stream_duration(0))) logger.info('Download duration : {}'.format(get_stream_duration(1))) logger.info('Missing (approx.) : {}'.format(get_stream_duration(2))) logger.separator() merge_segments() except KeyboardInterrupt: logger.separator() logger.binfo('The download has been aborted.') logger.separator() logger.info('Airtime duration : {}'.format(get_stream_duration(0))) logger.info('Download duration : {}'.format(get_stream_duration(1))) logger.info('Missing (approx.) : {}'.format(get_stream_duration(2))) logger.separator() if not pil.broadcast_downloader.is_aborted: pil.broadcast_downloader.stop() merge_segments()