7

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.

Community
  • 1
  • 1
Guillaume Chevalier
  • 9,613
  • 8
  • 51
  • 79
  • Share a link to the zip archive so we can see if `ffmpeg` can do anything with them. – llogan Feb 08 '17 at 01:25
  • I sent you an email with a download link :) – Guillaume Chevalier Feb 08 '17 at 01:40
  • 1
    _"I would like to listen to those videos in x2 speed in VLC"_ So why not just play those FLV videos files inside VLC anyway at x2 speed? XML is just a text file so it's irrelevant to audio/video playback (_ie:_ only a coded application would process xml or json data. A video format like mp4, flv, or avi does not read xml). – VC.One Feb 08 '17 at 10:32
  • 1
    The video and audio are separated in the `.flv` files. Moreover, it would be interesting to have every file reassembled automatically. I suspect that audio files may swap at different times than video files and this may be described in one of the `.xml` files that might act as a main. There are many adobe connect conferences of 2 to 3 hours each that I would like to process that way (more than 20), so this would be a tedious process to reassemble everything manually... Yet I hope someone went through that process already and is willing to help many that would like to save those videos. – Guillaume Chevalier Feb 08 '17 at 15:09
  • I took a very brief look but `ffmpeg` could not properly decode anything except the nellymoser audio format in the `camera*` files. You may need to find another solution. Perhaps the "video" is actually vector Flash stuff? – llogan Feb 09 '17 at 04:16

2 Answers2

7

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
Franck Dernoncourt
  • 77,520
  • 72
  • 342
  • 501
  • Didn't try nor read as I won't need this anymore. I accepted the answer as it seems elaborate. – Guillaume Chevalier Feb 13 '19 at 06:47
  • code seems to work fine, thank you! But there seems to be an issue with the screenshare content. How have you managed to play the screenshare_.flv file? I just get a black screen. The flv-file seems to be encoded with On2's VP6.2 Video (Flash) (VP6F). I have tried with VLC-media player, VirtualDub, Windows Media Player with different codecs installed, but I unfortunately only get a black screen. Thank you for your feedback! – smoens Oct 10 '19 at 20:37
  • The links that I have downloaded does not have the screenshare files also. I do not know what could be done. I tried the python code also. But it did not help much – Sritam Saha May 31 '20 at 15:19
  • 2
    I find the general structure really useful, but this only works if each screenshare and each cameravoip come in matching pairs. I ran into multiple problems when the screenshare starts after the audio or there is a gap in the audio, because the presenter stopped the audio but not the recording during a break. I'm not yet able to merge those without reencoding which takes too long. The mainstream.xml contains timestamp of start and end of each .flv in the main recording, but I can't figure out how to properly merge everything into one video. – j-hap Aug 10 '20 at 16:35
  • Really useful, there is a problem sorting file names, for example _0_18.flv is appended before _0_4.flv – gaia Oct 28 '20 at 22:07
  • 1
    To Franck Dernoncourt, did you make any updated version of this script? – fcole90 Nov 04 '20 at 21:51
  • @fcole90 sorry I didn't – Franck Dernoncourt Nov 04 '20 at 21:54
  • 1
    Do you have it in a repo or something? I made few modifications and it would be nice to push them. Actually I modified the code to have a small PyQt5 GUI, very rough and incomplete, but may be nice for those who don't want to dig into the CLI. If you make a repo of this, let me know :) – fcole90 Nov 05 '20 at 08:53
  • 1
    @fcole90 thanks, good idea: https://github.com/Franck-Dernoncourt/adobe-connect-video-downloader – Franck Dernoncourt Nov 11 '20 at 06:42
  • For who is still interested: You can read the timings from the xml files so that you can sync multiple flv files with the mp3 files – Servus Apr 28 '22 at 13:45
0


Hello
You should only use flv files, not xml files .
cameraVoip.xml file for microphone, camera video and screenshare.flv for shared screen (from desktop).

You can also get the start time of these files in the indexstream.xml file, which is embedded in tags in milliseconds.

Also, if you know how to work with ffmpeg or other software, you can download these files, get the schedules, and then combine the files in order.

But if you can not and only sound and videos are important to you, not text, you can use the program I wrote, which is better to look at my github address for a more complete explanation of the program.

It goes without saying that Adobe Connect files may be corrupted for any reason, so make sure they are safe and then work with Adobe files.

https://github.com/HosseinShams00/AdobeConnectDownloader

Hossein
  • 1
  • 3