コード例 #1
0
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)
コード例 #2
0
ファイル: info.py プロジェクト: tawawhite/auto-editor
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('')