def destroy_fcreplay(failed=False): """Destry the current compute engine Checks for the existance of /tmp/destroying. If it exists then don't try and destroy fcreplay Args: failed (bool, optional): Updates the replay to failed. Defaults to False. """ # Create destroying file try: Path('/tmp/destroying').touch(0o644, exist_ok=False) except FileExistsError: # File already exists, not running sys.exit(0) logging.info("Starting destroy_fcreplay") RECEIVING_FUNCTION = 'destroy_fcreplay_instance' HOSTNAME = socket.gethostname() if 'fcreplay-image-' not in HOSTNAME: logging.info(f"Not destroying {HOSTNAME}") return(False) # Only retry if failed is false, by default this is false, but sometimes recording # fails. So we don't want to try and re-record them until we work out why they # have failed. if failed is False: try: with open('/tmp/fcreplay_status', 'r') as f: line = f.readline() local_replay_id = line.split()[0].strip() local_replay_status = line.split()[1].strip() if local_replay_status in ['UPLOADING_TO_IA', 'UPLOADING_TO_YOUTUBE', 'UPLOADED_TO_IA', 'UPLOADED_TO_YOUTUBE']: logging.error(f"Not able to safely recover replay {local_replay_id}") elif local_replay_status not in ['FINISHED', 'REMOVED_GENERATED_FILES']: # Replay was in the middle of processing, going to set replay to be re-recorded db = Database() db.rerecord_replay(challenge_id=local_replay_id) except FileNotFoundError: logging.error('/tmp/fcreplay_status not found') function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}' metadata_server_url = \ f"http://metadata/computeMetadata/v1/instance/service-accounts/{config['gcloud_compute_service_account']}/identity?audience=" token_full_url = metadata_server_url + function_url token_headers = {'Metadata-Flavor': 'Google'} # Fetch the token token_response = requests.get(token_full_url, headers=token_headers) jwt = token_response.text # Provide the token in the request to the receiving function function_headers = {'Authorization': f'bearer {jwt}'} function_response = requests.post(function_url, headers=function_headers, json={'instance_name': HOSTNAME}) logging.info(f"destroy_fcreplay retruned: {function_response.status_code}") status = function_response.status_code return(status)
def get_data(query): r = requests.post("https://www.fightcade.com/api/", json=query) if r.status_code == 500: logging.error("500 Code, trying up to 3 times") raise IOError("Unable to get data") else: return r
def __init__(self): config = Config().config # Create Engine try: self.engine = create_engine(config['sql_baseurl'], echo=True) Base.metadata.create_all(self.engine) except Exception as e: logging.error(f"Unable to connect to {config['sql_baseurl']}: {e}") self.Session = sessionmaker(bind=self.engine)
def failed(self, *args, **kwargs): try: return func(self, *args, **kwargs) except Exception as e: trace_back = sys.exc_info()[2] logging.error( f"Excption: {str(traceback.format_tb(trace_back))}, shutting down" ) logging.info(f"Setting {self.replay.id} to failed") self.db.update_failed_replay(challenge_id=self.replay.id) self.update_status("FAILED") if self.config['gcloud_destroy_on_fail']: destroy_fcreplay(failed=True) sys.exit(1)
def record(self): """Start recording a replay """ logging.info( f"Starting capture with {self.replay.id} and {self.replay.length}") time_min = int(self.replay.length / 60) logging.info(f"Capture will take {time_min} minutes") self.update_status('RECORDING') # Star a recording store recording status logging.debug(f"""Starting record.main with argumens: fc_challange_id={self.replay.id}, fc_time={self.replay.length}, kill_time={self.config['record_timeout']}, fcadefbneo_path={self.config['fcadefbneo_path']}, fcreplay_path={self.config['fcreplay_dir']}, game_name={self.replay.game}""") record_status = fc_record.main( fc_challange_id=self.replay.id, fc_time=self.replay.length, kill_time=self.config['record_timeout'], fcadefbneo_path=self.config['fcadefbneo_path'], fcreplay_path=self.config['fcreplay_dir'], game_name=self.replay.game) # Check recording status if not record_status == "Pass": logging.error(f"Recording failed on {self.replay.id}," "Status: \"{record_status}\", exiting.") if record_status == "FailTimeout": raise TimeoutError else: logging.error(f"Unknown error: ${record_status}, exiting") raise ValueError logging.info("Capture finished") self.update_status('RECORDED') return True
def set_description(self): """Set the description of the video Returns: Boolean: Success or failure """ logging.info("Creating description") self.description_text = f"({self.replay.p1_loc}) {self.replay.p1} vs " \ f"({self.replay.p2_loc}) {self.replay.p2} - {self.replay.date_replay}" \ f"\nFightcade replay id: {self.replay.id}" # Read the append file: if self.config['description_append_file'][0] is True: # Check if file exists: if not os.path.exists(self.config['description_append_file'][1]): logging.error( f"Description append file {self.config['description_append_file'][1]} doesn't exist" ) return False else: with open(self.config['description_append_file'][1], 'r') as description_append: self.description_text += "\n" + description_append.read() self.update_status('DESCRIPTION_CREATED') logging.info("Finished creating description") # Add description to database logging.info('Adding description to database') self.db.add_description(challenge_id=self.replay.id, description=self.description_text) logging.debug( f"Description Text is: {self.description_text.encode('unicode-escape')}" ) return True
def encode(self): logging.info("Encoding file") avi_files_list = os.listdir(f"{self.config['fcreplay_dir']}/finished") avi_dict = { i: int(i.split('_')[1].split('.')[0], 16) for i in avi_files_list } sorted_avi_files_list = [] for i in sorted(avi_dict.items(), key=lambda x: x[1]): sorted_avi_files_list.append(i[0]) avi_files = [ f"{self.config['fcreplay_dir']}/finished/" + i for i in sorted_avi_files_list ] logging.info("Running mencoder with:" + " ".join([ 'mencoder', '-oac', 'mp3lame', '-lameopts', 'abr:br=128', '-ovc', 'x264', '-x264encopts', 'preset=fast:crf=23:subq=1:threads=8', '-vf', 'flip,scale=800:600,dsize=4/3', *avi_files, '-o', f"{self.config['fcreplay_dir']}/finished/{self.replay.id}.mkv" ])) mencoder_rc = subprocess.run([ 'mencoder', '-oac', 'mp3lame', '-lameopts', 'abr:br=128', '-ovc', 'x264', '-x264encopts', 'preset=slow:crf=23:subq=1:threads=8', '-vf', 'flip,scale=800:600,dsize=4/3', *avi_files, '-o', f"{self.config['fcreplay_dir']}/finished/{self.replay.id}.mkv" ], capture_output=True) try: mencoder_rc.check_returncode() except subprocess.CalledProcessError as e: logging.error( f"Unable to process avi files. Return code: {e.returncode}, stdout: {mencoder_rc.stdout}, stderr: {mencoder_rc.stderr}" ) raise e
def upload_to_yt(self): """Upload video to youtube """ self.update_status('UPLOADING_TO_YOUTUBE') title = f"{self.config['supported_games'][self.replay.game]['game_name']}: ({self.replay.p1_loc}) {self.replay.p1} vs "\ f"({self.replay.p2_loc}) {self.replay.p2} - {self.replay.date_replay}" filename = f"{self.replay.id}.mkv" import_format = '%Y-%m-%d %H:%M:%S' date_raw = datetime.datetime.strptime(str(self.replay.date_replay), import_format) # YYYY-MM-DDThh:mm:ss.sZ youtube_date = date_raw.strftime('%Y-%m-%dT%H:%M:%S.0Z') # Check if youtube-upload is installed if shutil.which('youtube-upload') is not None: # Check if credentials file exists if not os.path.exists(self.config['youtube_credentials']): logging.error("Youtube credentials don't exist exist") return False if not os.path.exists(self.config['youtube_secrets']): logging.error("Youtube secrets don't exist") return False # Check min and max length: if (int(self.replay.length) / 60) < int( self.config['yt_min_length']): logging.info("Replay is too short. Not uploading to youtube") return False if (int(self.replay.length) / 60) > int( self.config['yt_max_length']): logging.info("Replay is too long. Not uploading to youtube") return False # Find number of uploads today day_log = self.db.get_youtube_day_log() # Check max uploads # Get todays date, dd-mm-yyyy today = datetime.date.today() # Check the log is for today if day_log.date.date() == today: # Check number of uploads if day_log.count >= int( self.config['youtube_max_daily_uploads']): logging.info("Maximum uploads reached for today") return False else: # It's a new day, update the counter logging.info("New day for youtube uploads") self.db.update_youtube_day_log_count(count=1, date=today) # Create description file with open(f"{self.config['fcreplay_dir']}/tmp/description.txt", 'w') as description_file: description_file.write(self.description_text) # Do upload logging.info("Uploading to youtube") yt_rc = subprocess.run([ 'youtube-upload', '--credentials-file', self.config['youtube_credentials'], '--client-secrets', self.config['youtube_secrets'], '-t', title, '-c', 'Gaming', '--description-file', f"{self.config['fcreplay_dir']}/tmp/description.txt", '--recording-date', youtube_date, '--default-language', 'en', '--thumbnail', f"{self.config['fcreplay_dir']}/tmp/thumbnail.jpg", f"{self.config['fcreplay_dir']}/finished/{filename}", ], stderr=subprocess.PIPE, stdout=subprocess.PIPE) logging.info(yt_rc.stdout.decode()) logging.info(yt_rc.stderr.decode()) if not self.replay.player_requested: logging.info('Updating day_log') logging.info("Updating counter") self.db.update_youtube_day_log_count(count=day_log.count + 1, date=today) # Remove description file os.remove(f"{self.config['fcreplay_dir']}/tmp/description.txt") self.update_status('UPLOADED_TO_YOUTUBE') logging.info('Finished uploading to Youtube') else: raise ModuleNotFoundError