GoExport 2.0 exports GoAnimate movies through a Flash-enabled Chromium browser. It has two CLI commands and no GUI:
recordcaptures playback and system audio with PyScap, then muxes the video and optionally appends an outro.exportseeks each movie frame, encodes browser screenshots, builds an audio timeline from movie XML, and mixes locally resolved sound assets.
Use Python 3.13, as the release workflow does. Create a virtual environment and install the Python dependencies:
python -m venv .venv
# Activate .venv using your shell's activation command.
python -m pip install -r requirements.txt
python scripts/download_dependencies.py
python main.py doctor
python main.py --helpThe dependency installer replaces the Chromium and FFmpeg directories in bin/.
Chromium 87 and its matching ChromeDriver are deliberately retained for PPAPI
Flash support. The FFmpeg download URLs use moving release endpoints; they are
not reproducibly pinned. requirements.txt retains the existing Python pins.
Start your Wrapper: Offline instance and asset server before exporting. Defaults
in goexport/config.py use localhost ports 4343 and 4664; the CLI can override
server URLs and player paths.
python main.py record -id MOVIE_ID -out final_output --no-outro
python main.py export -id MOVIE_ID -xml movie.xml -ugc /path/to/ugc -as /path/to/theme/assetsRun either command with --help for all options. MP4, MOV, and MKV are supported.
Recording defaults to 1280x720 at 24 fps and appends resources/outro.mp4 unless
--no-outro is supplied. Its intermediate files are <output>.video.mkv and
<output>.audio.wav; failed captures retain available diagnostics. Recording
without captured audio muxes a silent audio track into the requested container.
Chromium, ChromeDriver, Pepper Flash, and FFmpeg paths can be changed with
--chrome-path, --chromedriver-path, --flash-plugin-path, and --ffmpeg-path.
The Flash version passed to Chromium can be changed with
--flash-plugin-version. These default to the existing platform-specific
configuration, paths are validated before the command starts, and the path
options are also honored by doctor.
Frame-by-frame export uses output.mkv, audio.wav, and final_output.<format>
in the working directory. These fixed names are unsuitable for concurrent runs.
Place --json before the command to reserve stdout for newline-delimited JSON:
python main.py --json record -id MOVIE_ID -out final_output --no-outro
python main.py --json export -id MOVIE_ID -xml movie.xml -ugc /path/to/ugc -as /path/to/theme/assets
python main.py --json doctorProgress events use {"event":"progress","progress":42.5,"stage":"recording"}.
Successful video commands finish with
{"event":"complete","progress":100,"output":"final_output.mp4"}, while
failures use {"event":"error","message":"...","code":1}. A caller can consume
the live stream one event at a time:
for line in process.stdout:
event = json.loads(line)
if "progress" in event:
update_progress(event["progress"])main.pycallsgoexport/cli.py, which parses once, sets up Rich logging, and dispatches the commands registered ingoexport/commands/__init__.py.commands/record.pyandcommands/export.pydefine command-specific options.helpers.pycontains shared player options, argument validators, and output path handling.config.pydefines defaults and platform-specific runtime paths.services/browser.pymanages Selenium, display sizing, Flash permission, template injection, and capture-target lookup.close()releases Chromium and the virtual display.services/flash.pywaits for player readiness and the template's start/stop markers.services/recorder.pycoordinates playback, the stop watcher, capture, audio alignment, and output cleanup.services/capture.pyconverts timestamps and chooses frames for a constant frame rate.services/renderer.pyencodes frame-by-frame screenshots.timeline_builder.py,asset_resolver.py, andaudio.pyload movie sounds, resolve UGC/theme files, and send the timeline to the audio encoder.services/ffmpeg.pyowns encoding commands, pipe/process diagnostics, muxing, and outro concatenation.models/audio_clip.pydescribes timeline clips; theTimelinemodel remains available, although export currently uses a list.scripts/download_dependencies.pyinstalls external runtime files.GoExport.specbundles Python code and PyScap libraries; the release workflow copiesbin/andresources/alongside the executable separately.
Prefer explicit control flow, descriptive names, four-space indentation, and the existing command/service split. Keep platform branches visible. Share genuinely repeated logic without adding service layers. Raise errors where they occur; the CLI logs uncaught errors and returns 1, or 130 for cancellation. Cleanup exceptions that are deliberately tolerated should include diagnostic logging. Keep encoder stderr file-backed to avoid pipe deadlocks.
python -m pip install -r requirements-dev.txt
python -m unittest discover -s tests -v
python3 -m unittest tests.test_macos_capture_diagnostic -v
python -m ruff check goexport scripts tests main.py
python -m ruff format --check goexport scripts tests main.py
python -m mypy
python -m compileall -q goexport scripts tests main.py
python -m pip check
python -m PyInstaller GoExport.specMypy checks annotated code and ignores missing third-party stubs; this is not strict checking of every service method. FFmpeg integration tests run when the configured bundled executable exists and otherwise report a skip. They use temporary files, verify decoded frames/audio and container headers, and cover silent export audio and outro concatenation. Failure tests cover browser, capture, and encoder cleanup. Build output is ignored by Git.
Windows and macOS select the browser capture target by its unique recording title. Linux explicitly uses PyScap's X11 backend and the current display's root window rather than enumerating windows. It tries to start an Xvfb display and logs a fallback to the current display if startup fails. Linux therefore needs an available X11 display/Xvfb; macOS may require screen-capture permission.
PyScap timestamps use integer nanoseconds within GoExport and are never compared
with Python wall clocks. The recorder discards dequeued frames until play()
returns, but queued frames can precede that call. Stop is bounded by the first
dequeued frame after the watcher observes the stop marker. Exact player-marker
alignment and cancellation of a blocked native capture read remain limitations.
The export command needs movie XML for local audio even though --movie-xml is
not marked required by argparse. The shared HTML template plays by movie ID;
it does not consume the supplied XML path or user ID. Flash settings automation
locates Chromium's Flash permission control through the settings page's Shadow
DOM. Outro concatenation expects an audio stream in the custom outro. These
workflows require an actual Wrapper instance and platform testing beyond
synthetic FFmpeg checks.
- Lexian-Droid
- Octanuary Assisted in the SWF side of things.