def __init__(self, dirPath, myFFmpeg: bool, FFdebug, log): self.mylog = log self.FFdebug = FFdebug newF = None if (system() == 'Windows' and not myFFmpeg): newF = path.join(dirPath, f'win-ffmpeg{sep()}bin{sep()}ffmpeg.exe') if (system() == 'Darwin' and not myFFmpeg): newF = path.join(dirPath, f'mac-ffmpeg{sep()}bin{sep()}ffmpeg') if (newF is not None and path.isfile(newF)): self.myPath = newF else: self.myPath = 'ffmpeg' try: pipeToConsole([self.myPath, '-h']) except FileNotFoundError: if (system() == 'Darwin'): self.mylog.error('No ffmpeg found, download via homebrew or restore' \ ' the included binaries.') elif (system() == 'Windows'): self.mylog.error('No ffmpeg found, download ffmpeg with your' \ ' favorite package manager (ex chocolatey), or restore the' \ ' included binaries.') else: self.mylog.error('FFmpeg must be on PATH. Download ffmpeg by running:\n' \ ' sudo apt-get install libavformat-dev libavfilter-dev libavdevice-dev ffmpeg' \ '\nOr something similar depending on your distro.')
def pipe(self, cmd: list) -> str: full_cmd = [self.myPath, '-v', 'error'] + cmd self.mylog.debug(full_cmd) output = pipeToConsole(full_cmd) self.mylog.debug(output) return output
def pipe(self, cmd: list) -> str: cmd = [self.myPath, '-y'] + cmd self.mylog.debug(cmd) output = pipeToConsole(cmd) self.mylog.debug(output) return output
def ffmpegFPS(ffmpeg: str, path: str, log) -> float: output = pipeToConsole([ffmpeg, '-i', path, '-hide_banner']) try: matchDict = re.search(r'\s(?P<fps>[\d\.]+?)\stbr', output).groupdict() return float(matchDict['fps']) except AttributeError: log.warning('frame rate detection failed.\n' \ 'If your video has a variable frame rate, ignore this message.') return 30.0
def getSampleRate(file: str, ffmpeg: str, sr) -> str: if (sr is None): output = pipeToConsole([ffmpeg, '-i', file, '-hide_banner']) try: matchDict = re.search(r'\s(?P<grp>\w+?)\sHz', output).groupdict() return matchDict['grp'] except AttributeError: return '48000' return str(sr)
def getVideoCodec(file: str, ffmpeg: str, log, vcodec: str) -> str: if(vcodec == 'copy'): output = pipeToConsole([ffmpeg, '-i', file, '-hide_banner']) try: matchDict = re.search(r'Video:\s(?P<grp>\w+?)\s', output).groupdict() vcodec = matchDict['grp'] log.debug('Video Codec: ' + str(vcodec)) except AttributeError: log.warning("Couldn't automatically detect video codec.") if(vcodec is None or vcodec == 'uncompressed'): vcodec = 'copy' return vcodec
def vidTracks(videoFile: str, ffprobe: str, log) -> int: """ Return the number of audio tracks in a video file. """ numbers = pipeToConsole([ ffprobe, videoFile, '-hide_banner', '-loglevel', 'panic', '-show_entries', 'stream=index', '-select_streams', 'a', '-of', 'compact=p=0:nk=1' ]).split('\n') log.ffmpeg('Track data: ' + str(numbers)) if (numbers[0].isnumeric()): return len(numbers) - 1 else: log.warning('ffprobe had an invalid output.') return 1 # Assume there's one audio track.
def getAudioBitrate(file: str, ffprobe: str, log, abit: str) -> str: if(abit is None): if(file.endswith('.mkv')): return None else: output = pipeToConsole([ffprobe, '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=bit_rate', '-of', 'compact=p=0:nk=1', file]).strip() if(output.isnumeric()): return str(round(int(output) / 1000)) + 'k' else: log.warning("Couldn't automatically detect audio bitrate.") log.debug('Setting audio bitrate to 500k') log.debug(f'Output: {output}') return '500k' return str(abit)
def vidTracks(videoFile: str, ffprobe: str, log) -> int: """ Return the number of audio tracks in a video file. """ numbers = pipeToConsole([ ffprobe, videoFile, '-hide_banner', '-loglevel', 'panic', '-show_entries', 'stream=index', '-select_streams', 'a', '-of', 'compact=p=0:nk=1' ]).split('\n') # Remove all \r chars that can appear in certain environments numbers = [s.replace('\r', '') for s in numbers] # Remove all blanks numbers = [s for s in numbers if s != ''] log.ffmpeg('Track data: ' + str(numbers)) if (numbers[0].isnumeric()): return len(numbers) else: log.warning('ffprobe had an invalid output.') return 1 # Assume there's one audio track.
def muxVideo(ffmpeg, outFile, args, tracks, temp, log): if(args.keep_tracks_seperate): cmd = [ffmpeg, '-y'] for i in range(tracks): cmd.extend(['-i', f'{temp}/new{i}.wav']) cmd.extend(['-i', f'{temp}/spedup.mp4']) for i in range(tracks): cmd.extend(['-map', f'{i}:a:0']) cmd.extend(['-map', f'{tracks}:v:0']) else: # Merge all the audio tracks into one. if(tracks > 1): cmd = [ffmpeg] for i in range(tracks): cmd.extend(['-i', f'{temp}/new{i}.wav']) cmd.extend(['-filter_complex', f'amerge=inputs={tracks}', '-ac', '2', f'{temp}/newAudioFile.wav']) cmd = ffAddDebug(cmd, log.is_ffmpeg) log.debug(cmd) subprocess.call(cmd) else: move(f'{temp}/new0.wav', f'{temp}/newAudioFile.wav') cmd = [ffmpeg, '-y', '-i', f'{temp}/newAudioFile.wav', '-i', f'{temp}/spedup.mp4'] cmd.extend(['-c:v', 'copy']) if(args.audio_codec is not None): cmd.extend(['-c:a', args.audio_codec]) cmd = ffAddDebug(cmd, log.is_ffmpeg) cmd.append(outFile) log.debug(cmd) message = pipeToConsole(cmd) log.debug(message) log.conwrite('')
def main(): dirPath = os.path.dirname(os.path.realpath(__file__)) # Fixes pip not able to find other included modules. sys.path.append(os.path.abspath(dirPath)) # Print the version if only the -v option is added. if (sys.argv[1:] == ['-v'] or sys.argv[1:] == ['-V']): print(f'Auto-Editor version {version}\nPlease use --version instead.') sys.exit() # If the users just runs: $ auto-editor if (sys.argv[1:] == []): # Print print( '\nAuto-Editor is an automatic video/audio creator and editor.\n') print( 'By default, it will detect silence and create a new video with ') print( 'those sections cut out. By changing some of the options, you can') print( 'export to a traditional editor like Premiere Pro and adjust the') print( 'edits there, adjust the pacing of the cuts, and change the method' ) print('of editing like using audio loudness and video motion to judge') print('making cuts.') print( '\nRun:\n auto-editor --help\n\nTo get the list of options.\n') sys.exit() from vanparse import ParseOptions from usefulFunctions import Log, Timer if (len(sys.argv) > 1 and sys.argv[1] == 'generate_test'): option_data = generate_options() args = ParseOptions(sys.argv[2:], Log(), option_data) if (args.help): genHelp(option_data) sys.exit() from generateTestMedia import generateTestMedia from usefulFunctions import getBinaries ffmpeg, __ = getBinaries(platform.system(), dirPath, args.my_ffmpeg) generateTestMedia(ffmpeg, args.output_file, args.fps, args.duration, args.width, args.height) sys.exit() elif (len(sys.argv) > 1 and sys.argv[1] == 'info'): option_data = info_options() args = ParseOptions(sys.argv[2:], Log(), option_data) if (args.help): genHelp(option_data) sys.exit() log = Log(False, False, False) from info import getInfo from usefulFunctions import getBinaries ffmpeg, ffprobe = getBinaries(platform.system(), dirPath, args.my_ffmpeg) getInfo(args.input, ffmpeg, ffprobe, log) sys.exit() else: option_data = main_options() args = ParseOptions(sys.argv[1:], Log(True), option_data) log = Log(args.debug, args.show_ffmpeg_debug, args.quiet) log.debug('') # Print the help screen for the entire program. if (args.help): print('\n Have an issue? Make an issue. '\ 'Visit https://github.com/wyattblue/auto-editor/issues\n') print(' The help option can also be used on a specific option:') print(' auto-editor --frame_margin --help\n') genHelp(option_data) sys.exit() del option_data if (args.version): print('Auto-Editor version', version) sys.exit() from usefulFunctions import getBinaries, pipeToConsole, ffAddDebug from mediaMetadata import vidTracks, getSampleRate, getAudioBitrate from mediaMetadata import getVideoCodec, ffmpegFPS from wavfile import read ffmpeg, ffprobe = getBinaries(platform.system(), dirPath, args.my_ffmpeg) makingDataFile = (args.export_to_premiere or args.export_to_resolve or args.export_to_final_cut_pro or args.export_as_json) is64bit = '64-bit' if sys.maxsize > 2**32 else '32-bit' if (args.debug and args.input == []): print('Python Version:', platform.python_version(), is64bit) print('Platform:', platform.system(), platform.release()) # Platform can be 'Linux', 'Darwin' (macOS), 'Java', 'Windows' ffmpegVersion = pipeToConsole([ffmpeg, '-version']).split('\n')[0] ffmpegVersion = ffmpegVersion.replace('ffmpeg version', '').strip() ffmpegVersion = ffmpegVersion.split(' ')[0] print('FFmpeg path:', ffmpeg) print('FFmpeg version:', ffmpegVersion) print('Auto-Editor version', version) sys.exit() if (is64bit == '32-bit'): log.warning('You have the 32-bit version of Python, which may lead to' \ 'memory crashes.') from usefulFunctions import isLatestVersion if (not args.quiet and isLatestVersion(version, log)): log.print('\nAuto-Editor is out of date. Run:\n') log.print(' pip3 install -U auto-editor') log.print('\nto upgrade to the latest version.\n') from argsCheck import hardArgsCheck, softArgsCheck hardArgsCheck(args, log) args = softArgsCheck(args, log) from validateInput import validInput inputList = validInput(args.input, ffmpeg, log) timer = Timer(args.quiet) # Figure out the output file names. def newOutputName(oldFile: str, exa=False, data=False, exc=False) -> str: dotIndex = oldFile.rfind('.') if (exc): return oldFile[:dotIndex] + '.json' elif (data): return oldFile[:dotIndex] + '.xml' ext = oldFile[dotIndex:] if (exa): ext = '.wav' return oldFile[:dotIndex] + '_ALTERED' + ext if (len(args.output_file) < len(inputList)): for i in range(len(inputList) - len(args.output_file)): args.output_file.append( newOutputName(inputList[i], args.export_as_audio, makingDataFile, args.export_as_json)) TEMP = tempfile.mkdtemp() log.debug(f'\n - Temp Directory: {TEMP}') if (args.combine_files): # Combine video files, then set input to 'combined.mp4'. cmd = [ffmpeg, '-y'] for fileref in inputList: cmd.extend(['-i', fileref]) cmd.extend([ '-filter_complex', f'[0:v]concat=n={len(inputList)}:v=1:a=1', '-codec:v', 'h264', '-pix_fmt', 'yuv420p', '-strict', '-2', f'{TEMP}/combined.mp4' ]) cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) inputList = [f'{TEMP}/combined.mp4'] speeds = [args.silent_speed, args.video_speed] log.debug(f' - Speeds: {speeds}') audioExtensions = [ '.wav', '.mp3', '.m4a', '.aiff', '.flac', '.ogg', '.oga', '.acc', '.nfa', '.mka' ] # videoExtensions = ['.mp4', '.mkv', '.mov', '.webm', '.ogv'] for i, INPUT_FILE in enumerate(inputList): fileFormat = INPUT_FILE[INPUT_FILE.rfind('.'):] chunks = None if (fileFormat == '.json'): log.debug('Reading .json file') from makeCutList import readCutList INPUT_FILE, chunks, speeds = readCutList(INPUT_FILE, version, log) newOutput = newOutputName(INPUT_FILE, args.export_as_audio, makingDataFile, False) fileFormat = INPUT_FILE[INPUT_FILE.rfind('.'):] else: newOutput = args.output_file[i] log.debug(f' - INPUT_FILE: {INPUT_FILE}') log.debug(f' - newOutput: {newOutput}') if (os.path.isfile(newOutput) and INPUT_FILE != newOutput): log.debug(f' Removing already existing file: {newOutput}') os.remove(newOutput) sampleRate = getSampleRate(INPUT_FILE, ffmpeg, args.sample_rate) audioBitrate = getAudioBitrate(INPUT_FILE, ffprobe, log, args.audio_bitrate) log.debug(f' - sampleRate: {sampleRate}') log.debug(f' - audioBitrate: {audioBitrate}') audioFile = fileFormat in audioExtensions if (audioFile): if (args.force_fps_to is None): fps = 30 # Audio files don't have frames, so give fps a dummy value. else: fps = args.force_fps_to if (args.force_tracks_to is None): tracks = 1 else: tracks = args.force_tracks_to cmd = [ffmpeg, '-y', '-i', INPUT_FILE] if (audioBitrate is not None): cmd.extend(['-b:a', audioBitrate]) cmd.extend( ['-ac', '2', '-ar', sampleRate, '-vn', f'{TEMP}/fastAud.wav']) cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) sampleRate, audioData = read(f'{TEMP}/fastAud.wav') else: if (args.force_fps_to is not None): fps = args.force_fps_to elif (args.export_to_premiere or args.export_to_final_cut_pro): # This is the default fps value for Premiere Pro Projects. fps = 29.97 else: # Don't know the correct fps to set so get the input's fps = ffmpegFPS(ffmpeg, INPUT_FILE, log) log.debug(f'ffmpeg fps: {fps}') tracks = args.force_tracks_to if (tracks is None): tracks = vidTracks(INPUT_FILE, ffprobe, log) if (args.cut_by_this_track >= tracks): allTracks = '' for trackNum in range(tracks): allTracks += f'Track {trackNum}\n' if (tracks == 1): message = f'is only {tracks} track' else: message = f'are only {tracks} tracks' log.error("You choose a track that doesn't exist.\n" \ f'There {message}.\n {allTracks}') vcodec = getVideoCodec(INPUT_FILE, ffmpeg, log, args.video_codec) # Split audio tracks into: 0.wav, 1.wav, etc. for trackNum in range(tracks): cmd = [ffmpeg, '-y', '-i', INPUT_FILE] if (audioBitrate is not None): cmd.extend(['-ab', audioBitrate]) cmd.extend([ '-ac', '2', '-ar', sampleRate, '-map', f'0:a:{trackNum}', f'{TEMP}/{trackNum}.wav' ]) cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) # Check if the `--cut_by_all_tracks` flag has been set or not. if (args.cut_by_all_tracks): # Combine all audio tracks into one audio file, then read. cmd = [ ffmpeg, '-y', '-i', INPUT_FILE, '-filter_complex', f'[0:a]amix=inputs={tracks}:duration=longest', '-ar', sampleRate, '-ac', '2', '-f', 'wav', f'{TEMP}/combined.wav' ] cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) sampleRate, audioData = read(f'{TEMP}/combined.wav') else: # Read only one audio file. if (os.path.isfile(f'{TEMP}/{args.cut_by_this_track}.wav')): sampleRate, audioData = read( f'{TEMP}/{args.cut_by_this_track}.wav') else: log.bug('Audio track not found!') log.debug(f' - Frame Rate: {fps}') if (chunks is None): from cutting import audioToHasLoud, motionDetection audioList = None motionList = None if ('audio' in args.edit_based_on): log.debug('Analyzing audio volume.') audioList = audioToHasLoud(audioData, sampleRate, args.silent_threshold, fps, log) if ('motion' in args.edit_based_on): log.debug('Analyzing video motion.') motionList = motionDetection(INPUT_FILE, ffprobe, args.motion_threshold, log, width=args.width, dilates=args.dilates, blur=args.blur) if (audioList is not None): if (len(audioList) != len(motionList)): log.debug(f'audioList Length: {len(audioList)}') log.debug(f'motionList Length: {len(motionList)}') if (len(audioList) > len(motionList)): log.debug( 'Reducing the size of audioList to match motionList.' ) audioList = audioList[:len(motionList)] elif (len(motionList) > len(audioList)): log.debug( 'Reducing the size of motionList to match audioList.' ) motionList = motionList[:len(audioList)] from cutting import combineArrs, applySpacingRules hasLoud = combineArrs(audioList, motionList, args.edit_based_on, log) del audioList, motionList chunks, includeFrame = applySpacingRules( hasLoud, fps, args.frame_margin, args.min_clip_length, args.min_cut_length, args.ignore, args.cut_out, log) del hasLoud else: from cutting import generateIncludes includeFrame = generateIncludes(chunks, log) clips = [] numCuts = len(chunks) for chunk in chunks: if (speeds[chunk[2]] != 99999): clips.append([chunk[0], chunk[1], speeds[chunk[2]] * 100]) if (fps is None and not audioFile): if (makingDataFile): dotIndex = INPUT_FILE.rfind('.') end = '_constantFPS' + INPUT_FILE[dotIndex:] constantLoc = INPUT_FILE[:dotIndex] + end else: constantLoc = f'{TEMP}/constantVid{fileFormat}' cmd = [ ffmpeg, '-y', '-i', INPUT_FILE, '-filter:v', 'fps=fps=30', constantLoc ] cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) INPUT_FILE = constantLoc if (args.export_as_json): from makeCutList import makeCutList makeCutList(INPUT_FILE, newOutput, version, chunks, speeds, log) continue if (args.preview): newOutput = None from preview import preview preview(INPUT_FILE, chunks, speeds, fps, audioFile, log) continue if (args.export_to_premiere or args.export_to_resolve): from editor import editorXML editorXML(INPUT_FILE, TEMP, newOutput, clips, chunks, tracks, sampleRate, audioFile, fps, log) continue if (audioFile): from fastAudio import fastAudio, handleAudio theFile = handleAudio(ffmpeg, INPUT_FILE, audioBitrate, str(sampleRate), TEMP, log) fastAudio(theFile, newOutput, chunks, speeds, log, fps, args.machine_readable_progress, args.no_progress) continue from fastVideo import handleAudioTracks, fastVideo, muxVideo continueVid = handleAudioTracks(ffmpeg, newOutput, args.export_as_audio, tracks, args.keep_tracks_seperate, chunks, speeds, fps, TEMP, args.machine_readable_progress, args.no_progress, log) if (continueVid): fastVideo(INPUT_FILE, chunks, includeFrame, speeds, fps, args.machine_readable_progress, args.no_progress, TEMP, log) muxVideo(ffmpeg, newOutput, args.keep_tracks_seperate, tracks, args.video_bitrate, args.tune, args.preset, vcodec, args.audio_codec, args.constant_rate_factor, TEMP, log) if (newOutput is not None and not os.path.isfile(newOutput)): log.bug(f'The file {newOutput} was not created.') if (not args.preview and not makingDataFile): timer.stop() if (not args.preview and makingDataFile): from usefulFunctions import humanReadableTime # Assume making each cut takes about 30 seconds. timeSave = humanReadableTime(numCuts * 30) s = 's' if numCuts != 1 else '' log.print(f'Auto-Editor made {numCuts} cut{s}', end='') log.print( f', which would have taken about {timeSave} if edited manually.') if (not args.no_open): from usefulFunctions import smartOpen smartOpen(newOutput, log) log.debug('Deleting temp dir') rmtree(TEMP)
def getInfo(files, ffmpeg, ffprobe, log): for file in files: print(f'file: {file}') hasVid = pipeToConsole([ ffprobe, '-v', 'error', '-show_streams', '-select_streams', 'v', file ]) hasAud = pipeToConsole([ ffprobe, '-v', 'error', '-show_streams', '-select_streams', 'a', file ]) hasAud = len(hasAud) > 5 hasVid = len(hasVid) > 5 if (hasVid): fps = ffmpegFPS(ffmpeg, file, log) print(f' - fps: {fps}') res = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=height,width', '-of', 'csv=s=x:p=0', file ]) print(f' - resolution: {res.strip()}') raw_data = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=codec_name,bit_rate', '-of', 'compact=p=0:nk=1', file ]).split('|') print(f' - video codec: {raw_data[0]}') if (raw_data[1].isnumeric()): vbit = str(int(int(raw_data[1]) / 1000)) + 'k' print(f' - video bitrate: {vbit}') if (hasAud): tracks = vidTracks(file, ffprobe, log) print(f' - audio tracks: {tracks}') for track in range(tracks): print(f' - Track #{track}') raw_data = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', f'a:{track}', '-show_entries', 'stream=codec_name,sample_rate', '-of', 'compact=p=0:nk=1', file ]) raw_data = raw_data.split('|') acod = raw_data[0] sr = str(int(raw_data[1]) / 1000) + ' kHz' print(f' - codec: {acod}') print(f' - samplerate: {sr}') output = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', f'a:{track}', '-show_entries', 'stream=bit_rate', '-of', 'compact=p=0:nk=1', file ]).strip() if (output.isnumeric()): abit = str(round(int(output) / 1000)) + 'k' print(f' - bitrate: {abit}') else: print(' - audio tracks: 0') elif (hasAud): raw_data = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=codec_name,sample_rate', '-of', 'compact=p=0:nk=1', file ]).split('|') acod = raw_data[0] sr = str(int(raw_data[1]) / 1000) + ' kHz' print(f' - codec: {acod}') print(f' - samplerate: {sr}') output = pipeToConsole([ ffprobe, '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=bit_rate', '-of', 'compact=p=0:nk=1', file ]).strip() if (output.isnumeric()): abit = str(round(int(output) / 1000)) + 'k' print(f' - bitrate: {abit}') else: print('Invalid media.') print('')
def muxVideo(ffmpeg, outFile, keepTracksSep, tracks, vbitrate, tune, preset, vcodec, acodec, crf, temp, log): def extender(cmd, vbitrate, tune, preset, acodec, outFile, isFFmpeg): if (acodec is not None): cmd.extend(['-acodec', acodec]) if (vbitrate is None): cmd.extend(['-crf', crf]) else: cmd.extend(['-b:v', vbitrate]) if (tune != 'none'): cmd.extend(['-tune', tune]) cmd.extend([ '-preset', preset, '-movflags', '+faststart', '-strict', '-2', outFile ]) cmd = ffAddDebug(cmd, isFFmpeg) return cmd # Now mix new audio(s) and the new video. if (keepTracksSep): cmd = [ffmpeg, '-y'] for i in range(tracks): cmd.extend(['-i', f'{temp}/new{i}.wav']) cmd.extend(['-i', f'{temp}/spedup.mp4']) for i in range(tracks): cmd.extend(['-map', f'{i}:a:0']) cmd.extend(['-map', f'{tracks}:v:0', '-c:v', vcodec]) cmd = extender(cmd, vbitrate, tune, preset, acodec, outFile, log.is_ffmpeg) else: # Merge all the audio tracks into one. if (tracks > 1): cmd = [ffmpeg] for i in range(tracks): cmd.extend(['-i', f'{temp}/new{i}.wav']) cmd.extend([ '-filter_complex', f'amerge=inputs={tracks}', '-ac', '2', f'{temp}/newAudioFile.wav' ]) if (log.is_ffmpeg): cmd.extend(['-hide_banner']) else: cmd.extend(['-nostats', '-loglevel', '8']) subprocess.call(cmd) else: move(f'{temp}/new0.wav', f'{temp}/newAudioFile.wav') cmd = [ ffmpeg, '-y', '-i', f'{temp}/newAudioFile.wav', '-i', f'{temp}/spedup.mp4', '-c:v', vcodec ] cmd = extender(cmd, vbitrate, tune, preset, acodec, outFile, log.is_ffmpeg) message = pipeToConsole(cmd) log.debug(message) if ('Conversion failed!' in message): log.warning('The muxing/compression failed. '\ 'This may be a problem with FFmpeg, your video codec, '\ 'or your video bitrate. \nTrying, again but not compressing.') cmd = [ ffmpeg, '-y', '-i', f'{temp}/newAudioFile.wav', '-i', f'{temp}/spedup.mp4', '-c:v', 'copy', '-movflags', '+faststart', outFile ] cmd = ffAddDebug(cmd, log.is_ffmpeg) subprocess.call(cmd) log.conwrite('')