flashffmpegconvertersflvadobe-connect

Adobe connect video: FLV to MP4 (export, convert)


I would like to convert an adobe connect video from .flv in the downloaded zip to .mp4. I have already done the steps explained in this question and answer, however I get .flv files organised like this inside the .zip:

enter image description here

Moreover, I know that ffmpeg can merge video and sound files together as well as concatenating resulting clips directly from the command-line which could be quite useful: https://www.labnol.org/internet/useful-ffmpeg-commands/28490/

I can't ask the owner of the video to make it available as an .mp4 from within the adobe connect admin interface. Briefly, I would like to listen to those videos in x2 speed in VLC (just like what I do when listening to random math classes on YouTube - I put ON the x2 speed). The amount of time I would gain to watch adobe connect videos in x2 speed is MASSIVE.

I think I am not the only one that would like to do this. There are a lot of questions on forums about downloading adobe connect videos, but the .flv format mixed with some .xml is generally a killer when the host does not make the videos properly available in .mp4.

Dealing with the order of the .flv files is a puzzle. At least, I would not care to flush the chat away and leave some details like that behind, that would help to reconstruct the videos. Any scripts to automate the process would be useful.


Solution

  • On my side ffmpeg works fine with the cameraVoip__.xml files.

    I wrote this Python script to export an Adobe Connect recording as a video (code repo: https://github.com/Franck-Dernoncourt/adobe-connect-video-downloader):

    '''
    Requirements:
    - python 2.7 or 3
    - wget, unzip, and ffmpeg accessible from command line.
    
    Examples:
    python connect2vid_v2.py https://my.adobeconnect.com/pqc06mcawjgn/  --output_filename=" Understanding how the Network impacts your service"
    
    The script assumes that the .zip files contains screenshare__.flv files, which contain the screen share.
    
    Please email Franck Dernoncourt <franck.dernoncourt@gmail.com> if you improve this code.
    '''
    
    import shlex
    import subprocess
    import os
    import glob
    import argparse
    import sys
    import re
    
    
    def run_command(command):
        print('running command: {0}'.format(command))
        process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
        while True:
            output = process.stdout.readline()
            print(output.strip())
            if output == b'' and process.poll() is not None:
                print('Done running the command.')
                break
            if output:
                print(output.strip())
        rc = process.poll()
        return rc
    
    def create_folder_if_not_exists(directory):
        '''
        Create the folder if it doesn't exist already.
        '''
        if not os.path.exists(directory):
            os.makedirs(directory)
    
    def extract_connect_id(parser, args):
        '''
        Function written by Aaron Hertzmann
        '''
        # ----- extract the connectID or ZIP file  -----
    
        if len(args.URLorIDorZIP) < 1:
        #    print('Error: No Connect recording URL provided.')
            parser.print_help()
            sys.exit(0)
    
        if args.URLorIDorZIP[0][-4:].lower() == '.zip':
            sourceZIP = args.URLorIDorZIP[0]
            connectID = os.path.basename(sourceZIP[:-4])
        elif len(args.URLorIDorZIP[0]) == 12:
            connectID = args.URLorIDorZIP[0]
        else:
            s = args.URLorIDorZIP[0].split('/')
            connectID = None
            for i in range(len(s)-1):
                if 'adobeconnect.com' in s[i]:
                    connectID = s[i+1]
                    break
            if connectID == None:
                print("Error: couldn't parse URL")
                sys.exit(1)
    
        return connectID
    
    
    def main():
        '''
        This is the main function
        '''
    
        # ================ parse the arguments (part of the parsing code was written by Aaron Hertzmann) ======================
    
        parser = argparse.ArgumentParser(description='Download an Adobe Connect recording and convert to a video file.')
        parser.add_argument('URLorIDorZIP', nargs='*', help='URL, code, or ZIP file for the Connect recording')
        parser.add_argument('--output_folder',default='output_videos',help='Folder for output files')
        parser.add_argument('--output_filename',default='noname', help='output_filename')
        args = parser.parse_args()
    
        #main_output_folder = "all_videos"
        main_output_folder = args.output_folder
        output_filename = args.output_filename
        output_filename =  re.sub(r'[^\w\s]','', output_filename)
        output_filename = output_filename.replace('@', '').strip()
        print('output_filename: {0}'.format(output_filename))
        connect_id = 'pul1pgdvpr87'
        connect_id = 'p6vwxp2d0c2f'
        connect_id = extract_connect_id(parser, args)
        video_filename = 'hello'
        video_filename = output_filename
    
        # ================ Download video  ======================
        output_folder = connect_id
        output_zip_filename = '{0}.zip'.format(connect_id)
        create_folder_if_not_exists(output_folder)
        create_folder_if_not_exists(main_output_folder)
    
        # Step 1: retrieve audio and video files
        connect_zip_url = 'https://my.adobeconnect.com/{0}/output/{0}.zip?download=zip'.format(connect_id)
        wget_command = 'wget -nc -O {1} {0}'.format(connect_zip_url, output_zip_filename) # -nc, --no-clobber: skip downloads that would download to existing files.
        run_command(wget_command)
        unzip_command = 'unzip -n {0} -d {1}'.format(output_zip_filename, output_folder) # -n: Unzip only newer files.
        run_command(unzip_command)
    
        # Step 2: create final video output
        cameraVoip_filepaths = []
        for filepaths in sorted(glob.glob(os.path.join(output_folder, 'cameraVoip_*.flv'))):
            cameraVoip_filepaths.append(filepaths)
        print('cameraVoip_filepaths: {0}'.format(cameraVoip_filepaths))
    
        screenshare_filepaths = []
        for filepaths in sorted(glob.glob(os.path.join(output_folder, 'screenshare_*.flv'))):
            screenshare_filepaths.append(filepaths)
    
        part = 0
        output_filepaths = []
        for cameraVoip_filepath, screenshare_filepath in zip(cameraVoip_filepaths, screenshare_filepaths):
            output_filepath = os.path.join(main_output_folder, '{0}_{1:04d}.flv'.format(video_filename, part))
            #output_filepath = '{0}_{1:04d}.flv'.format(video_filename, part)
            output_filepaths.append(output_filepath)
            # ffmpeg command from Oliver Wang / Yannick Hold-Geoffroy / Aaron Hertzmann
            conversion_command = 'ffmpeg -i "%s" -i "%s" -c copy -map 0:a:0 -map 1:v:0 -shortest -y "%s"'%(cameraVoip_filepath, screenshare_filepath, output_filepath)
            # -y: override output file if exists
            run_command(conversion_command)
            part += 1
    
        # Concatenate all videos into one single video
        # https://stackoverflow.com/questions/7333232/how-to-concatenate-two-mp4-files-using-ffmpeg
        video_list_filename = 'video_list.txt'
        video_list_file = open(video_list_filename, 'w')
        for output_filepath in output_filepaths:
            video_list_file.write("file '{0}'\n".format(output_filepath))
        video_list_file.close()
        final_output_filepath = '{0}.flv'.format(video_filename)
        # ffmpeg command from Oliver Wang / Yannick Hold-Geoffroy / Aaron Hertzmann
        conversion_command = 'ffmpeg -safe 0 -y -f concat -i "{1}" -c copy "{0}"'.format(final_output_filepath, video_list_filename)
        run_command(conversion_command)
        #os.remove(video_list_filename)
    
    if __name__ == "__main__":
        main()
        #cProfile.run('main()') # if you want to do some profiling